]>
Commit | Line | Data |
---|---|---|
8b7f930a OM |
1 | #!/usr/bin/python |
2 | ||
3 | # | |
4 | # Copyright (c) 2014, ARM Limited. All rights reserved. | |
5 | # | |
f4dfad05 | 6 | # SPDX-License-Identifier: BSD-2-Clause-Patent |
8b7f930a OM |
7 | # |
8 | ||
9 | import getopt | |
10 | import operator | |
11 | import os | |
12 | import pickle | |
13 | import sys | |
14 | from sys import argv | |
15 | from cStringIO import StringIO | |
16 | ||
17 | modules = {} | |
18 | functions = {} | |
19 | functions_addr = {} | |
20 | ||
21 | def usage(): | |
22 | print "-t,--trace: Location of the Trace file" | |
23 | print "-s,--symbols: Location of the symbols and modules" | |
24 | ||
743a2a55 OM |
25 | def get_address_from_string(address): |
26 | return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16) | |
27 | ||
8b7f930a OM |
28 | def get_module_from_addr(modules, addr): |
29 | for key,value in modules.items(): | |
30 | if (value['start'] <= addr) and (addr <= value['end']): | |
31 | return key | |
32 | return None | |
33 | ||
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) | |
40 | ||
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 | |
45 | ||
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 | |
52 | ||
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) | |
57 | ||
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 | |
66 | ||
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) | |
71 | else: | |
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) | |
76 | ||
77 | # Generate the key for the given address | |
78 | key = addr & ~0x0FFF | |
79 | ||
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 | |
88 | ||
89 | add_cycles_to_function.prev_func_name = None | |
90 | return None | |
91 | ||
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 | |
96 | ||
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) | |
102 | ||
103 | print "Warning: Function %s @ 0x%x not found" % (func_name, addr) | |
104 | ||
105 | add_cycles_to_function.prev_func_name = None | |
106 | return None | |
107 | ||
108 | # Static variables for the previous function | |
109 | add_cycles_to_function.prev_func_name = None | |
110 | add_cycles_to_function.prev_entry = None | |
111 | ||
112 | def trace_read(): | |
113 | global trace_process | |
114 | line = trace.readline() | |
115 | trace_process += len(line) | |
116 | return line | |
117 | ||
118 | # | |
119 | # Parse arguments | |
120 | # | |
121 | trace_name = None | |
122 | symbols_file = None | |
123 | ||
124 | opts,args = getopt.getopt(sys.argv[1:], "ht:vs:v", ["help","trace=","symbols="]) | |
125 | if (opts is None) or (not opts): | |
126 | usage() | |
127 | sys.exit() | |
128 | ||
129 | for o,a in opts: | |
130 | if o in ("-h","--help"): | |
131 | usage() | |
132 | sys.exit() | |
133 | elif o in ("-t","--trace"): | |
134 | trace_name = a | |
135 | elif o in ("-s","--symbols"): | |
136 | symbols_file = a | |
137 | else: | |
138 | assert False, "Unhandled option (%s)" % o | |
139 | ||
140 | # | |
141 | # We try first to see if we run the script from DS-5 | |
142 | # | |
143 | try: | |
144 | from arm_ds.debugger_v1 import Debugger | |
145 | from arm_ds.debugger_v1 import DebugException | |
146 | ||
147 | # Debugger object for accessing the debugger | |
148 | debugger = Debugger() | |
149 | ||
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) | |
156 | ||
157 | # | |
158 | # Get the module name and their memory range | |
159 | # | |
160 | info_file = ec.executeDSCommand("info file") | |
161 | info_file_str = StringIO(info_file) | |
162 | ||
163 | line = info_file_str.readline().strip('\n') | |
164 | while line != '': | |
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] = {} | |
169 | ||
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): | |
743a2a55 OM |
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') | |
177 | break; | |
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]) | |
8b7f930a OM |
181 | line = info_file_str.readline().strip('\n') |
182 | break; | |
183 | line = info_file_str.readline().strip('\n') | |
184 | line = info_file_str.readline().strip('\n') | |
185 | ||
186 | # | |
187 | # Get the function name and their memory range | |
188 | # | |
189 | info_func = ec.executeDSCommand("info func") | |
190 | info_func_str = StringIO(info_func) | |
191 | ||
192 | # Skip the first line 'Low-level symbols ...' | |
193 | line = info_func_str.readline().strip('\n') | |
194 | func_prev = None | |
195 | while line != '': | |
743a2a55 OM |
196 | # We ignore all the functions after 'Functions in' |
197 | if ("Functions in " in line): | |
198 | line = info_func_str.readline().strip('\n') | |
199 | while line != '': | |
200 | line = info_func_str.readline().strip('\n') | |
201 | line = info_func_str.readline().strip('\n') | |
202 | continue | |
203 | ||
8b7f930a OM |
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'] | |
208 | func_prev = None | |
209 | ||
210 | line = info_func_str.readline().strip('\n') | |
211 | continue | |
743a2a55 | 212 | |
8b7f930a | 213 | func_name = line.split()[1] |
743a2a55 | 214 | func_start = get_address_from_string(line.split()[0]) |
8b7f930a | 215 | module_name = get_module_from_addr(modules, func_start) |
743a2a55 | 216 | |
8b7f930a OM |
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 | |
223 | ||
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] | |
228 | ||
229 | line = info_func_str.readline().strip('\n') | |
230 | ||
231 | # Fixup the last function | |
232 | func_prev['end'] = modules[module_name]['end'] | |
233 | ||
234 | if symbols_file is not None: | |
235 | pickle.dump((modules, functions), open(symbols_file, "w")) | |
236 | except: | |
237 | if symbols_file is None: | |
238 | print "Error: Symbols file is required when run out of ARM DS-5" | |
239 | sys.exit() | |
240 | ||
241 | (modules, functions) = pickle.load(open(symbols_file, "r")) | |
242 | ||
243 | # | |
244 | # Build optimized table for the <Unknown> functions | |
245 | # | |
246 | functions_addr = {} | |
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 | |
255 | ||
256 | # | |
257 | # Process the trace file | |
258 | # | |
259 | if trace_name is None: | |
260 | sys.exit() | |
261 | ||
262 | trace = open(trace_name, "r") | |
263 | trace_size = os.path.getsize(trace_name) | |
264 | trace_process = 0 | |
265 | ||
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') | |
271 | ||
272 | line = trace_read() | |
273 | i = 0 | |
274 | prev_callee = None | |
275 | while line: | |
276 | try: | |
277 | func_name = line.split('\t')[column_function].strip() | |
743a2a55 | 278 | address = get_address_from_string(line.split('\t')[column_addr]) |
8b7f930a OM |
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 | |
283 | prev_callee = callee | |
284 | except ValueError: | |
285 | pass | |
286 | line = trace_read() | |
287 | if ((i % 1000000) == 0) and (i != 0): | |
288 | percent = (trace_process * 100.00) / trace_size | |
289 | print "Processing file ... (%.2f %%)" % (percent) | |
290 | i = i + 1 | |
291 | ||
292 | # Fixup the last callee | |
293 | functions[prev_callee[0]][prev_callee[1]]['count'] += 1 | |
294 | ||
295 | # | |
296 | # Process results | |
297 | # | |
298 | functions_cycles = {} | |
299 | all_functions_cycles = {} | |
300 | total_cycles = 0 | |
301 | ||
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'] | |
307 | ||
308 | if func_key not in all_functions_cycles.keys(): | |
309 | all_functions_cycles[func_key] = (module_value['cycles'], module_value['count']) | |
310 | else: | |
311 | all_functions_cycles[func_key] = tuple(map(sum, zip(all_functions_cycles[func_key], (module_value['cycles'], module_value['count'])))) | |
312 | ||
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) | |
315 | ||
316 | ||
317 | print "----" | |
318 | for (key,value) in sorted_functions_cycles[:20]: | |
319 | if value[0] != 0: | |
320 | print "%s (cycles: %d - %d%%, count: %d)" % (key, value[0], (value[0] * 100) / total_cycles, value[1]) | |
321 | else: | |
322 | break; | |
323 | print "----" | |
df8f8688 | 324 | for (key,value) in sorted_all_functions_cycles[:20]: |
8b7f930a OM |
325 | if value[0] != 0: |
326 | print "%s (cycles: %d - %d%%, count: %d)" % (key, value[0], (value[0] * 100) / total_cycles, value[1]) | |
327 | else: | |
328 | break; |