diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index 61b993169..69c3f8d65 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -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 - (*sub_error).source() - }) - .map(|error| format!("{error}")) - .collect::>(); - - 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); diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 2f10e05c4..b9cbd9ca8 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -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 - (*sub_error).source() - }) - // Important to use the plain Display impl to not interfere - // with anyhow's "smart" printing - .map(|e| format!("{e}")) - .collect::>() - .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 { + iter::successors(Some(top_error), |sub_error| { + // Dereference necessary to mitigate rustc compiler bug. + // See + (*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;