Skip to content

Commit 60bb45c

Browse files
committed
fixup: address review comments
- Update workflow test to use the new mshv default - Remove unnecessary comments - Update documentation to reflect latest version of code - Update tracing macros to check the length of the message and fail if it would overflow the tracing buffer - Update tracing buffer to take a generic instead of a function to help with inlining and avoid performance penalties due to indirection Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 32445e5 commit 60bb45c

File tree

14 files changed

+72
-34
lines changed

14 files changed

+72
-34
lines changed

.github/workflows/dep_rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ jobs:
175175
env:
176176
CARGO_TERM_COLOR: always
177177
RUST_LOG: debug
178-
run: just test-rust-tracing ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}}
178+
run: just test-rust-tracing ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv2' && 'mshv2' || ''}}
179179

180180
- name: Download benchmarks from "latest"
181181
run: just bench-download ${{ runner.os }} ${{ matrix.hypervisor }} ${{ matrix.cpu}} dev-latest # compare to prerelease

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ test-rust-tracing target=default-target features="":
160160
| sed -n 's/.*Creating trace file at: \(.*\)/\1/p' \
161161
| xargs -I {} cargo run -p trace_dump ./{{ simpleguest_source }}/{{ target }}/simpleguest {} list_frames
162162

163-
# Rebuild the tracing guest without the tracing feature
163+
# Rebuild the tracing guests without the tracing feature
164164
# This is to ensure that the tracing feature does not affect the other tests
165165
just build-rust-guests {{ target }}
166166
just move-rust-guests {{ target }}

docs/hyperlight-metrics-logs-and-traces.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ Once the container or the exe is running, the trace output can be viewed in the
9696

9797
Hyperlight provides advanced observability features for guest code running inside micro virtual machines. You can enable guest-side tracing, stack unwinding, and memory profiling using the `trace_guest`, `unwind_guest`, and `mem_profile` features. This section explains how to build, run, and inspect guest traces.
9898

99+
The following features are available for guest tracing:
100+
- `trace_guest`: Enables tracing for guest code, capturing function calls and execution time.
101+
- `unwind_guest`: Enables stack unwinding for guest code, allowing you to capture stack traces.
102+
- `mem_profile`: Enables memory profiling for guest code, capturing memory allocations and usage.
103+
99104
### Building a Guest with Tracing Support
100105

101106
To build a guest with tracing enabled, use the following commands:
@@ -135,7 +140,7 @@ This command will list the stack frames and tracing information captured during
135140
cargo run -p trace_dump ./src/tests/rust_guests/bin/debug/simpleguest ./trace/<UUID>.trace list_frames
136141
```
137142

138-
You can use additional features such as `unwind_guest` and `mem_profile` by enabling them during the build and run steps. Refer to the guest and host documentation for more details on these features.
143+
You can use additional features such as `unwind_guest` and `mem_profile` by enabling them during the build and run steps.
139144

140145
> **Note:** Make sure to follow the build and run steps in order, and ensure that the guest binaries are up to date before running the host example.
141146

src/hyperlight_guest_bin/src/exceptions/handler.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ pub extern "C" fn hl_exception_handler(
6565
exception_number: u64,
6666
page_fault_address: u64,
6767
) {
68-
// This function does not return, so wrapping it in a tracing macro
69-
// issues a warning of unreachable code.
68+
// When using the `trace_function` macro, it wraps the function body with create_trace_record
69+
// call, which generates a warning because of the `abort_with_code_and_message` call which does
70+
// not return.
71+
// This is manually added to avoid the warning.
7072
hyperlight_guest_tracing_macro::trace!("> hl_exception_handler");
7173

7274
let ctx = stack_pointer as *mut Context;
@@ -99,6 +101,7 @@ pub extern "C" fn hl_exception_handler(
99101
)(exception_number, exn_info, ctx, page_fault_address)
100102
}
101103
{
104+
hyperlight_guest_tracing_macro::trace!("< hl_exception_handler");
102105
return;
103106
}
104107
}

src/hyperlight_guest_tracing/src/lib.rs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ use spin::Mutex;
2727
type SendToHostFn = fn(u64, &[TraceRecord]);
2828

2929
/// Global trace buffer for storing trace records.
30-
static TRACE_BUFFER: Mutex<TraceBuffer> = Mutex::new(TraceBuffer::new(send_to_host));
30+
static TRACE_BUFFER: Mutex<TraceBuffer<SendToHostFn>> = Mutex::new(TraceBuffer::new(send_to_host));
3131

3232
/// Maximum number of entries in the trace buffer.
3333
/// From local testing, 32 entries seems to be a good balance between performance and memory usage.
3434
const MAX_NO_OF_ENTRIES: usize = 32;
3535

3636
/// Maximum length of a trace message in bytes.
37-
const MAX_TRACE_MSG_LEN: usize = 64;
37+
pub const MAX_TRACE_MSG_LEN: usize = 64;
3838

3939
#[derive(Debug, Copy, Clone)]
4040
/// Represents a trace record of a guest with a number of cycles and a message.
@@ -47,41 +47,40 @@ pub struct TraceRecord {
4747
pub msg: [u8; MAX_TRACE_MSG_LEN],
4848
}
4949

50-
impl TryFrom<&str> for TraceRecord {
51-
type Error = &'static str;
52-
53-
fn try_from(msg: &str) -> Result<Self, Self::Error> {
50+
impl From<&str> for TraceRecord {
51+
fn from(mut msg: &str) -> Self {
5452
if msg.len() > MAX_TRACE_MSG_LEN {
55-
return Err("Message too long");
53+
// If the message is too long, truncate it to fit the maximum length
54+
msg = &msg[..MAX_TRACE_MSG_LEN];
5655
}
5756

5857
let cycles = invariant_tsc::read_tsc();
5958

60-
Ok(TraceRecord {
59+
TraceRecord {
6160
cycles,
6261
msg: {
6362
let mut arr = [0u8; MAX_TRACE_MSG_LEN];
6463
arr[..msg.len()].copy_from_slice(msg.as_bytes());
6564
arr
6665
},
6766
msg_len: msg.len(),
68-
})
67+
}
6968
}
7069
}
7170

7271
/// A buffer for storing trace records.
73-
struct TraceBuffer {
72+
struct TraceBuffer<F: Fn(u64, &[TraceRecord])> {
7473
/// The entries in the trace buffer.
7574
entries: [TraceRecord; MAX_NO_OF_ENTRIES],
7675
/// The index where the next entry will be written.
7776
write_index: usize,
7877
/// Function to send the trace records to the host.
79-
send_to_host: SendToHostFn,
78+
send_to_host: F,
8079
}
8180

82-
impl TraceBuffer {
81+
impl<F: Fn(u64, &[TraceRecord])> TraceBuffer<F> {
8382
/// Creates a new `TraceBuffer` with uninitialized entries.
84-
const fn new(f: SendToHostFn) -> Self {
83+
const fn new(f: F) -> Self {
8584
Self {
8685
entries: unsafe { [MaybeUninit::zeroed().assume_init(); MAX_NO_OF_ENTRIES] },
8786
write_index: 0,
@@ -158,18 +157,15 @@ pub mod invariant_tsc {
158157
}
159158
}
160159

161-
/// Attempt to create a trace record from the message.
162-
/// If the message is too long it will skip the record.
163-
/// This is useful for ensuring that the trace buffer does not overflow.
164-
/// If the message is successfully converted, it will be pushed to the trace buffer.
160+
/// Create a trace record from the message and push it to the trace buffer.
165161
///
166-
/// **Note**: The message must not exceed `MAX_TRACE_MSG_LEN` bytes.
162+
/// **NOTE**: If the message is too long it will be truncated to fit within `MAX_TRACE_MSG_LEN`.
163+
/// This is useful for ensuring that the trace buffer does not overflow.
167164
pub fn create_trace_record(msg: &str) {
168-
// Maybe panic if the message is too long?
169-
if let Ok(entry) = TraceRecord::try_from(msg) {
170-
let mut buffer = TRACE_BUFFER.lock();
171-
buffer.push(entry);
172-
}
165+
let entry = TraceRecord::from(msg);
166+
let mut buffer = TRACE_BUFFER.lock();
167+
168+
buffer.push(entry);
173169
}
174170

175171
/// Flush the trace buffer to send any remaining trace records to the host.
@@ -271,7 +267,11 @@ mod tests {
271267
#[test]
272268
fn test_trace_record_creation_too_long() {
273269
let long_msg = "A".repeat(MAX_TRACE_MSG_LEN + 1);
274-
let result = TraceRecord::try_from(long_msg.as_str());
275-
assert!(result.is_err(), "Expected error for message too long");
270+
let result = TraceRecord::from(long_msg.as_str());
271+
assert_eq!(result.msg_len, MAX_TRACE_MSG_LEN);
272+
assert_eq!(
273+
&result.msg[..MAX_TRACE_MSG_LEN],
274+
&long_msg.as_bytes()[..MAX_TRACE_MSG_LEN],
275+
);
276276
}
277277
}

src/hyperlight_guest_tracing_macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ readme.workspace = true
1010
description = """Provides the tracing macros for the hyperlight guest, enabling structured logging and tracing capabilities."""
1111

1212
[dependencies]
13+
hyperlight-guest-tracing = { workspace = true }
1314
proc-macro2 = "1.0"
1415
quote = "1.0.40"
1516
syn = { version = "2.0.104", features = ["full"] }

src/hyperlight_guest_tracing_macro/src/lib.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,31 @@ pub fn trace_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
4444
let fn_attrs = &input_fn.attrs;
4545
let fn_output = &input_fn.sig.output;
4646

47+
// Compose entry/exit messages
48+
let entry_msg = format!("> {}", fn_name_str);
49+
let exit_msg = format!("< {}", fn_name_str);
50+
51+
if entry_msg.len() > hyperlight_guest_tracing::MAX_TRACE_MSG_LEN
52+
|| exit_msg.len() > hyperlight_guest_tracing::MAX_TRACE_MSG_LEN
53+
{
54+
panic!(
55+
"Trace messages must not exceed {} bytes in length.",
56+
hyperlight_guest_tracing::MAX_TRACE_MSG_LEN
57+
);
58+
}
59+
4760
let expanded = match fn_output {
4861
syn::ReturnType::Default => {
4962
// No return value (unit)
5063
quote! {
5164
#(#fn_attrs)*
5265
#fn_vis #fn_sig {
5366
#[cfg(feature = "trace_guest")]
54-
hyperlight_guest_tracing::create_trace_record(concat!("> ", #fn_name_str));
67+
hyperlight_guest_tracing::create_trace_record(#entry_msg);
5568
// Call the original function body
5669
#fn_block
5770
#[cfg(feature = "trace_guest")]
58-
hyperlight_guest_tracing::create_trace_record(concat!("< ", #fn_name_str));
71+
hyperlight_guest_tracing::create_trace_record(#exit_msg);
5972
}
6073
}
6174
}
@@ -90,6 +103,18 @@ impl syn::parse::Parse for TraceMacroInput {
90103
if !matches!(message, syn::Lit::Str(_)) {
91104
return Err(input.error("first argument to trace! must be a string literal"));
92105
}
106+
if let syn::Lit::Str(ref lit_str) = message {
107+
if lit_str.value().is_empty() {
108+
return Err(input.error("trace message must not be empty"));
109+
}
110+
if lit_str.value().len() > hyperlight_guest_tracing::MAX_TRACE_MSG_LEN {
111+
return Err(input.error(format!(
112+
"trace message must not exceed {} bytes",
113+
hyperlight_guest_tracing::MAX_TRACE_MSG_LEN
114+
)));
115+
}
116+
}
117+
93118
let statement = if input.peek(syn::Token![,]) {
94119
let _: syn::Token![,] = input.parse()?;
95120
Some(input.parse()?)

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,6 @@ fn handle_outb_impl(
353353
let buffer = &mut buffer[..];
354354

355355
// Read the trace records from the guest memory
356-
// TODO: maybe this can be done without copying?
357356
mem_mgr
358357
.as_ref()
359358
.shared_mem

src/tests/rust_guests/callbackguest/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)