Skip to content

Commit 9e4a43d

Browse files
committed
Rename and refactor dump_on_crash feature, clean up print_debug feature
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 6d4c3a8 commit 9e4a43d

File tree

13 files changed

+164
-189
lines changed

13 files changed

+164
-189
lines changed

.github/workflows/dep_rust.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ jobs:
7474
# with only one driver enabled (driver mshv/kvm feature is ignored on windows) + seccomp + inprocess
7575
just test-rust ${{ matrix.config }} inprocess,seccomp,${{ matrix.hypervisor == 'mshv' && 'mshv' || 'kvm' }}
7676
77+
# make sure certain cargo features compile
78+
cargo check -p hyperlight-host --features crashdump
79+
cargo check -p hyperlight-host --features print_debug
80+
7781
# without any driver (shouldn't compile)
7882
just test-rust-feature-compilation-fail ${{ matrix.config }}
7983

docs/debugging-hyperlight.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ cargo test --package hyperlight-host --test integration_test --features print_de
3939

4040
## Dumping the memory configuration, virtual processor register state and memory contents on a crash or unexpected VM Exit
4141

42-
To dump the details of the memory configuration, the virtual processors register state and the contents of the VM memory set the feature `dump_on_crash` and run a debug build. This will result in a dump file being created in the temporary directory. The name and location of the dump file will be printed to the console and logged as an error message.
42+
To dump the details of the memory configuration, the virtual processors register state and the contents of the VM memory set the feature `crashdump` and run a debug build. This will result in a dump file being created in the temporary directory. The name and location of the dump file will be printed to the console and logged as an error message.
4343

4444
There are no tools at this time to analyze the dump file, but it can be useful for debugging.

src/hyperlight_host/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,7 @@ function_call_metrics = []
118118
executable_heap = []
119119
# This feature enables printing of debug information to stdout in debug builds
120120
print_debug = []
121-
# This feature enables dunping of the VMs details to a file when an unexpected or error exit occurs in a VM in debug mode
122-
# the name of the file is output to stdout and logged.
123-
dump_on_crash = ["dep:tempfile"]
121+
crashdump = ["dep:tempfile"] # Dumps the VM state to a file on unexpected errors or crashes. The path of the file will be printed on stdout and logged. This feature can only be used in debug builds.
124122
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
125123
mshv = ["dep:mshv-bindings", "dep:mshv-ioctls"]
126124
inprocess = []

src/hyperlight_host/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ fn main() -> Result<()> {
9494
// inprocess feature is aliased with debug_assertions to make it only available in debug-builds.
9595
// You should never use #[cfg(feature = "inprocess")] in the codebase. Use #[cfg(inprocess)] instead.
9696
inprocess: { all(feature = "inprocess", debug_assertions) },
97+
// crashdump feature is aliased with debug_assertions to make it only available in debug-builds.
98+
crashdump: { all(feature = "crashdump", debug_assertions) },
99+
// print_debug feature is aliased with debug_assertions to make it only available in debug-builds.
100+
print_debug: { all(feature = "print_debug", debug_assertions) },
97101
}
98102

99103
write_built_file()?;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::io::Write;
2+
3+
use tempfile::NamedTempFile;
4+
5+
use super::Hypervisor;
6+
use crate::{new_error, Result};
7+
8+
/// Dump registers + memory regions + raw memory to a tempfile
9+
#[cfg(crashdump)]
10+
pub(crate) fn crashdump_to_tempfile(hv: &dyn Hypervisor) -> Result<()> {
11+
let mem_regions = hv.get_memory_regions();
12+
let mem_size = mem_regions
13+
.iter()
14+
.map(|region| region.host_region.len())
15+
.sum();
16+
let mem_start_addr = mem_regions[0].host_region.start as *const u8;
17+
18+
if mem_start_addr.is_null() || mem_size == 0 {
19+
return Err(new_error!(
20+
"Invalid address or size while creating crashdump"
21+
));
22+
}
23+
24+
let mut temp_file = NamedTempFile::with_prefix("mem")?;
25+
26+
let hv_details = format!("{:#x?}", hv);
27+
28+
// write hypervisor details such as registers, memory regions, etc.
29+
temp_file.write_all(hv_details.as_bytes())?;
30+
// write memory dump
31+
temp_file.write_all(b"================ MEMORY DUMP =================\n")?;
32+
// SAFETY: Address and size non-null and non-zero
33+
unsafe {
34+
let slice = std::slice::from_raw_parts(mem_start_addr, mem_size);
35+
temp_file.write_all(slice)?;
36+
temp_file.flush()?;
37+
}
38+
let persist_path = temp_file.path().with_extension("dmp");
39+
temp_file
40+
.persist(&persist_path)
41+
.map_err(|e| new_error!("Failed to persist crashdump file: {:?}", e))?;
42+
43+
println!("Memory dumped to file: {:?}", persist_path);
44+
log::error!("Memory dumped to file: {:?}", persist_path);
45+
46+
Ok(())
47+
}

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::hypervisor::hypervisor_handler::HypervisorHandler;
3737
use crate::hypervisor::HyperlightExit;
3838
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3939
use crate::mem::ptr::{GuestPtr, RawPtr};
40-
use crate::{debug, log_then_return, new_error, Result};
40+
use crate::{log_then_return, new_error, Result};
4141

4242
/// Determine whether the HyperV for Linux hypervisor API is present
4343
/// and functional.
@@ -287,7 +287,7 @@ impl Hypervisor for HypervLinuxDriver {
287287
let result = match &self.vcpu_fd.run(hv_message) {
288288
Ok(m) => match m.header.message_type {
289289
HALT_MESSAGE => {
290-
debug!("mshv - Halt Details : {:#?}", &self);
290+
crate::debug!("mshv - Halt Details : {:#?}", &self);
291291
HyperlightExit::Halt()
292292
}
293293
IO_PORT_INTERCEPT_MESSAGE => {
@@ -296,7 +296,7 @@ impl Hypervisor for HypervLinuxDriver {
296296
let rip = io_message.header.rip;
297297
let rax = io_message.rax;
298298
let instruction_length = io_message.header.instruction_length() as u64;
299-
debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
299+
crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
300300
HyperlightExit::IoOut(
301301
port_number,
302302
rax.to_le_bytes().to_vec(),
@@ -307,24 +307,22 @@ impl Hypervisor for HypervLinuxDriver {
307307
UNMAPPED_GPA_MESSAGE => {
308308
let mimo_message = m.to_memory_info()?;
309309
let addr = mimo_message.guest_physical_address;
310-
debug!(
310+
crate::debug!(
311311
"mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
312-
addr, &self
312+
addr,
313+
&self
313314
);
314-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
315-
self.dump_on_crash(self.mem_regions.clone());
316315
HyperlightExit::Mmio(addr)
317316
}
318317
INVALID_GPA_ACCESS_MESSAGE => {
319318
let mimo_message = m.to_memory_info()?;
320319
let gpa = mimo_message.guest_physical_address;
321320
let access_info = MemoryRegionFlags::try_from(mimo_message)?;
322-
debug!(
321+
crate::debug!(
323322
"mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
324-
gpa, &self
323+
gpa,
324+
&self
325325
);
326-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
327-
self.dump_on_crash(self.mem_regions.clone());
328326
match self.get_memory_access_violation(
329327
gpa as usize,
330328
&self.mem_regions,
@@ -335,9 +333,7 @@ impl Hypervisor for HypervLinuxDriver {
335333
}
336334
}
337335
other => {
338-
debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
339-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
340-
self.dump_on_crash(self.mem_regions.clone());
336+
crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
341337
log_then_return!("unknown Hyper-V run message type {:?}", other);
342338
}
343339
},
@@ -346,9 +342,7 @@ impl Hypervisor for HypervLinuxDriver {
346342
libc::EINTR => HyperlightExit::Cancelled(),
347343
libc::EAGAIN => HyperlightExit::Retry(),
348344
_ => {
349-
debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
350-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
351-
self.dump_on_crash(self.mem_regions.clone());
345+
crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
352346
log_then_return!("Error running VCPU {:?}", e);
353347
}
354348
},
@@ -360,6 +354,11 @@ impl Hypervisor for HypervLinuxDriver {
360354
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
361355
self as &mut dyn Hypervisor
362356
}
357+
358+
#[cfg(crashdump)]
359+
fn get_memory_regions(&self) -> &[MemoryRegion] {
360+
&self.mem_regions
361+
}
363362
}
364363

365364
impl Drop for HypervLinuxDriver {

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use std::fmt;
1919
use std::fmt::{Debug, Formatter};
2020
use std::string::String;
2121

22-
use cfg_if::cfg_if;
2322
use hyperlight_common::mem::PAGE_SIZE_USIZE;
2423
use tracing::{instrument, Span};
2524
use windows::Win32::Foundation::HANDLE;
@@ -46,7 +45,7 @@ use crate::hypervisor::wrappers::WHvGeneralRegisters;
4645
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4746
use crate::mem::ptr::{GuestPtr, RawPtr};
4847
use crate::HyperlightError::{NoHypervisorFound, WindowsAPIError};
49-
use crate::{debug, log_then_return, new_error, Result};
48+
use crate::{log_then_return, new_error, Result};
5049

5150
/// A Hypervisor driver for HyperV-on-Windows.
5251
pub(crate) struct HypervWindowsDriver {
@@ -463,19 +462,11 @@ impl Hypervisor for HypervWindowsDriver {
463462
// see https://learn.microsoft.com/en-us/virtualization/api/hypervisor-platform/funcs/whvexitcontextdatatypes)
464463
let instruction_length = exit_context.VpContext._bitfield & 0xF;
465464
unsafe {
466-
cfg_if! {
467-
if #[cfg(all(feature = "print_debug", debug_assertions))] {
468-
println!(
469-
"HyperV IO Details :\n Port: {:#x} \n {:#?}",
470-
exit_context.Anonymous.IoPortAccess.PortNumber, &self
471-
);
472-
} else {
473-
debug!(
474-
"HyperV IO Details :\n Port: {:#x} \n {:#?}",
475-
exit_context.Anonymous.IoPortAccess.PortNumber, &self
476-
);
477-
}
478-
}
465+
crate::debug!(
466+
"HyperV IO Details :\n Port: {:#x} \n {:#?}",
467+
exit_context.Anonymous.IoPortAccess.PortNumber,
468+
&self
469+
);
479470
HyperlightExit::IoOut(
480471
exit_context.Anonymous.IoPortAccess.PortNumber,
481472
exit_context
@@ -491,7 +482,7 @@ impl Hypervisor for HypervWindowsDriver {
491482
}
492483
// HvRunVpExitReasonX64Halt
493484
WHV_RUN_VP_EXIT_REASON(8i32) => {
494-
debug!("HyperV Halt Details :\n {:#?}", &self);
485+
crate::debug!("HyperV Halt Details :\n {:#?}", &self);
495486
HyperlightExit::Halt()
496487
}
497488
// WHvRunVpExitReasonMemoryAccess
@@ -504,22 +495,12 @@ impl Hypervisor for HypervWindowsDriver {
504495
)
505496
};
506497
let access_info = MemoryRegionFlags::try_from(access_info)?;
507-
debug!(
498+
crate::debug!(
508499
"HyperV Memory Access Details :\n GPA: {:#?}\n Access Info :{:#?}\n {:#?} ",
509-
gpa, access_info, &self
500+
gpa,
501+
access_info,
502+
&self
510503
);
511-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
512-
{
513-
if let Err(e) = unsafe {
514-
self.write_dump_file(
515-
self.mem_regions.clone(),
516-
self.source_address.add(PAGE_SIZE_USIZE) as *const u8,
517-
self.size,
518-
)
519-
} {
520-
println!("Error dumping memory: {}", e);
521-
}
522-
}
523504

524505
match self.get_memory_access_violation(gpa as usize, &self.mem_regions, access_info)
525506
{
@@ -531,26 +512,15 @@ impl Hypervisor for HypervWindowsDriver {
531512
// Execution was cancelled by the host.
532513
// This will happen when guest code runs for too long
533514
WHV_RUN_VP_EXIT_REASON(8193i32) => {
534-
debug!("HyperV Cancelled Details :\n {:#?}", &self);
515+
crate::debug!("HyperV Cancelled Details :\n {:#?}", &self);
535516
HyperlightExit::Cancelled()
536517
}
537518
WHV_RUN_VP_EXIT_REASON(_) => {
538-
debug!(
519+
crate::debug!(
539520
"HyperV Unexpected Exit Details :#nReason {:#?}\n {:#?}",
540-
exit_context.ExitReason, &self
521+
exit_context.ExitReason,
522+
&self
541523
);
542-
#[cfg(all(debug_assertions, feature = "dump_on_crash"))]
543-
{
544-
if let Err(e) = unsafe {
545-
self.write_dump_file(
546-
self.mem_regions.clone(),
547-
self.source_address.add(PAGE_SIZE_USIZE) as *const u8,
548-
self.size,
549-
)
550-
} {
551-
println!("Error dumping memory: {}", e);
552-
}
553-
}
554524
match self.get_exit_details(exit_context.ExitReason) {
555525
Ok(error) => HyperlightExit::Unknown(error),
556526
Err(e) => HyperlightExit::Unknown(format!("Error getting exit details: {}", e)),
@@ -569,6 +539,11 @@ impl Hypervisor for HypervWindowsDriver {
569539
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
570540
self as &mut dyn Hypervisor
571541
}
542+
543+
#[cfg(crashdump)]
544+
fn get_memory_regions(&self) -> &[MemoryRegion] {
545+
&self.mem_regions
546+
}
572547
}
573548

574549
#[cfg(test)]

src/hyperlight_host/src/hypervisor/inprocess.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use std::fmt::Debug;
1818
use std::os::raw::c_void;
1919

2020
use super::{HyperlightExit, Hypervisor};
21+
#[cfg(crashdump)]
22+
use crate::mem::memory_region::MemoryRegion;
2123
use crate::sandbox::leaked_outb::LeakedOutBWrapper;
2224
use crate::Result;
2325

@@ -123,4 +125,9 @@ impl<'a> Hypervisor for InprocessDriver<'a> {
123125
fn get_partition_handle(&self) -> windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE {
124126
unimplemented!("get_partition_handle should not be needed since we are in in-process mode")
125127
}
128+
129+
#[cfg(crashdump)]
130+
fn get_memory_regions(&self) -> &[MemoryRegion] {
131+
unimplemented!("get_memory_regions is not supported since we are in in-process mode")
132+
}
126133
}

0 commit comments

Comments
 (0)