Skip to content

Commit 771d069

Browse files
committed
Add TraceContext unit tests
Signed-off-by: Doru Blânzeanu <dblnz@pm.me>
1 parent d5aea38 commit 771d069

File tree

2 files changed

+224
-4
lines changed

2 files changed

+224
-4
lines changed

Justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ test-rust-tracing target=default-target features="":
196196
# Run tests for the tracing guest and macro
197197
{{ cargo-cmd }} test -p hyperlight-guest-tracing -F trace --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }}
198198
{{ cargo-cmd }} test -p hyperlight-common -F trace_guest --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }}
199+
{{ cargo-cmd }} test -p hyperlight-host --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features trace_guest'} else { "--features trace_guest," + features } }} {{ target-triple-flag }}
199200

200201
# Build the tracing guest to ensure it builds with the tracing feature
201202
just build-rust-guests {{ target }} trace_guest

src/hyperlight_host/src/sandbox/trace/context.rs

Lines changed: 223 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,17 @@ impl TraceContext {
235235
let trace_batch = TraceBatch::try_from((regs, mem_mgr))?;
236236
let trace_batch = trace_batch.data;
237237

238+
self.handle_trace_impl(trace_batch)
239+
}
240+
241+
fn handle_trace_impl(&mut self, trace_data: GuestTraceData) -> Result<()> {
238242
let tracer = global::tracer("guest-tracer");
239243

240244
// Stack to keep track of open spans
241245
let mut spans_stack = vec![];
242246

243247
// Process each event
244-
for ev in trace_batch.events.into_iter() {
248+
for ev in trace_data.events.into_iter() {
245249
match ev {
246250
GuestEvent::OpenSpan {
247251
id,
@@ -253,7 +257,7 @@ impl TraceContext {
253257
} => {
254258
// Calculate start timestamp
255259
let start_ts =
256-
self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?;
260+
self.calculate_guest_time_relative_to_host(trace_data.start_tsc, tsc)?;
257261

258262
// Determine parent context
259263
// Priority:
@@ -300,7 +304,7 @@ impl TraceContext {
300304
// Remove the span and end it
301305
if let Some(mut span) = self.guest_spans.remove(&id) {
302306
let end_ts =
303-
self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?;
307+
self.calculate_guest_time_relative_to_host(trace_data.start_tsc, tsc)?;
304308
span.end_with_timestamp(end_ts);
305309

306310
// The span ids should be closed in order
@@ -320,7 +324,7 @@ impl TraceContext {
320324
fields,
321325
} => {
322326
let ts =
323-
self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?;
327+
self.calculate_guest_time_relative_to_host(trace_data.start_tsc, tsc)?;
324328

325329
// Add the event to the parent span
326330
// It should always have a parent span
@@ -389,3 +393,218 @@ impl Drop for TraceContext {
389393
}
390394
}
391395
}
396+
397+
#[cfg(test)]
398+
mod tests {
399+
use hyperlight_common::flatbuffer_wrappers::guest_trace_data::{
400+
GuestEvent, GuestTraceData, KeyValue,
401+
};
402+
403+
use super::*;
404+
405+
fn create_dummy_trace_context() -> TraceContext {
406+
let mut trace_ctx = TraceContext::new();
407+
// Set TSC frequency to avoid calculating it
408+
trace_ctx.tsc_freq = Some(3_200_000_000); // 3.2 GHz
409+
// Set start wall time and Instant
410+
trace_ctx.start_wall = Some(SystemTime::now() - Duration::from_secs(1));
411+
trace_ctx.start_instant = Some(Instant::now() - Duration::from_secs(1));
412+
413+
trace_ctx
414+
}
415+
416+
fn create_open_span(
417+
id: u64,
418+
parent_id: Option<u64>,
419+
name_str: &str,
420+
target_str: &str,
421+
start_tsc: u64,
422+
fields: Vec<KeyValue>,
423+
) -> GuestEvent {
424+
GuestEvent::OpenSpan {
425+
id,
426+
parent_id,
427+
name: String::from(name_str),
428+
target: String::from(target_str),
429+
tsc: start_tsc,
430+
fields,
431+
}
432+
}
433+
434+
fn create_close_span(id: u64, end_tsc: u64) -> GuestEvent {
435+
GuestEvent::CloseSpan { id, tsc: end_tsc }
436+
}
437+
438+
fn create_log_event(
439+
parent_id: u64,
440+
tsc: u64,
441+
name_str: &str,
442+
fields: Vec<KeyValue>,
443+
) -> GuestEvent {
444+
GuestEvent::LogEvent {
445+
parent_id,
446+
name: String::from(name_str),
447+
tsc,
448+
fields,
449+
}
450+
}
451+
452+
#[test]
453+
fn test_guest_trace_context_creation() {
454+
let trace_ctx = TraceContext::new();
455+
assert!(trace_ctx.host_spans.len() == 1);
456+
assert!(trace_ctx.guest_spans.is_empty());
457+
}
458+
459+
/// Test handling a `TraceBatch` with no spans or events.
460+
#[test]
461+
fn test_guest_trace_empty_trace_batch() {
462+
let mut trace_ctx = TraceContext::new();
463+
464+
let trace_data = GuestTraceData {
465+
start_tsc: 0,
466+
events: Vec::new(),
467+
};
468+
469+
let res = trace_ctx.handle_trace_impl(trace_data);
470+
assert!(res.is_ok());
471+
assert!(trace_ctx.guest_spans.is_empty());
472+
assert!(trace_ctx.host_spans.len() == 1);
473+
}
474+
475+
/// Test handling a `TraceBatch` with one span and no events.
476+
/// The span is not closed.
477+
#[test]
478+
fn test_guest_trace_single_span() {
479+
let mut trace_ctx = create_dummy_trace_context();
480+
481+
let trace_data = GuestTraceData {
482+
start_tsc: 1000,
483+
events: vec![create_open_span(
484+
1,
485+
None,
486+
"test-span",
487+
"test-target",
488+
2000,
489+
vec![],
490+
)],
491+
};
492+
493+
let res = trace_ctx.handle_trace_impl(trace_data);
494+
assert!(res.is_ok());
495+
assert!(trace_ctx.guest_spans.len() == 1);
496+
// The active host span is new because a new guest span was created
497+
assert!(trace_ctx.host_spans.len() == 2);
498+
}
499+
500+
/// Test handling a `TraceBatch` with one span that is closed.
501+
/// The span is closed.
502+
#[test]
503+
fn test_guest_trace_single_closed_span() {
504+
let mut trace_ctx = create_dummy_trace_context();
505+
506+
let trace_data = GuestTraceData {
507+
start_tsc: 1000,
508+
events: vec![
509+
create_open_span(1, None, "test-span", "test-target", 2000, vec![]),
510+
create_close_span(1, 2500),
511+
],
512+
};
513+
514+
let res = trace_ctx.handle_trace_impl(trace_data);
515+
assert!(res.is_ok());
516+
assert!(trace_ctx.guest_spans.is_empty());
517+
// The active host span is the same as before because no new guest span was created
518+
// as the span was closed.
519+
assert!(trace_ctx.host_spans.len() == 1);
520+
}
521+
522+
/// Test handling a `TraceBatch` with one span and one event.
523+
/// The span is not closed.
524+
#[test]
525+
fn test_guest_trace_span_with_event() {
526+
let mut trace_ctx = create_dummy_trace_context();
527+
528+
let trace_data = GuestTraceData {
529+
start_tsc: 1000,
530+
events: vec![
531+
create_open_span(1, None, "test-span", "test-target", 2000, vec![]),
532+
create_log_event(1, 2500, "test-event", vec![]),
533+
],
534+
};
535+
536+
let res = trace_ctx.handle_trace_impl(trace_data);
537+
assert!(res.is_ok());
538+
assert!(trace_ctx.guest_spans.len() == 1);
539+
// The active host span is new because a new guest span was created
540+
assert!(trace_ctx.host_spans.len() == 2);
541+
}
542+
543+
/// Test handling a `TraceBatch` with two open spans in a parent-child relationship.
544+
/// The spans are not closed.
545+
#[test]
546+
fn test_guest_trace_parent_child_spans() {
547+
let mut trace_ctx = create_dummy_trace_context();
548+
549+
let trace_data = GuestTraceData {
550+
start_tsc: 1000,
551+
events: vec![
552+
create_open_span(1, None, "parent-span", "test-target", 2000, vec![]),
553+
create_open_span(2, Some(1), "child-span", "test-target", 2500, vec![]),
554+
],
555+
};
556+
557+
let res = trace_ctx.handle_trace_impl(trace_data);
558+
assert!(res.is_ok());
559+
assert!(trace_ctx.guest_spans.len() == 2);
560+
// The active host span is new because new guest spans were created
561+
assert!(trace_ctx.host_spans.len() == 2);
562+
}
563+
564+
/// Test handling a `TraceBatch` with two closed spans in a parent-child relationship.
565+
/// The spans are closed.
566+
#[test]
567+
fn test_guest_trace_closed_parent_child_spans() {
568+
let mut trace_ctx = create_dummy_trace_context();
569+
570+
let trace_data = GuestTraceData {
571+
start_tsc: 1000,
572+
events: vec![
573+
create_open_span(1, None, "parent-span", "test-target", 2000, vec![]),
574+
create_open_span(2, Some(1), "child-span", "test-target", 2500, vec![]),
575+
create_close_span(2, 3500),
576+
create_close_span(1, 3000),
577+
],
578+
};
579+
580+
let res = trace_ctx.handle_trace_impl(trace_data);
581+
assert!(res.is_ok());
582+
assert!(trace_ctx.guest_spans.is_empty());
583+
// The active host span is the same as before because no new guest spans were created
584+
// as the spans were closed.
585+
assert!(trace_ctx.host_spans.len() == 1);
586+
}
587+
588+
/// Test handling a `TraceBatch` with two spans partially closed in a parent-child
589+
/// relationship.
590+
/// The parent span is open, the child span is closed.
591+
#[test]
592+
fn test_guest_trace_partially_closed_parent_child_spans() {
593+
let mut trace_ctx = create_dummy_trace_context();
594+
595+
let trace_data = GuestTraceData {
596+
start_tsc: 1000,
597+
events: vec![
598+
create_open_span(1, None, "parent-span", "test-target", 2000, vec![]),
599+
create_open_span(2, Some(1), "child-span", "test-target", 2500, vec![]),
600+
create_close_span(2, 3500),
601+
],
602+
};
603+
604+
let res = trace_ctx.handle_trace_impl(trace_data);
605+
assert!(res.is_ok());
606+
assert!(trace_ctx.guest_spans.len() == 1);
607+
// The active host span is new because a new guest span was created
608+
assert!(trace_ctx.host_spans.len() == 2);
609+
}
610+
}

0 commit comments

Comments
 (0)