Skip to content

Commit d26ccba

Browse files
syntacticallydblnz
authored andcommitted
Add trace support for recording memory allocations and frees
This allows for producing profiles of memory usage. Signed-off-by: Lucy Menon <[email protected]> Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent e027582 commit d26ccba

File tree

6 files changed

+111
-2
lines changed

6 files changed

+111
-2
lines changed

src/hyperlight_common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ spin = "0.10.0"
2626
default = ["tracing"]
2727
fuzzing = ["dep:arbitrary"]
2828
unwind_guest = []
29+
mem_profile = []
2930
std = []
3031

3132
[dev-dependencies]

src/hyperlight_common/src/outb.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,19 @@ impl TryFrom<u8> for Exception {
9191
/// - Abort: aborts the execution of the guest,
9292
/// - DebugPrint: prints a message to the host
9393
/// - TraceRecordStack: records the stack trace of the guest
94+
/// - TraceMemoryAlloc: records memory allocation events
95+
/// - TraceMemoryFree: records memory deallocation events
9496
pub enum OutBAction {
9597
Log = 99,
9698
CallFunction = 101,
9799
Abort = 102,
98100
DebugPrint = 103,
99101
#[cfg(feature = "unwind_guest")]
100102
TraceRecordStack = 104,
103+
#[cfg(feature = "mem_profile")]
104+
TraceMemoryAlloc,
105+
#[cfg(feature = "mem_profile")]
106+
TraceMemoryFree,
101107
}
102108

103109
impl TryFrom<u16> for OutBAction {
@@ -110,6 +116,10 @@ impl TryFrom<u16> for OutBAction {
110116
103 => Ok(OutBAction::DebugPrint),
111117
#[cfg(feature = "unwind_guest")]
112118
104 => Ok(OutBAction::TraceRecordStack),
119+
#[cfg(feature = "mem_profile")]
120+
105 => Ok(OutBAction::TraceMemoryAlloc),
121+
#[cfg(feature = "mem_profile")]
122+
106 => Ok(OutBAction::TraceMemoryFree),
113123
_ => Err(anyhow::anyhow!("Invalid OutBAction value: {}", val)),
114124
}
115125
}

src/hyperlight_guest_bin/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and third-party code used by our C-API needed to build a native hyperlight-guest
1717
default = ["libc", "printf"]
1818
libc = [] # compile musl libc
1919
printf = [] # compile printf
20+
mem_profile = ["hyperlight-common/unwind_guest","hyperlight-common/mem_profile"]
2021

2122
[dependencies]
2223
hyperlight-guest = { workspace = true, default-features = false }

src/hyperlight_guest_bin/src/lib.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use guest_function::register::GuestFunctionRegister;
2828
use guest_logger::init_logger;
2929
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
3030
use hyperlight_common::mem::HyperlightPEB;
31+
#[cfg(feature = "mem_profile")]
32+
use hyperlight_common::outb::OutBAction;
3133
use hyperlight_guest::exit::{abort_with_code_and_message, halt};
3234
use hyperlight_guest::guest_handle::handle::GuestHandle;
3335
use log::LevelFilter;
@@ -54,9 +56,69 @@ pub mod host_comm;
5456
pub mod memory;
5557
pub mod paging;
5658

59+
// Globals
60+
#[cfg(feature = "mem_profile")]
61+
struct ProfiledLockedHeap<const ORDER: usize>(LockedHeap<ORDER>);
62+
#[cfg(feature = "mem_profile")]
63+
unsafe impl<const ORDER: usize> alloc::alloc::GlobalAlloc for ProfiledLockedHeap<ORDER> {
64+
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
65+
let addr = unsafe { self.0.alloc(layout) };
66+
unsafe {
67+
core::arch::asm!("out dx, al",
68+
in("dx") OutBAction::TraceMemoryAlloc as u16,
69+
in("rax") layout.size() as u64,
70+
in("rcx") addr as u64);
71+
}
72+
addr
73+
}
74+
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
75+
unsafe {
76+
core::arch::asm!("out dx, al",
77+
in("dx") OutBAction::TraceMemoryFree as u16,
78+
in("rax") layout.size() as u64,
79+
in("rcx") ptr as u64);
80+
self.0.dealloc(ptr, layout)
81+
}
82+
}
83+
unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
84+
let addr = unsafe { self.0.alloc_zeroed(layout) };
85+
unsafe {
86+
core::arch::asm!("out dx, al",
87+
in("dx") OutBAction::TraceMemoryAlloc as u16,
88+
in("rax") layout.size() as u64,
89+
in("rcx") addr as u64);
90+
}
91+
addr
92+
}
93+
unsafe fn realloc(
94+
&self,
95+
ptr: *mut u8,
96+
layout: core::alloc::Layout,
97+
new_size: usize,
98+
) -> *mut u8 {
99+
let new_ptr = unsafe { self.0.realloc(ptr, layout, new_size) };
100+
unsafe {
101+
core::arch::asm!("out dx, al",
102+
in("dx") OutBAction::TraceMemoryFree as u16,
103+
in("rax") layout.size() as u64,
104+
in("rcx") ptr);
105+
core::arch::asm!("out dx, al",
106+
in("dx") OutBAction::TraceMemoryAlloc as u16,
107+
in("rax") new_size as u64,
108+
in("rcx") new_ptr);
109+
}
110+
new_ptr
111+
}
112+
}
113+
57114
// === Globals ===
115+
#[cfg(not(feature = "mem_profile"))]
58116
#[global_allocator]
59117
pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
118+
#[cfg(feature = "mem_profile")]
119+
#[global_allocator]
120+
pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> =
121+
ProfiledLockedHeap(LockedHeap::<32>::empty());
60122

61123
pub(crate) static mut GUEST_HANDLE: GuestHandle = GuestHandle::new();
62124
pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
@@ -129,7 +191,11 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve
129191

130192
let heap_start = (*peb_ptr).guest_heap.ptr as usize;
131193
let heap_size = (*peb_ptr).guest_heap.size as usize;
132-
HEAP_ALLOCATOR
194+
#[cfg(not(feature = "mem_profile"))]
195+
let heap_allocator = &HEAP_ALLOCATOR;
196+
#[cfg(feature = "mem_profile")]
197+
let heap_allocator = &HEAP_ALLOCATOR.0;
198+
heap_allocator
133199
.try_lock()
134200
.expect("Failed to access HEAP_ALLOCATOR")
135201
.init(heap_start, heap_size);

src/hyperlight_host/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ libc = { version = "0.2.174" }
3030
flatbuffers = "25.2.10"
3131
framehop = { version = "0.13.1", optional = true }
3232
fallible-iterator = { version = "0.3.0", optional = true }
33+
blake3 = "1.8.2"
3334
page_size = "0.6.0"
3435
termcolor = "1.2.0"
3536
bitflags = "2.9.1"
@@ -47,7 +48,6 @@ metrics = "0.24.2"
4748
serde_json = "1.0"
4849
elfcore = "2.0"
4950
uuid = { version = "1.17.0", features = ["v4"] }
50-
blake3 = "1.8.2"
5151

5252
[target.'cfg(windows)'.dependencies]
5353
windows = { version = "0.61", features = [
@@ -135,6 +135,7 @@ trace_guest = []
135135
# This feature enables unwinding the guest stack from the host, in
136136
# order to produce stack traces for debugging or profiling.
137137
unwind_guest = [ "trace_guest", "dep:framehop", "dep:fallible-iterator", "hyperlight-common/unwind_guest" ]
138+
mem_profile = [ "unwind_guest", "hyperlight-common/mem_profile" ]
138139
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
139140
# This feature is deprecated in favor of mshv3
140141
mshv2 = ["dep:mshv-bindings2", "dep:mshv-ioctls2"]

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,36 @@ fn handle_outb_impl(
257257
write_stack(f, &stack);
258258
})
259259
}
260+
#[cfg(feature = "mem_profile")]
261+
OutBAction::TraceMemoryAlloc => {
262+
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), _hv.trace_info_as_ref()) else {
263+
return Ok(());
264+
};
265+
let Ok(amt) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RAX) else {
266+
return Ok(());
267+
};
268+
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else {
269+
return Ok(());
270+
};
271+
record_trace_frame(_hv.trace_info_as_ref(), 2u64, |f| {
272+
let _ = f.write_all(&ptr.to_ne_bytes());
273+
let _ = f.write_all(&amt.to_ne_bytes());
274+
write_stack(f, &stack);
275+
})
276+
}
277+
#[cfg(feature = "mem_profile")]
278+
OutBAction::TraceMemoryFree => {
279+
let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), _hv.trace_info_as_ref()) else {
280+
return Ok(());
281+
};
282+
let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else {
283+
return Ok(());
284+
};
285+
record_trace_frame(_hv.trace_info_as_ref(), 3u64, |f| {
286+
let _ = f.write_all(&ptr.to_ne_bytes());
287+
write_stack(f, &stack);
288+
})
289+
}
260290
}
261291
}
262292

0 commit comments

Comments
 (0)