Skip to content

Commit 43061c5

Browse files
committed
Add trace support for recording memory allocations and frees
This allows for producing profiles of memory usage. Signed-off-by: Lucy Menon <[email protected]>
1 parent 0bed9aa commit 43061c5

File tree

6 files changed

+107
-1
lines changed

6 files changed

+107
-1
lines changed

src/hyperlight_guest/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ default = ["libc", "printf", "alloca"]
1717
libc = [] # compile musl libc
1818
printf = [] # compile printf
1919
alloca = [] # compile alloca wrapper
20+
mem_profile = []
2021

2122
[dependencies]
2223
anyhow = { version = "1.0.45", default-features = false }

src/hyperlight_guest/src/entrypoint.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_
131131

132132
let heap_start = (*peb_ptr).guestheapData.guestHeapBuffer as usize;
133133
let heap_size = (*peb_ptr).guestheapData.guestHeapSize as usize;
134-
HEAP_ALLOCATOR
134+
#[cfg(not(feature = "mem_profile"))]
135+
let heap_allocator = &HEAP_ALLOCATOR;
136+
#[cfg(feature = "mem_profile")]
137+
let heap_allocator = &HEAP_ALLOCATOR.0;
138+
heap_allocator
135139
.try_lock()
136140
.expect("Failed to access HEAP_ALLOCATOR")
137141
.init(heap_start, heap_size);

src/hyperlight_guest/src/host_function_call.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ pub enum OutBAction {
3737
Log = 99,
3838
CallFunction = 101,
3939
Abort = 102,
40+
#[cfg(feature = "mem_profile")]
41+
#[allow(dead_code)]
42+
TraceRecordStack = 103,
43+
#[cfg(feature = "mem_profile")]
44+
TraceMemoryAlloc = 104,
45+
#[cfg(feature = "mem_profile")]
46+
TraceMemoryFree = 105,
4047
}
4148

4249
pub fn get_host_value_return_as_void() -> Result<()> {

src/hyperlight_guest/src/lib.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,60 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
8383
}
8484

8585
// Globals
86+
#[cfg(feature = "mem_profile")]
87+
struct ProfiledLockedHeap<const ORDER: usize>(LockedHeap<ORDER>);
88+
#[cfg(feature = "mem_profile")]
89+
unsafe impl<const ORDER: usize> alloc::alloc::GlobalAlloc for ProfiledLockedHeap<ORDER> {
90+
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
91+
let addr = self.0.alloc(layout);
92+
unsafe {
93+
core::arch::asm!("out dx, al",
94+
in("dx") OutBAction::TraceMemoryAlloc as u16,
95+
in("rax") layout.size() as u64,
96+
in("rcx") addr as u64);
97+
}
98+
addr
99+
}
100+
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
101+
unsafe {
102+
core::arch::asm!("out dx, al",
103+
in("dx") OutBAction::TraceMemoryFree as u16,
104+
in("rax") layout.size() as u64,
105+
in("rcx") ptr as u64);
106+
}
107+
self.0.dealloc(ptr, layout)
108+
}
109+
unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
110+
let addr = self.0.alloc_zeroed(layout);
111+
unsafe {
112+
core::arch::asm!("out dx, al",
113+
in("dx") OutBAction::TraceMemoryAlloc as u16,
114+
in("rax") layout.size() as u64,
115+
in("rcx") addr as u64);
116+
}
117+
addr
118+
}
119+
unsafe fn realloc(&self, ptr: *mut u8, layout: core::alloc::Layout, new_size: usize) -> *mut u8 {
120+
let new_ptr = self.0.realloc(ptr, layout, new_size);
121+
unsafe {
122+
core::arch::asm!("out dx, al",
123+
in("dx") OutBAction::TraceMemoryFree as u16,
124+
in("rax") layout.size() as u64,
125+
in("rcx") ptr);
126+
core::arch::asm!("out dx, al",
127+
in("dx") OutBAction::TraceMemoryAlloc as u16,
128+
in("rax") new_size as u64,
129+
in("rcx") new_ptr);
130+
}
131+
new_ptr
132+
}
133+
}
134+
#[cfg(not(feature = "mem_profile"))]
86135
#[global_allocator]
87136
pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
137+
#[cfg(feature = "mem_profile")]
138+
#[global_allocator]
139+
pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> = ProfiledLockedHeap(LockedHeap::<32>::empty());
88140

89141
///cbindgen:ignore
90142
#[no_mangle]

src/hyperlight_host/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ workspace = true
2424
goblin = { version = "0.9" }
2525
framehop = { version = "0.13.1", optional = true }
2626
fallible-iterator = { version = "0.3.0", optional = true }
27+
blake3 = { version = "1.5.5", optional = true }
2728
rand = { version = "0.8.5" }
2829
cfg-if = { version = "1.0.0" }
2930
libc = { version = "0.2.167" }
@@ -130,6 +131,7 @@ trace_guest = []
130131
# This feature enables unwinding the guest stack from the host, in
131132
# order to produce stack traces for debugging or profiling.
132133
unwind_guest = [ "trace_guest", "dep:framehop", "dep:fallible-iterator" ]
134+
mem_profile = [ "unwind_guest", "dep:blake3" ]
133135
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
134136
mshv = ["dep:mshv-bindings", "dep:mshv-ioctls"]
135137
inprocess = []

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ pub(super) enum OutBAction {
4848
Abort,
4949
#[cfg(feature = "unwind_guest")]
5050
TraceRecordStack,
51+
#[cfg(feature = "mem_profile")]
52+
TraceMemoryAlloc,
53+
#[cfg(feature = "mem_profile")]
54+
TraceMemoryFree,
5155
}
5256

5357
impl TryFrom<u16> for OutBAction {
@@ -60,6 +64,10 @@ impl TryFrom<u16> for OutBAction {
6064
102 => Ok(OutBAction::Abort),
6165
#[cfg(feature = "unwind_guest")]
6266
103 => Ok(OutBAction::TraceRecordStack),
67+
#[cfg(feature = "mem_profile")]
68+
104 => Ok(OutBAction::TraceMemoryAlloc),
69+
#[cfg(feature = "mem_profile")]
70+
105 => Ok(OutBAction::TraceMemoryFree),
6371
_ => Err(new_error!("Invalid OutB value: {}", val)),
6472
}
6573
}
@@ -234,6 +242,38 @@ fn handle_outb_impl(
234242
|f| { write_stack(f, &stack); }
235243
)
236244
}
245+
#[cfg(feature = "mem_profile")]
246+
OutBAction::TraceMemoryAlloc => {
247+
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), &mut _trace_info)
248+
else { return Ok(()); };
249+
let Ok(amt) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RAX)
250+
else { return Ok(()) };
251+
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX)
252+
else { return Ok(()); };
253+
record_trace_frame(
254+
&_trace_info,
255+
2u64,
256+
|f| {
257+
let _ = f.write_all(&ptr.to_ne_bytes());
258+
let _ = f.write_all(&amt.to_ne_bytes());
259+
write_stack(f, &stack);
260+
}
261+
)
262+
}
263+
#[cfg(feature = "mem_profile")]
264+
OutBAction::TraceMemoryFree => {
265+
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), &mut _trace_info)
266+
else { return Ok(()); };
267+
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX)
268+
else { return Ok(()); };
269+
record_trace_frame(
270+
&_trace_info,
271+
3u64,
272+
|f| {
273+
let _ = f.write_all(&ptr.to_ne_bytes());
274+
write_stack(f, &stack);
275+
}
276+
)
237277
}
238278
}
239279
}

0 commit comments

Comments
 (0)