diff --git a/vm-migration/src/lib.rs b/vm-migration/src/lib.rs index 60b1a4749..1941e6f83 100644 --- a/vm-migration/src/lib.rs +++ b/vm-migration/src/lib.rs @@ -3,6 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // +use std::error::Error; +use std::iter; + use anyhow::anyhow; pub use context::{ CompletedMigrationContext, DowntimeContext, MemoryMigrationContext, MigrationContextError, @@ -20,6 +23,21 @@ pub mod progress; pub mod protocol; pub mod tls; +/// Mimics the error chain printing of CH for migration-related errors, where we +/// do not exit the VMM (which would print the error chain). +pub fn nested_error_to_flat_chain_as_string(top_error: &dyn Error) -> String { + 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(" => ") +} + #[derive(Error, Debug)] pub enum UffdError { #[error("Snapshot ranges are not page-aligned")] diff --git a/vm-migration/src/progress.rs b/vm-migration/src/progress.rs index c5babdad9..dc2f358b8 100644 --- a/vm-migration/src/progress.rs +++ b/vm-migration/src/progress.rs @@ -22,6 +22,8 @@ use std::fmt::Display; use std::num::NonZeroU32; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use crate::nested_error_to_flat_chain_as_string; + #[derive( Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, )] @@ -329,7 +331,7 @@ impl MigrationProgress { self.timestamp_snapshot_ms = current_unix_timestamp_ms(); self.timestamp_snapshot_relative_ms = self.timestamp_snapshot_ms - self.timestamp_begin_ms; self.state = MigrationState::Failed { - error_msg: format!("{error}",), + error_msg: nested_error_to_flat_chain_as_string(error), error_msg_debug: format!("{error:?}",), }; } diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 0bb2f6530..07454a0b1 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -56,7 +56,7 @@ use vm_migration::progress::{ use vm_migration::protocol::*; use vm_migration::{ MemoryMigrationContext, Migratable, MigratableError, OngoingMigrationContext, Pausable, - Snapshot, Snapshottable, Transportable, + Snapshot, Snapshottable, Transportable, nested_error_to_flat_chain_as_string, }; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::signal::unblock_signal; @@ -2213,37 +2213,6 @@ impl Vmm { self.vm.vm_mut().unwrap().restore() } - /// Prints the error chain to `error!()` level, akin to user-facing errors when Cloud Hypervisor - /// or ch-remote fail. - // TODO: For upstreaming, we should unify this with the code-paths used by ch-remote and - // Cloud Hypervisor on failure. - fn log_print_error_chain<'a>(top_error: &'a (dyn std::error::Error + 'static)) { - // Print chain of errors - if top_error.source().is_none() { - error!("Migration failed with the following error:"); - error!(" {top_error}"); - } else { - // In cli_print_error_chain(), we also print the - // ::fmt() as oneliner so that we can see all - // properties. As we use anyhow errors in the migration path, - // Debug::fmt() is not helpful for us as it doesn't print the - // underlying properties (like the default Debug::fmt() impl would - // do). Instead, it would print a trace itself, which is not what - // we want to do here. - - error!("Migration failed with the following chain of errors:"); - std::iter::successors(Some(top_error), |sub_error| { - // Dereference necessary to mitigate rustc compiler bug. - // See - (*sub_error).source() - }) - .enumerate() - .for_each(|(level, error)| { - error!(" {level}: {error}"); - }); - } - } - /// Checks the migration result. /// /// This should be called when the migration thread indicated a state @@ -2345,7 +2314,10 @@ impl Vmm { } } Err(e) => { - Self::log_print_error_chain(&e); + error!( + "Migration failed: {}", + nested_error_to_flat_chain_as_string(&e) + ); try_resume_vm(vm); // Update migration progress snapshot