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