vmm: streamline printing of error chains

In [0] we agreed on the current format.

- Use `: ` over ` => `
- Streamline usages in a helper

Context https://github.com/cloud-hypervisor/cloud-hypervisor/issues/8510

[0] https://github.com/cloud-hypervisor/cloud-hypervisor/pull/7066

On-behalf-of: SAP philipp.schuster@sap.com
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
This commit is contained in:
Philipp Schuster
2026-07-02 15:04:32 +02:00
committed by Rob Bradford
parent 53a00c0514
commit da80fde887
2 changed files with 42 additions and 31 deletions

View File

@@ -12,7 +12,7 @@ use std::panic::AssertUnwindSafe;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use std::sync::mpsc::Sender;
use std::{fs, iter, panic, result, thread};
use std::{fs, panic, result, thread};
use block::fcntl::{LockError, LockGranularity, LockType, try_acquire_lock};
use log::{error, info};
@@ -35,6 +35,7 @@ use crate::api::{
};
use crate::landlock::Landlock;
use crate::seccomp_filters::{Thread, get_seccomp_filter};
use crate::util::{error_chain_messages, flatten_error_chain_to_string};
use crate::vm::Error as VmError;
use crate::{Error as VmmError, Result};
@@ -102,20 +103,13 @@ const HTTP_ROOT: &str = "/api/v1";
pub fn error_response(error: HttpError) -> Response {
let mut response = Response::new(Version::Http11, error.status_code());
let error: &dyn Error = &error;
// Write the Display::display() output all errors (from top to root).
let error_messages = iter::successors(Some(error), |sub_error| {
// Dereference necessary to mitigate rustc compiler bug.
// See <https://github.com/rust-lang/rust/issues/141673>
(*sub_error).source()
})
.map(|error| format!("{error}"))
.collect::<Vec<_>>();
info!("HTTP API error response: {}", error_messages.join(": "));
info!(
"HTTP API error response: {}",
flatten_error_chain_to_string(&error)
);
// TODO: Move `api` module from `vmm` to dedicated crate and use a common type definition
let json = serde_json::to_string(&error_messages).unwrap();
let json = serde_json::to_string(&error_chain_messages(&error)).unwrap();
let body = Body::new(json);
response.set_body(body);

View File

@@ -4,7 +4,6 @@
//
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File;
use std::io::{Read, Write, stdout};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
@@ -16,7 +15,7 @@ use std::sync::mpsc;
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender, channel};
use std::sync::{Arc, Mutex, Weak};
use std::time::{Duration, Instant};
use std::{any, io, iter, mem, panic, path, process, result, thread};
use std::{any, io, mem, panic, path, process, result, thread};
use anyhow::{Context, anyhow};
#[cfg(feature = "dbus_api")]
@@ -2033,22 +2032,10 @@ impl Vmm {
}
}
Err(e) => {
// Mimic the error chain that CH prints on error in the log.
// Required to get useful error messages in the log.
let top_error: &dyn StdError = &e;
let error_chain_str = {
iter::successors(Some(top_error), |sub_error| {
// Dereference necessary to mitigate rustc compiler bug.
// See <https://github.com/rust-lang/rust/issues/141673>
(*sub_error).source()
})
// Important to use the plain Display impl to not interfere
// with anyhow's "smart" printing
.map(|e| format!("{e}"))
.collect::<Vec<_>>()
.join(" => ")
};
error!("Migration failed: {error_chain_str}");
error!(
"Migration failed: {}",
util::flatten_error_chain_to_string(&e)
);
try_resume_vm_after_failed_migration(vm);
}
}
@@ -3240,6 +3227,36 @@ const CPU_MANAGER_SNAPSHOT_ID: &str = "cpu-manager";
const MEMORY_MANAGER_SNAPSHOT_ID: &str = "memory-manager";
const DEVICE_MANAGER_SNAPSHOT_ID: &str = "device-manager";
mod util {
use std::error::Error as StdError;
use std::iter;
/// Creates in iterator over the [`Display`]-formatted representations of
/// the chain of errors of a [`StdError`].
///
/// The first index is the top error, the last index is the root cause.
///
/// This mimics the error chain that we print on exit in CH or ch-remote for
/// situations where we do not exit the program.
pub fn error_chain_messages(top_error: &dyn StdError) -> Vec<String> {
iter::successors(Some(top_error), |sub_error| {
// Dereference necessary to mitigate rustc compiler bug.
// See <https://github.com/rust-lang/rust/issues/141673>
(*sub_error).source()
})
// Important to use the plain Display impl to not interfere
// with anyhow's "smart" printing
.map(|e| format!("{e}"))
.collect()
}
/// Flattens the chain of errors of a [`StdError`] into a single printable
/// line.
pub fn flatten_error_chain_to_string(top_error: &dyn StdError) -> String {
// Separator discussed here: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/8510
error_chain_messages(top_error).join(": ")
}
}
#[cfg(test)]
mod unit_tests {
use std::path::PathBuf;