Skip to content

Commit 01523e1

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 01523e1

File tree

13 files changed

+163
-187
lines changed

13 files changed

+163
-187
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: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -463,19 +463,11 @@ impl Hypervisor for HypervWindowsDriver {
463463
// see https://learn.microsoft.com/en-us/virtualization/api/hypervisor-platform/funcs/whvexitcontextdatatypes)
464464
let instruction_length = exit_context.VpContext._bitfield & 0xF;
465465
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-
}
466+
crate::debug!(
467+
"HyperV IO Details :\n Port: {:#x} \n {:#?}",
468+
exit_context.Anonymous.IoPortAccess.PortNumber,
469+
&self
470+
);
479471
HyperlightExit::IoOut(
480472
exit_context.Anonymous.IoPortAccess.PortNumber,
481473
exit_context
@@ -491,7 +483,7 @@ impl Hypervisor for HypervWindowsDriver {
491483
}
492484
// HvRunVpExitReasonX64Halt
493485
WHV_RUN_VP_EXIT_REASON(8i32) => {
494-
debug!("HyperV Halt Details :\n {:#?}", &self);
486+
crate::debug!("HyperV Halt Details :\n {:#?}", &self);
495487
HyperlightExit::Halt()
496488
}
497489
// WHvRunVpExitReasonMemoryAccess
@@ -504,22 +496,12 @@ impl Hypervisor for HypervWindowsDriver {
504496
)
505497
};
506498
let access_info = MemoryRegionFlags::try_from(access_info)?;
507-
debug!(
499+
crate::debug!(
508500
"HyperV Memory Access Details :\n GPA: {:#?}\n Access Info :{:#?}\n {:#?} ",
509-
gpa, access_info, &self
501+
gpa,
502+
access_info,
503+
&self
510504
);
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-
}
523505

524506
match self.get_memory_access_violation(gpa as usize, &self.mem_regions, access_info)
525507
{
@@ -531,26 +513,15 @@ impl Hypervisor for HypervWindowsDriver {
531513
// Execution was cancelled by the host.
532514
// This will happen when guest code runs for too long
533515
WHV_RUN_VP_EXIT_REASON(8193i32) => {
534-
debug!("HyperV Cancelled Details :\n {:#?}", &self);
516+
crate::debug!("HyperV Cancelled Details :\n {:#?}", &self);
535517
HyperlightExit::Cancelled()
536518
}
537519
WHV_RUN_VP_EXIT_REASON(_) => {
538-
debug!(
520+
crate::debug!(
539521
"HyperV Unexpected Exit Details :#nReason {:#?}\n {:#?}",
540-
exit_context.ExitReason, &self
522+
exit_context.ExitReason,
523+
&self
541524
);
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-
}
554525
match self.get_exit_details(exit_context.ExitReason) {
555526
Ok(error) => HyperlightExit::Unknown(error),
556527
Err(e) => HyperlightExit::Unknown(format!("Error getting exit details: {}", e)),
@@ -569,6 +540,11 @@ impl Hypervisor for HypervWindowsDriver {
569540
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
570541
self as &mut dyn Hypervisor
571542
}
543+
544+
#[cfg(crashdump)]
545+
fn get_memory_regions(&self) -> &[MemoryRegion] {
546+
&self.mem_regions
547+
}
572548
}
573549

574550
#[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)