From 18d615485679b698fe3b496ad9fe817eb220579e Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 1 Jul 2026 15:12:26 +0200 Subject: [PATCH] vmm, vm-migration: streamline error chain message with upstream Streamlines the printing of the error message with upstream [0, 1]. [0] https://github.com/cloud-hypervisor/cloud-hypervisor/pull/8469/changes/fcf2b5bbe6afeebb17dba5bace72a6896531be5e [1] https://github.com/cloud-hypervisor/cloud-hypervisor/commit/252702049e55cc2903a07fd24e6a165a33c54026 On-behalf-of: SAP philipp.schuster@sap.com Signed-off-by: Philipp Schuster --- vm-migration/src/lib.rs | 18 +++++++++++++++++ vm-migration/src/progress.rs | 4 +++- vmm/src/lib.rs | 38 +++++------------------------------- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/vm-migration/src/lib.rs b/vm-migration/src/lib.rs index 60b1a47496..1941e6f830 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 c5babdad92..dc2f358b82 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 0bb2f65301..07454a0b18 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