@@ -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