11#!/usr/bin/env python3
2+ # Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
3+ # SPDX-License-Identifier: MIT
4+
25# /// script
36# requires-python = ">=3.8"
47# dependencies = [
1720import sys
1821from collections import defaultdict
1922from datetime import datetime
20- from pathlib import Path
2123
2224try :
2325 from jinja2 import Environment , FileSystemLoader
3133def parse_arguments ():
3234 """Parse command-line arguments."""
3335 if len (sys .argv ) < 7 :
34- print ("Usage: analyze_build_trace.py <trace_file> <output_file> <target> <granularity> <build_time> <template_dir>" )
36+ print (
37+ "Usage: analyze_build_trace.py <trace_file> <output_file> <target> <granularity> <build_time> <template_dir>"
38+ )
3539 sys .exit (1 )
3640
3741 return {
38- ' trace_file' : sys .argv [1 ],
39- ' output_file' : sys .argv [2 ],
40- ' target' : sys .argv [3 ],
41- ' granularity' : sys .argv [4 ],
42- ' build_time' : sys .argv [5 ],
43- ' template_dir' : sys .argv [6 ],
42+ " trace_file" : sys .argv [1 ],
43+ " output_file" : sys .argv [2 ],
44+ " target" : sys .argv [3 ],
45+ " granularity" : sys .argv [4 ],
46+ " build_time" : sys .argv [5 ],
47+ " template_dir" : sys .argv [6 ],
4448 }
4549
4650
4751def load_trace_data (trace_file ):
4852 """Load and parse the trace JSON file."""
49- print (f' Loading trace file: { trace_file } ' )
50- with open (trace_file , 'r' ) as f :
53+ print (f" Loading trace file: { trace_file } " )
54+ with open (trace_file , "r" ) as f :
5155 return json .load (f )
5256
5357
5458def process_events (data ):
5559 """Process trace events and extract template instantiation statistics."""
56- print (' Processing events...' )
60+ print (" Processing events..." )
5761
58- template_stats = defaultdict (lambda : {' count' : 0 , ' total_dur' : 0 })
62+ template_stats = defaultdict (lambda : {" count" : 0 , " total_dur" : 0 })
5963 phase_stats = defaultdict (int )
6064 top_individual = []
6165
62- for event in data .get (' traceEvents' , []):
63- name = event .get (' name' , '' )
64- dur = int (event .get (' dur' , 0 )) # Keep as integer microseconds
66+ for event in data .get (" traceEvents" , []):
67+ name = event .get (" name" , "" )
68+ dur = int (event .get (" dur" , 0 )) # Keep as integer microseconds
6569
6670 if name and dur > 0 :
6771 phase_stats [name ] += dur
6872
69- if name in ['InstantiateFunction' , 'InstantiateClass' ]:
70- detail = event .get ('args' , {}).get ('detail' , '' )
71- top_individual .append ({
72- 'detail' : detail ,
73- 'dur' : dur ,
74- 'type' : name
75- })
73+ if name in ["InstantiateFunction" , "InstantiateClass" ]:
74+ detail = event .get ("args" , {}).get ("detail" , "" )
75+ top_individual .append ({"detail" : detail , "dur" : dur , "type" : name })
7676
7777 # Extract template name (everything before '<' or '(')
78- match = re .match (r' ^([^<(]+)' , detail )
78+ match = re .match (r" ^([^<(]+)" , detail )
7979 if match :
8080 template_name = match .group (1 ).strip ()
8181 # Normalize template names
82- template_name = re .sub (r' ^ck::' , '' , template_name )
83- template_name = re .sub (r' ^std::' , ' std::' , template_name )
82+ template_name = re .sub (r" ^ck::" , "" , template_name )
83+ template_name = re .sub (r" ^std::" , " std::" , template_name )
8484
85- template_stats [template_name ][' count' ] += 1
86- template_stats [template_name ][' total_dur' ] += dur
85+ template_stats [template_name ][" count" ] += 1
86+ template_stats [template_name ][" total_dur" ] += dur
8787
8888 return template_stats , phase_stats , top_individual
8989
9090
9191def prepare_template_data (template_stats , phase_stats , top_individual ):
9292 """Prepare and calculate derived statistics for template rendering."""
93- print (' Sorting data...' )
93+ print (" Sorting data..." )
9494
9595 # Sort data
9696 sorted_phases = sorted (phase_stats .items (), key = lambda x : x [1 ], reverse = True )
97- top_individual .sort (key = lambda x : x [' dur' ], reverse = True )
97+ top_individual .sort (key = lambda x : x [" dur" ], reverse = True )
9898
9999 # Calculate totals
100- total_template_time = sum (s [' total_dur' ] for s in template_stats .values ())
100+ total_template_time = sum (s [" total_dur" ] for s in template_stats .values ())
101101 total_trace_time = sum (phase_stats .values ())
102- total_inst = sum (s [' count' ] for s in template_stats .values ())
102+ total_inst = sum (s [" count" ] for s in template_stats .values ())
103103
104104 # Prepare templates by time with calculated fields
105105 templates_by_time = []
106- for name , stats in sorted (template_stats .items (), key = lambda x : x [1 ]['total_dur' ], reverse = True ):
107- templates_by_time .append ((name , {
108- 'count' : stats ['count' ],
109- 'total_dur' : stats ['total_dur' ],
110- 'avg' : stats ['total_dur' ] // stats ['count' ] if stats ['count' ] > 0 else 0 ,
111- 'pct' : 100 * stats ['total_dur' ] / total_template_time if total_template_time > 0 else 0
112- }))
106+ for name , stats in sorted (
107+ template_stats .items (), key = lambda x : x [1 ]["total_dur" ], reverse = True
108+ ):
109+ templates_by_time .append (
110+ (
111+ name ,
112+ {
113+ "count" : stats ["count" ],
114+ "total_dur" : stats ["total_dur" ],
115+ "avg" : stats ["total_dur" ] // stats ["count" ]
116+ if stats ["count" ] > 0
117+ else 0 ,
118+ "pct" : 100 * stats ["total_dur" ] / total_template_time
119+ if total_template_time > 0
120+ else 0 ,
121+ },
122+ )
123+ )
113124
114125 # Prepare templates by count
115126 templates_by_count = []
116- for name , stats in sorted (template_stats .items (), key = lambda x : x [1 ]['count' ], reverse = True ):
117- templates_by_count .append ((name , {
118- 'count' : stats ['count' ],
119- 'total_dur' : stats ['total_dur' ],
120- 'avg' : stats ['total_dur' ] // stats ['count' ] if stats ['count' ] > 0 else 0
121- }))
127+ for name , stats in sorted (
128+ template_stats .items (), key = lambda x : x [1 ]["count" ], reverse = True
129+ ):
130+ templates_by_count .append (
131+ (
132+ name ,
133+ {
134+ "count" : stats ["count" ],
135+ "total_dur" : stats ["total_dur" ],
136+ "avg" : stats ["total_dur" ] // stats ["count" ]
137+ if stats ["count" ] > 0
138+ else 0 ,
139+ },
140+ )
141+ )
122142
123143 # Add friendly type names to individual instantiations
124144 for inst in top_individual :
125- inst [' inst_type' ] = ' Func' if inst [' type' ] == ' InstantiateFunction' else ' Class'
145+ inst [" inst_type" ] = " Func" if inst [" type" ] == " InstantiateFunction" else " Class"
126146
127147 # Calculate additional metrics
128148 median_count = 0
129149 if len (template_stats ) > 0 :
130- median_count = sorted ([s ["count" ] for s in template_stats .values ()])[len (template_stats ) // 2 ]
150+ median_count = sorted ([s ["count" ] for s in template_stats .values ()])[
151+ len (template_stats ) // 2
152+ ]
131153
132154 top10_pct = 0
133155 if len (templates_by_time ) >= 10 :
134- top10_pct = 100 * sum (s [1 ]["total_dur" ] for s in templates_by_time [:10 ]) / total_template_time
156+ top10_pct = (
157+ 100
158+ * sum (s [1 ]["total_dur" ] for s in templates_by_time [:10 ])
159+ / total_template_time
160+ )
135161
136162 return {
137- ' sorted_phases' : sorted_phases ,
138- ' top_individual' : top_individual ,
139- ' templates_by_time' : templates_by_time ,
140- ' templates_by_count' : templates_by_count ,
141- ' total_template_time' : total_template_time ,
142- ' total_trace_time' : total_trace_time ,
143- ' total_inst' : total_inst ,
144- ' median_count' : median_count ,
145- ' top10_pct' : top10_pct ,
146- ' unique_families' : len (template_stats ),
163+ " sorted_phases" : sorted_phases ,
164+ " top_individual" : top_individual ,
165+ " templates_by_time" : templates_by_time ,
166+ " templates_by_count" : templates_by_count ,
167+ " total_template_time" : total_template_time ,
168+ " total_trace_time" : total_trace_time ,
169+ " total_inst" : total_inst ,
170+ " median_count" : median_count ,
171+ " top10_pct" : top10_pct ,
172+ " unique_families" : len (template_stats ),
147173 }
148174
149175
@@ -153,17 +179,17 @@ def setup_jinja_environment(template_dir):
153179
154180 def format_number (value ):
155181 """Format number with thousand separators."""
156- return f' { value :,} '
182+ return f" { value :,} "
157183
158184 def truncate (value , length ):
159185 """Truncate string to length with ellipsis."""
160186 if len (value ) > length :
161- return value [:length - 3 ] + ' ...'
187+ return value [: length - 3 ] + " ..."
162188 return value
163189
164190 def pad (value , length ):
165191 """Pad string to specified length."""
166- return f' { value :<{length }} '
192+ return f" { value :<{length }} "
167193
168194 def us_to_ms (value ):
169195 """Convert microseconds to milliseconds."""
@@ -173,37 +199,37 @@ def us_to_s(value):
173199 """Convert microseconds to seconds."""
174200 return value / 1000000.0
175201
176- env .filters [' format_number' ] = format_number
177- env .filters [' truncate' ] = truncate
178- env .filters [' pad' ] = pad
179- env .filters [' us_to_ms' ] = us_to_ms
180- env .filters [' us_to_s' ] = us_to_s
202+ env .filters [" format_number" ] = format_number
203+ env .filters [" truncate" ] = truncate
204+ env .filters [" pad" ] = pad
205+ env .filters [" us_to_ms" ] = us_to_ms
206+ env .filters [" us_to_s" ] = us_to_s
181207
182208 return env
183209
184210
185211def generate_report (env , data , args , total_events ):
186212 """Generate the final report using Jinja2 template."""
187- print (' Rendering report with Jinja2...' )
213+ print (" Rendering report with Jinja2..." )
188214
189- template = env .get_template (' build_analysis_report.md.jinja' )
215+ template = env .get_template (" build_analysis_report.md.jinja" )
190216
191217 report_content = template .render (
192218 timestamp = datetime .now ().strftime ("%Y-%m-%d %H:%M:%S" ),
193- target = args [' target' ],
194- granularity = args [' granularity' ],
195- build_time = args [' build_time' ],
219+ target = args [" target" ],
220+ granularity = args [" granularity" ],
221+ build_time = args [" build_time" ],
196222 total_events = total_events ,
197- total_instantiations = data [' total_inst' ],
198- unique_families = data [' unique_families' ],
199- total_trace_time = data [' total_trace_time' ],
200- total_template_time = data [' total_template_time' ],
201- phases = data [' sorted_phases' ],
202- top_individual = data [' top_individual' ],
203- templates_by_time = data [' templates_by_time' ],
204- templates_by_count = data [' templates_by_count' ],
205- median_count = data [' median_count' ],
206- top10_pct = data [' top10_pct' ]
223+ total_instantiations = data [" total_inst" ],
224+ unique_families = data [" unique_families" ],
225+ total_trace_time = data [" total_trace_time" ],
226+ total_template_time = data [" total_template_time" ],
227+ phases = data [" sorted_phases" ],
228+ top_individual = data [" top_individual" ],
229+ templates_by_time = data [" templates_by_time" ],
230+ templates_by_count = data [" templates_by_count" ],
231+ median_count = data [" median_count" ],
232+ top10_pct = data [" top10_pct" ],
207233 )
208234
209235 return report_content
@@ -214,8 +240,8 @@ def main():
214240 args = parse_arguments ()
215241
216242 # Load trace data
217- trace_data = load_trace_data (args [' trace_file' ])
218- total_events = len (trace_data .get (' traceEvents' , []))
243+ trace_data = load_trace_data (args [" trace_file" ])
244+ total_events = len (trace_data .get (" traceEvents" , []))
219245
220246 # Process events
221247 template_stats , phase_stats , top_individual = process_events (trace_data )
@@ -224,18 +250,18 @@ def main():
224250 data = prepare_template_data (template_stats , phase_stats , top_individual )
225251
226252 # Setup Jinja2 environment
227- env = setup_jinja_environment (args [' template_dir' ])
253+ env = setup_jinja_environment (args [" template_dir" ])
228254
229255 # Generate report
230256 report_content = generate_report (env , data , args , total_events )
231257
232258 # Write output
233- with open (args [' output_file' ], 'w' ) as f :
259+ with open (args [" output_file" ], "w" ) as f :
234260 f .write (report_content )
235261
236- print (f' Report generated: { args [" output_file" ] } ' )
237- print (f' Report size: { len (report_content )} bytes' )
262+ print (f" Report generated: { args [' output_file' ] } " )
263+ print (f" Report size: { len (report_content )} bytes" )
238264
239265
240- if __name__ == ' __main__' :
266+ if __name__ == " __main__" :
241267 main ()
0 commit comments