openvmm_entry/
crash_dump.rs1use anyhow::Context;
7use futures::StreamExt;
8use futures_concurrency::stream::Merge;
9use get_resources::crash::GuestCrashDeviceHandle;
10use mesh::OneshotReceiver;
11use mesh::channel;
12use mesh::rpc::FailableRpc;
13use pal_async::task::Spawn;
14use pal_async::task::Task;
15use std::fs::File;
16use std::path::Path;
17use std::path::PathBuf;
18use unicycle::FuturesUnordered;
19use vm_resource::IntoResource;
20use vm_resource::Resource;
21use vm_resource::kind::VmbusDeviceHandleKind;
22
23pub fn spawn_dump_handler(
26 spawner: impl Spawn,
27 dump_path: PathBuf,
28 max_file_size: Option<u64>,
29) -> (Resource<VmbusDeviceHandleKind>, Task<()>) {
30 const DEFAULT_MAX_DUMP_SIZE: u64 = 256 * 1024 * 1024;
31
32 let (send, recv) = channel::<FailableRpc<_, _>>();
33 let task = spawner.spawn("crash_dumps", async move {
34 handle_dump_requests(&dump_path, recv).await
35 });
36 let config = GuestCrashDeviceHandle {
37 request_dump: send,
38 max_dump_size: max_file_size.unwrap_or(DEFAULT_MAX_DUMP_SIZE),
39 };
40 (config.into_resource(), task)
41}
42
43pub async fn handle_dump_requests(
46 dump_path: &Path,
47 mut recv: mesh::Receiver<
48 mesh::rpc::Rpc<OneshotReceiver<()>, Result<File, mesh::error::RemoteError>>,
49 >,
50) {
51 let mut tasks = FuturesUnordered::new();
52 while let Some(rpc) = ((&mut recv).map(Some), (&mut tasks).map(|()| None))
53 .merge()
54 .next()
55 .await
56 {
57 let Some(rpc) = rpc else { continue };
58 rpc.handle_failable_sync(|done| {
59 let tempfile = tempfile::Builder::new()
60 .prefix("underhill.")
61 .suffix(".core")
62 .tempfile_in(dump_path)
63 .context("failed to create file")?;
64
65 let file = tempfile
66 .as_file()
67 .try_clone()
68 .context("failed to clone file")?;
69
70 tracing::info!(path = %tempfile.path().display(), "writing VTL2 crash dump");
71 tasks.push(wait_for_dump(done, tempfile));
72 anyhow::Ok(file)
73 })
74 }
75}
76
77async fn wait_for_dump(done: OneshotReceiver<()>, tempfile: tempfile::NamedTempFile) {
78 if let Ok(()) = done.await {
79 match tempfile.keep() {
80 Ok((_, path)) => {
81 tracing::info!(
82 path = %path.display(),
83 "wrote VTL2 crash dump"
84 );
85 }
86 Err(err) => {
87 tracing::error!(
88 path = %err.file.path().display(),
89 "failed to persist VTL2 dump file"
90 );
91 }
92 }
93 } else {
94 tracing::info!(
95 path = %tempfile.path().display(),
96 "VTL2 crash dump did not complete, removing"
97 );
98 drop(tempfile);
99 }
100}