4 # Copyright (c) 2014, ARM Limited. All rights reserved.
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
15 from cStringIO
import StringIO
22 print "-t,--trace: Location of the Trace file"
23 print "-s,--symbols: Location of the symbols and modules"
25 def get_address_from_string(address
):
26 return int(address
.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16)
28 def get_module_from_addr(modules
, addr
):
29 for key
,value
in modules
.items():
30 if (value
['start'] <= addr
) and (addr
<= value
['end']):
34 def add_cycles_to_function(functions
, func_name
, addr
, cycles
):
35 if func_name
!= "<Unknown>":
36 # Check if we are still in the previous function
37 if add_cycles_to_function
.prev_func_name
== func_name
:
38 add_cycles_to_function
.prev_entry
['cycles'] += cycles
39 return (add_cycles_to_function
.prev_func_name
, add_cycles_to_function
.prev_module_name
)
41 if func_name
in functions
.keys():
42 for module_name
, module_value
in functions
[func_name
].iteritems():
43 if (module_value
['start'] <= addr
) and (addr
< module_value
['end']):
44 module_value
['cycles'] += cycles
46 add_cycles_to_function
.prev_func_name
= func_name
47 add_cycles_to_function
.prev_module_name
= module_name
48 add_cycles_to_function
.prev_entry
= module_value
49 return (func_name
, module_name
)
50 elif (module_value
['end'] == 0):
51 module_value
['cycles'] += cycles
53 add_cycles_to_function
.prev_func_name
= func_name
54 add_cycles_to_function
.prev_module_name
= module_name
55 add_cycles_to_function
.prev_entry
= module_value
56 return (func_name
, module_name
)
58 # Workaround to fix the 'info func' limitation that does not expose the 'static' function
59 module_name
= get_module_from_addr(modules
, addr
)
60 functions
[func_name
] = {}
61 functions
[func_name
][module_name
] = {}
62 functions
[func_name
][module_name
]['start'] = 0
63 functions
[func_name
][module_name
]['end'] = 0
64 functions
[func_name
][module_name
]['cycles'] = cycles
65 functions
[func_name
][module_name
]['count'] = 0
67 add_cycles_to_function
.prev_func_name
= func_name
68 add_cycles_to_function
.prev_module_name
= module_name
69 add_cycles_to_function
.prev_entry
= functions
[func_name
][module_name
]
70 return (func_name
, module_name
)
72 # Check if we are still in the previous function
73 if (add_cycles_to_function
.prev_entry
is not None) and (add_cycles_to_function
.prev_entry
['start'] <= addr
) and (addr
< add_cycles_to_function
.prev_entry
['end']):
74 add_cycles_to_function
.prev_entry
['cycles'] += cycles
75 return (add_cycles_to_function
.prev_func_name
, add_cycles_to_function
.prev_module_name
)
77 # Generate the key for the given address
80 if key
not in functions_addr
.keys():
81 if 'Unknown' not in functions
.keys():
82 functions
['Unknown'] = {}
83 if 'Unknown' not in functions
['Unknown'].keys():
84 functions
['Unknown']['Unknown'] = {}
85 functions
['Unknown']['Unknown']['cycles'] = 0
86 functions
['Unknown']['Unknown']['count'] = 0
87 functions
['Unknown']['Unknown']['cycles'] += cycles
89 add_cycles_to_function
.prev_func_name
= None
92 for func_key
, module
in functions_addr
[key
].iteritems():
93 for module_key
, module_value
in module
.iteritems():
94 if (module_value
['start'] <= addr
) and (addr
< module_value
['end']):
95 module_value
['cycles'] += cycles
97 # In case o <Unknown> we prefer to fallback on the direct search
98 add_cycles_to_function
.prev_func_name
= func_key
99 add_cycles_to_function
.prev_module_name
= module_key
100 add_cycles_to_function
.prev_entry
= module_value
101 return (func_key
, module_key
)
103 print "Warning: Function %s @ 0x%x not found" % (func_name
, addr
)
105 add_cycles_to_function
.prev_func_name
= None
108 # Static variables for the previous function
109 add_cycles_to_function
.prev_func_name
= None
110 add_cycles_to_function
.prev_entry
= None
114 line
= trace
.readline()
115 trace_process
+= len(line
)
124 opts
,args
= getopt
.getopt(sys
.argv
[1:], "ht:vs:v", ["help","trace=","symbols="])
125 if (opts
is None) or (not opts
):
130 if o
in ("-h","--help"):
133 elif o
in ("-t","--trace"):
135 elif o
in ("-s","--symbols"):
138 assert False, "Unhandled option (%s)" % o
141 # We try first to see if we run the script from DS-5
144 from arm_ds
.debugger_v1
import Debugger
145 from arm_ds
.debugger_v1
import DebugException
147 # Debugger object for accessing the debugger
148 debugger
= Debugger()
150 # Initialisation commands
151 ec
= debugger
.getExecutionContext(0)
152 ec
.getExecutionService().stop()
153 ec
.getExecutionService().waitForStop()
154 # in case the execution context reference is out of date
155 ec
= debugger
.getExecutionContext(0)
158 # Get the module name and their memory range
160 info_file
= ec
.executeDSCommand("info file")
161 info_file_str
= StringIO(info_file
)
163 line
= info_file_str
.readline().strip('\n')
165 if ("Symbols from" in line
):
166 # Get the module name from the line 'Symbols from "/home/...."'
167 module_name
= line
.split("\"")[1].split("/")[-1]
168 modules
[module_name
] = {}
170 # Look for the text section
171 line
= info_file_str
.readline().strip('\n')
172 while (line
!= '') and ("Symbols from" not in line
):
173 if ("ER_RO" in line
):
174 modules
[module_name
]['start'] = get_address_from_string(line
.split()[0])
175 modules
[module_name
]['end'] = get_address_from_string(line
.split()[2])
176 line
= info_file_str
.readline().strip('\n')
178 if (".text" in line
):
179 modules
[module_name
]['start'] = get_address_from_string(line
.split()[0])
180 modules
[module_name
]['end'] = get_address_from_string(line
.split()[2])
181 line
= info_file_str
.readline().strip('\n')
183 line
= info_file_str
.readline().strip('\n')
184 line
= info_file_str
.readline().strip('\n')
187 # Get the function name and their memory range
189 info_func
= ec
.executeDSCommand("info func")
190 info_func_str
= StringIO(info_func
)
192 # Skip the first line 'Low-level symbols ...'
193 line
= info_func_str
.readline().strip('\n')
196 # We ignore all the functions after 'Functions in'
197 if ("Functions in " in line
):
198 line
= info_func_str
.readline().strip('\n')
200 line
= info_func_str
.readline().strip('\n')
201 line
= info_func_str
.readline().strip('\n')
204 if ("Low-level symbols" in line
):
205 # We need to fixup the last function of the module
206 if func_prev
is not None:
207 func_prev
['end'] = modules
[module_name
]['end']
210 line
= info_func_str
.readline().strip('\n')
213 func_name
= line
.split()[1]
214 func_start
= get_address_from_string(line
.split()[0])
215 module_name
= get_module_from_addr(modules
, func_start
)
217 if func_name
not in functions
.keys():
218 functions
[func_name
] = {}
219 functions
[func_name
][module_name
] = {}
220 functions
[func_name
][module_name
]['start'] = func_start
221 functions
[func_name
][module_name
]['cycles'] = 0
222 functions
[func_name
][module_name
]['count'] = 0
224 # Set the end address of the previous function
225 if func_prev
is not None:
226 func_prev
['end'] = func_start
227 func_prev
= functions
[func_name
][module_name
]
229 line
= info_func_str
.readline().strip('\n')
231 # Fixup the last function
232 func_prev
['end'] = modules
[module_name
]['end']
234 if symbols_file
is not None:
235 pickle
.dump((modules
, functions
), open(symbols_file
, "w"))
237 if symbols_file
is None:
238 print "Error: Symbols file is required when run out of ARM DS-5"
241 (modules
, functions
) = pickle
.load(open(symbols_file
, "r"))
244 # Build optimized table for the <Unknown> functions
247 for func_key
, module
in functions
.iteritems():
248 for module_key
, module_value
in module
.iteritems():
249 key
= module_value
['start'] & ~
0x0FFF
250 if key
not in functions_addr
.keys():
251 functions_addr
[key
] = {}
252 if func_key
not in functions_addr
[key
].keys():
253 functions_addr
[key
][func_key
] = {}
254 functions_addr
[key
][func_key
][module_key
] = module_value
257 # Process the trace file
259 if trace_name
is None:
262 trace
= open(trace_name
, "r")
263 trace_size
= os
.path
.getsize(trace_name
)
266 # Get the column names from the first line
267 columns
= trace_read().split()
268 column_addr
= columns
.index('Address')
269 column_cycles
= columns
.index('Cycles')
270 column_function
= columns
.index('Function')
277 func_name
= line
.split('\t')[column_function
].strip()
278 address
= get_address_from_string(line
.split('\t')[column_addr
])
279 cycles
= int(line
.split('\t')[column_cycles
])
280 callee
= add_cycles_to_function(functions
, func_name
, address
, cycles
)
281 if (prev_callee
!= None) and (prev_callee
!= callee
):
282 functions
[prev_callee
[0]][prev_callee
[1]]['count'] += 1
287 if ((i
% 1000000) == 0) and (i
!= 0):
288 percent
= (trace_process
* 100.00) / trace_size
289 print "Processing file ... (%.2f %%)" % (percent
)
292 # Fixup the last callee
293 functions
[prev_callee
[0]][prev_callee
[1]]['count'] += 1
298 functions_cycles
= {}
299 all_functions_cycles
= {}
302 for func_key
, module
in functions
.iteritems():
303 for module_key
, module_value
in module
.iteritems():
304 key
= "%s/%s" % (module_key
, func_key
)
305 functions_cycles
[key
] = (module_value
['cycles'], module_value
['count'])
306 total_cycles
+= module_value
['cycles']
308 if func_key
not in all_functions_cycles
.keys():
309 all_functions_cycles
[func_key
] = (module_value
['cycles'], module_value
['count'])
311 all_functions_cycles
[func_key
] = tuple(map(sum, zip(all_functions_cycles
[func_key
], (module_value
['cycles'], module_value
['count']))))
313 sorted_functions_cycles
= sorted(functions_cycles
.iteritems(), key
=operator
.itemgetter(1), reverse
= True)
314 sorted_all_functions_cycles
= sorted(all_functions_cycles
.items(), key
=operator
.itemgetter(1), reverse
= True)
318 for (key
,value
) in sorted_functions_cycles
[:20]:
320 print "%s (cycles: %d - %d%%, count: %d)" % (key
, value
[0], (value
[0] * 100) / total_cycles
, value
[1])
324 for (key
,value
) in sorted_all_functions_cycles
[:20]:
326 print "%s (cycles: %d - %d%%, count: %d)" % (key
, value
[0], (value
[0] * 100) / total_cycles
, value
[1])