@@ -78,6 +78,8 @@ use zip::ZipArchive;
7878use zip:: ZipWriter ;
7979use zip:: write:: FullFileOptions ;
8080
81+ use super :: support_bundle:: perfetto;
82+
8183// We use "/var/tmp" to use Nexus' filesystem for temporary storage,
8284// rather than "/tmp", which would keep this collected data in-memory.
8385const TEMPDIR : & str = "/var/tmp" ;
@@ -1088,27 +1090,27 @@ impl BundleCollection {
10881090 . max ( 0 ) ;
10891091 let step_id = i + 1 ;
10901092
1091- json ! ( {
1092- " name" : step. name,
1093- " cat" : "bundle_collection" ,
1094- "ph" : "X" , // Complete event (has duration)
1095- "ts" : start_us,
1096- " dur" : duration_us,
1097- " pid" : 1 ,
1098- " tid" : step_id,
1099- " args" : {
1093+ perfetto :: TraceEvent {
1094+ name : step. name . clone ( ) ,
1095+ cat : "bundle_collection" . to_string ( ) ,
1096+ ph : "X" . to_string ( ) ,
1097+ ts : start_us,
1098+ dur : duration_us,
1099+ pid : 1 ,
1100+ tid : step_id,
1101+ args : json ! ( {
11001102 "status" : step. status. to_string( ) ,
1101- }
1102- } )
1103+ } ) ,
1104+ }
11031105 } )
11041106 . collect ( ) ;
11051107
1106- let trace_json = json ! ( {
1107- "traceEvents" : trace_events,
1108- "displayTimeUnit" : "ms" ,
1109- } ) ;
1108+ let trace = perfetto :: Trace {
1109+ trace_events,
1110+ display_time_unit : "ms" . to_string ( ) ,
1111+ } ;
11101112
1111- let trace_content = serde_json:: to_string_pretty ( & trace_json )
1113+ let trace_content = serde_json:: to_string_pretty ( & trace )
11121114 . context ( "Failed to serialize trace JSON" ) ?;
11131115
11141116 tokio:: fs:: write ( & trace_path, trace_content) . await . with_context (
@@ -1119,7 +1121,7 @@ impl BundleCollection {
11191121 self . log,
11201122 "Wrote trace file" ;
11211123 "path" => %trace_path,
1122- "num_events" => trace_events. len( )
1124+ "num_events" => trace . trace_events. len( )
11231125 ) ;
11241126
11251127 Ok ( ( ) )
@@ -2669,64 +2671,51 @@ mod test {
26692671 . await
26702672 . expect ( "Should be able to download trace file" ) ;
26712673
2672- // Parse the trace file as JSON
2674+ // Parse the trace file using our Perfetto structs
26732675 let body_bytes =
26742676 response. into_body ( ) . collect ( ) . await . unwrap ( ) . to_bytes ( ) ;
2675- let trace_json : serde_json :: Value = serde_json:: from_slice ( & body_bytes)
2676- . expect ( "Trace file should be valid JSON" ) ;
2677+ let trace : perfetto :: Trace = serde_json:: from_slice ( & body_bytes)
2678+ . expect ( "Trace file should be valid Perfetto JSON" ) ;
26772679
2678- // Verify the structure matches Perfetto Trace Event format
2679- let trace_events = trace_json
2680- . get ( "traceEvents" )
2681- . expect ( "Should have traceEvents field" )
2682- . as_array ( )
2683- . expect ( "traceEvents should be an array" ) ;
2680+ // Verify display time unit
2681+ assert_eq ! (
2682+ trace. display_time_unit, "ms" ,
2683+ "Display time unit should be milliseconds"
2684+ ) ;
26842685
26852686 // We should have at least the main collection steps
26862687 assert ! (
2687- !trace_events. is_empty( ) ,
2688+ !trace . trace_events. is_empty( ) ,
26882689 "Should have at least one trace event"
26892690 ) ;
26902691
2691- // Verify each event has the expected fields
2692- for event in trace_events {
2693- assert ! ( event . get ( "name" ) . is_some ( ) , "Event should have name" ) ;
2692+ // Verify each event has the expected structure
2693+ for event in & trace . trace_events {
2694+ // Verify category
26942695 assert_eq ! (
2695- event. get( "cat" ) . and_then( |v| v. as_str( ) ) ,
2696- Some ( "bundle_collection" ) ,
2696+ event. cat, "bundle_collection" ,
26972697 "Event should have category 'bundle_collection'"
26982698 ) ;
2699- assert_eq ! (
2700- event. get( "ph" ) . and_then( |v| v. as_str( ) ) ,
2701- Some ( "X" ) ,
2702- "Event should be Complete event type"
2703- ) ;
2704- assert ! (
2705- event. get( "ts" ) . and_then( |v| v. as_i64( ) ) . is_some( ) ,
2706- "Event should have timestamp"
2707- ) ;
2708- assert ! (
2709- event. get( "dur" ) . and_then( |v| v. as_i64( ) ) . is_some( ) ,
2710- "Event should have duration"
2711- ) ;
2712- assert ! (
2713- event. get( "args" ) . is_some( ) ,
2714- "Event should have args field"
2715- ) ;
2699+ // Verify phase type
2700+ assert_eq ! ( event. ph, "X" , "Event should be Complete event type" ) ;
2701+ // Verify timestamps are positive
2702+ assert ! ( event. ts >= 0 , "Event timestamp should be non-negative" ) ;
2703+ assert ! ( event. dur >= 0 , "Event duration should be non-negative" ) ;
2704+ // Verify process and thread IDs are set
2705+ assert_eq ! ( event. pid, 1 , "All events should have pid=1" ) ;
2706+ assert ! ( event. tid > 0 , "Event thread ID should be positive" ) ;
27162707 }
27172708
27182709 // Verify we have the same number of events as steps in the report
27192710 assert_eq ! (
2720- trace_events. len( ) ,
2711+ trace . trace_events. len( ) ,
27212712 report. steps. len( ) ,
27222713 "Number of events should match number of steps"
27232714 ) ;
27242715
27252716 // Verify step names match between report and trace
2726- let trace_names: std:: collections:: HashSet < _ > = trace_events
2727- . iter ( )
2728- . filter_map ( |e| e. get ( "name" ) . and_then ( |v| v. as_str ( ) ) )
2729- . collect ( ) ;
2717+ let trace_names: std:: collections:: HashSet < _ > =
2718+ trace. trace_events . iter ( ) . map ( |e| e. name . as_str ( ) ) . collect ( ) ;
27302719 let report_names: std:: collections:: HashSet < _ > =
27312720 report. steps . iter ( ) . map ( |s| s. name . as_str ( ) ) . collect ( ) ;
27322721 assert_eq ! (
0 commit comments