Skip to content

Commit ccaabc2

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

File tree

2 files changed

+223
-4
lines changed

2 files changed

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

0 commit comments

Comments
 (0)