Skip to content

Commit 0a6c80b

Browse files
committed
[trace-guest] update outb instructions to include trace info
Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 6de307b commit 0a6c80b

File tree

4 files changed

+105
-2
lines changed

4 files changed

+105
-2
lines changed

src/hyperlight_common/src/outb.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,16 @@ impl TryFrom<u8> for Exception {
9090
/// - CallFunction: makes a call to a host function,
9191
/// - Abort: aborts the execution of the guest,
9292
/// - DebugPrint: prints a message to the host
93+
/// - TraceBatch: reports a batch of spans and vents from the guest
9394
/// - TraceMemoryAlloc: records memory allocation events
9495
/// - TraceMemoryFree: records memory deallocation events
9596
pub enum OutBAction {
9697
Log = 99,
9798
CallFunction = 101,
9899
Abort = 102,
99100
DebugPrint = 103,
101+
#[cfg(feature = "trace_guest")]
102+
TraceBatch = 104,
100103
#[cfg(feature = "mem_profile")]
101104
TraceMemoryAlloc = 105,
102105
#[cfg(feature = "mem_profile")]
@@ -111,6 +114,8 @@ impl TryFrom<u16> for OutBAction {
111114
101 => Ok(OutBAction::CallFunction),
112115
102 => Ok(OutBAction::Abort),
113116
103 => Ok(OutBAction::DebugPrint),
117+
#[cfg(feature = "trace_guest")]
118+
104 => Ok(OutBAction::TraceBatch),
114119
#[cfg(feature = "mem_profile")]
115120
105 => Ok(OutBAction::TraceMemoryAlloc),
116121
#[cfg(feature = "mem_profile")]

src/hyperlight_guest/src/exit.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,34 @@ use hyperlight_common::outb::OutBAction;
2222
/// Halt the execution of the guest and returns control to the host.
2323
#[inline(never)]
2424
pub fn halt() {
25-
unsafe { asm!("hlt", options(nostack)) }
25+
#[cfg(feature = "trace_guest")]
26+
{
27+
// End any ongoing trace before halting
28+
hyperlight_guest_tracing::end_trace();
29+
// If tracing is enabled, we need to pass the trace batch info
30+
// along with the halt instruction so the host can retrieve it
31+
if let Some(tbi) = hyperlight_guest_tracing::guest_trace_info() {
32+
unsafe {
33+
asm!("hlt",
34+
in("r8") OutBAction::TraceBatch as u64,
35+
in("r9") tbi.guest_start_tsc,
36+
in("r10") tbi.spans_ptr,
37+
in("r11") tbi.events_ptr,
38+
options(nostack)
39+
)
40+
};
41+
hyperlight_guest_tracing::clean_trace_state();
42+
} else {
43+
// If tracing is not enabled, we can directly halt
44+
unsafe { asm!("hlt", options(nostack)) };
45+
}
46+
}
47+
48+
#[cfg(not(feature = "trace_guest"))]
49+
{
50+
// If tracing is not enabled, we can directly halt
51+
unsafe { asm!("hlt", options(nostack)) };
52+
}
2653
}
2754

2855
/// Exits the VM with an Abort OUT action and code 0.
@@ -33,6 +60,9 @@ pub extern "C" fn abort() -> ! {
3360

3461
/// Exits the VM with an Abort OUT action and a specific code.
3562
pub fn abort_with_code(code: &[u8]) -> ! {
63+
// End any ongoing trace before aborting
64+
#[cfg(feature = "trace_guest")]
65+
hyperlight_guest_tracing::end_trace();
3666
outb(OutBAction::Abort as u16, code);
3767
outb(OutBAction::Abort as u16, &[0xFF]); // send abort terminator (if not included in code)
3868
unreachable!()
@@ -43,6 +73,9 @@ pub fn abort_with_code(code: &[u8]) -> ! {
4373
/// # Safety
4474
/// This function is unsafe because it dereferences a raw pointer.
4575
pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! {
76+
// End any ongoing trace before aborting
77+
#[cfg(feature = "trace_guest")]
78+
hyperlight_guest_tracing::end_trace();
4679
unsafe {
4780
// Step 1: Send abort code (typically 1 byte, but `code` allows flexibility)
4881
outb(OutBAction::Abort as u16, code);
@@ -89,6 +122,31 @@ pub(crate) fn outb(port: u16, data: &[u8]) {
89122

90123
/// OUT function for sending a 32-bit value to the host.
91124
pub(crate) unsafe fn out32(port: u16, val: u32) {
125+
#[cfg(feature = "trace_guest")]
126+
{
127+
if let Some(tbi) = hyperlight_guest_tracing::guest_trace_info() {
128+
// If tracing is enabled, send the trace batch info along with the OUT action
129+
unsafe {
130+
asm!("out dx, eax",
131+
in("dx") port,
132+
in("eax") val,
133+
in("r8") OutBAction::TraceBatch as u64,
134+
in("r9") tbi.guest_start_tsc,
135+
in("r10") tbi.spans_ptr,
136+
in("r11") tbi.events_ptr,
137+
options(preserves_flags, nomem, nostack)
138+
)
139+
};
140+
141+
hyperlight_guest_tracing::clean_trace_state();
142+
} else {
143+
// If tracing is not enabled, just send the value
144+
unsafe {
145+
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack))
146+
};
147+
}
148+
}
149+
#[cfg(not(feature = "trace_guest"))]
92150
unsafe {
93151
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
94152
}

src/hyperlight_guest_tracing/src/lib.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ mod visitor;
3737
#[cfg(feature = "trace")]
3838
pub use state::TraceBatchInfo;
3939
#[cfg(feature = "trace")]
40-
pub use trace::{init_guest_tracing, set_start_tsc};
40+
pub use trace::{
41+
clean_trace_state, end_trace, guest_trace_info, init_guest_tracing, set_start_tsc,
42+
};
4143

4244
/// Maximum number of spans that the guest can store
4345
const MAX_NO_OF_SPANS: usize = 10;
@@ -174,4 +176,40 @@ mod trace {
174176
}
175177
}
176178
}
179+
180+
/// Ends the current trace by ending all active spans in the
181+
/// internal state and storing the end timestamps.
182+
///
183+
/// This expects an outb call to send the spans to the host.
184+
/// After calling this function, the internal state is marked
185+
/// for cleaning on the next access.
186+
pub fn end_trace() {
187+
if let Some(w) = GUEST_STATE.get() {
188+
if let Some(state) = w.upgrade() {
189+
state.lock().end_trace();
190+
}
191+
}
192+
}
193+
194+
/// Cleans the internal trace state by removing closed spans and events.
195+
/// This ensures that after a VM exit, we keep the spans that
196+
/// are still active (in the stack) and remove all other spans and events.
197+
pub fn clean_trace_state() {
198+
if let Some(w) = GUEST_STATE.get() {
199+
if let Some(state) = w.upgrade() {
200+
state.lock().clean();
201+
}
202+
}
203+
}
204+
205+
/// Returns information about the current trace state needed by the host to read the spans.
206+
pub fn guest_trace_info() -> Option<TraceBatchInfo> {
207+
let mut res = None;
208+
if let Some(w) = GUEST_STATE.get() {
209+
if let Some(state) = w.upgrade() {
210+
res = Some(state.lock().guest_trace_info());
211+
}
212+
}
213+
res
214+
}
177215
}

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ pub(crate) fn handle_outb(
182182
eprint!("{}", ch);
183183
Ok(())
184184
}
185+
#[cfg(feature = "trace_guest")]
186+
OutBAction::TraceBatch => Ok(()),
185187
#[cfg(feature = "mem_profile")]
186188
OutBAction::TraceMemoryAlloc => {
187189
let regs = _hv.regs()?;

0 commit comments

Comments
 (0)