Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fast trace collection & use it for instrumenting guest memory operations #103

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/hyperlight_guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ default = ["libc", "printf", "alloca"]
libc = [] # compile musl libc
printf = [] # compile printf
alloca = [] # compile alloca wrapper
mem_profile = []

[dependencies]
anyhow = { version = "1.0.45", default-features = false }
Expand Down
6 changes: 5 additions & 1 deletion src/hyperlight_guest/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_

let heap_start = (*peb_ptr).guestheapData.guestHeapBuffer as usize;
let heap_size = (*peb_ptr).guestheapData.guestHeapSize as usize;
HEAP_ALLOCATOR
#[cfg(not(feature = "mem_profile"))]
let heap_allocator = &HEAP_ALLOCATOR;
#[cfg(feature = "mem_profile")]
let heap_allocator = &HEAP_ALLOCATOR.0;
heap_allocator
.try_lock()
.expect("Failed to access HEAP_ALLOCATOR")
.init(heap_start, heap_size);
Expand Down
7 changes: 7 additions & 0 deletions src/hyperlight_guest/src/host_function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ pub enum OutBAction {
Log = 99,
CallFunction = 101,
Abort = 102,
#[cfg(feature = "mem_profile")]
#[allow(dead_code)]
TraceRecordStack = 103,
#[cfg(feature = "mem_profile")]
TraceMemoryAlloc = 104,
#[cfg(feature = "mem_profile")]
TraceMemoryFree = 105,
}

pub fn get_host_value_return_as_void() -> Result<()> {
Expand Down
58 changes: 58 additions & 0 deletions src/hyperlight_guest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,66 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
}

// Globals
#[cfg(feature = "mem_profile")]
struct ProfiledLockedHeap<const ORDER: usize>(LockedHeap<ORDER>);
#[cfg(feature = "mem_profile")]
unsafe impl<const ORDER: usize> alloc::alloc::GlobalAlloc for ProfiledLockedHeap<ORDER> {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
let addr = self.0.alloc(layout);
syntactically marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceMemoryAlloc as u16,
in("rax") layout.size() as u64,
in("rcx") addr as u64);
}
addr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceMemoryFree as u16,
in("rax") layout.size() as u64,
in("rcx") ptr as u64);
}
self.0.dealloc(ptr, layout)
}
unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
let addr = self.0.alloc_zeroed(layout);
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceMemoryAlloc as u16,
in("rax") layout.size() as u64,
in("rcx") addr as u64);
}
addr
}
unsafe fn realloc(
&self,
ptr: *mut u8,
layout: core::alloc::Layout,
new_size: usize,
) -> *mut u8 {
let new_ptr = self.0.realloc(ptr, layout, new_size);
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceMemoryFree as u16,
in("rax") layout.size() as u64,
in("rcx") ptr);
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceMemoryAlloc as u16,
in("rax") new_size as u64,
in("rcx") new_ptr);
}
new_ptr
}
}
#[cfg(not(feature = "mem_profile"))]
#[global_allocator]
pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
#[cfg(feature = "mem_profile")]
#[global_allocator]
pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> =
ProfiledLockedHeap(LockedHeap::<32>::empty());

///cbindgen:ignore
#[no_mangle]
Expand Down
2 changes: 2 additions & 0 deletions src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ workspace = true
goblin = { version = "0.9" }
framehop = { version = "0.13.1", optional = true }
fallible-iterator = { version = "0.3.0", optional = true }
blake3 = { version = "1.5.5", optional = true }
rand = { version = "0.8.5" }
cfg-if = { version = "1.0.0" }
libc = { version = "0.2.167" }
Expand Down Expand Up @@ -130,6 +131,7 @@ trace_guest = []
# This feature enables unwinding the guest stack from the host, in
# order to produce stack traces for debugging or profiling.
unwind_guest = [ "trace_guest", "dep:framehop", "dep:fallible-iterator" ]
mem_profile = [ "unwind_guest", "dep:blake3" ]
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
mshv = ["dep:mshv-bindings", "dep:mshv-ioctls"]
inprocess = []
Expand Down
38 changes: 38 additions & 0 deletions src/hyperlight_host/src/sandbox/outb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ pub(super) enum OutBAction {
Abort,
#[cfg(feature = "unwind_guest")]
TraceRecordStack,
#[cfg(feature = "mem_profile")]
TraceMemoryAlloc,
#[cfg(feature = "mem_profile")]
TraceMemoryFree,
}

impl TryFrom<u16> for OutBAction {
Expand All @@ -60,6 +64,10 @@ impl TryFrom<u16> for OutBAction {
102 => Ok(OutBAction::Abort),
#[cfg(feature = "unwind_guest")]
103 => Ok(OutBAction::TraceRecordStack),
#[cfg(feature = "mem_profile")]
104 => Ok(OutBAction::TraceMemoryAlloc),
#[cfg(feature = "mem_profile")]
105 => Ok(OutBAction::TraceMemoryFree),
_ => Err(new_error!("Invalid OutB value: {}", val)),
}
}
Expand Down Expand Up @@ -238,6 +246,36 @@ fn handle_outb_impl(
write_stack(f, &stack);
})
}
#[cfg(feature = "mem_profile")]
OutBAction::TraceMemoryAlloc => {
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), &mut _trace_info) else {
return Ok(());
};
let Ok(amt) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RAX) else {
return Ok(());
};
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else {
return Ok(());
};
record_trace_frame(&_trace_info, 2u64, |f| {
let _ = f.write_all(&ptr.to_ne_bytes());
let _ = f.write_all(&amt.to_ne_bytes());
write_stack(f, &stack);
})
}
#[cfg(feature = "mem_profile")]
OutBAction::TraceMemoryFree => {
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), &mut _trace_info) else {
return Ok(());
};
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else {
return Ok(());
};
record_trace_frame(&_trace_info, 3u64, |f| {
let _ = f.write_all(&ptr.to_ne_bytes());
write_stack(f, &stack);
})
}
}
}

Expand Down