vmm: migration: print whole error chain on error

In [0] we agreed on the current format. When ch-remote or
cloud-hypervisor exit with an error, they nicely print the whole chain.
This, however, doesn't work when simply doing `error!("error: {e}")`
- which is what we currently do for migration-related errors.

This commit walks the chain of errors and prints all components in a
single line. This massively improves the quality of error messages and
helps tracing down where an error is originating from. Using ` => ` as
separator is better than `\n` which doesn't work well in our current
log format.

# Example (Before - Bad)

```
cloud-hypervisor:   2.859287s: <vmm> ERROR:vmm/src/lib.rs:2021 -- Migration failed: Failed to send migratable component snapshot
```

# Example (New - Better)

```
cloud-hypervisor:   2.296160s: <vmm> ERROR:vmm/src/lib.rs:2038 -- Migration failed: Failed to send migratable component snapshot => Error connecting to TCP socket => Connection refused (os error 111)
```

[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-06-25 14:17:00 +02:00
committed by Bo Chen
parent 8c71a0d821
commit 252702049e

View File

@@ -4,6 +4,7 @@
// //
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write, stdout}; use std::io::{Read, Write, stdout};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
@@ -15,7 +16,7 @@ use std::sync::mpsc;
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender, channel}; use std::sync::mpsc::{Receiver, RecvError, SendError, Sender, channel};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{any, io, mem, panic, path, process, result, thread}; use std::{any, io, iter, mem, panic, path, process, result, thread};
use anyhow::{Context, anyhow}; use anyhow::{Context, anyhow};
#[cfg(feature = "dbus_api")] #[cfg(feature = "dbus_api")]
@@ -2018,7 +2019,22 @@ impl Vmm {
} }
} }
Err(e) => { Err(e) => {
error!("Migration failed: {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}");
try_resume_vm_after_failed_migration(vm); try_resume_vm_after_failed_migration(vm);
} }
} }