]>
Commit | Line | Data |
---|---|---|
8b7f930a OM |
1 | #!/usr/bin/python |
2 | ||
3 | # | |
4 | # Copyright (c) 2014, ARM Limited. All rights reserved. | |
5 | # | |
6 | # This program and the accompanying materials | |
7 | # are licensed and made available under the terms and conditions of the BSD License | |
8 | # which accompanies this distribution. The full text of the license may be found at | |
9 | # http://opensource.org/licenses/bsd-license.php | |
10 | # | |
11 | # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
12 | # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
13 | # | |
14 | ||
15 | import getopt | |
16 | import operator | |
17 | import os | |
18 | import pickle | |
19 | import sys | |
20 | from sys import argv | |
21 | from cStringIO import StringIO | |
22 | ||
23 | modules = {} | |
24 | functions = {} | |
25 | functions_addr = {} | |
26 | ||
27 | def usage(): | |
28 | print "-t,--trace: Location of the Trace file" | |
29 | print "-s,--symbols: Location of the symbols and modules" | |
30 | ||
743a2a55 OM |
31 | def get_address_from_string(address): |
32 | return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16) | |
33 | ||
8b7f930a OM |
34 | def get_module_from_addr(modules, addr): |
35 | for key,value in modules.items(): | |
36 | if (value['start'] <= addr) and (addr <= value['end']): | |
37 | return key | |
38 | return None | |
39 | ||
40 | def add_cycles_to_function(functions, func_name, addr, cycles): | |
41 | if func_name != "<Unknown>": | |
42 | # Check if we are still in the previous function | |
43 | if add_cycles_to_function.prev_func_name == func_name: | |
44 | add_cycles_to_function.prev_entry['cycles'] += cycles | |
45 | return (add_cycles_to_function.prev_func_name, add_cycles_to_function.prev_module_name) | |
46 | ||
47 | if func_name in functions.keys(): | |
48 | for module_name, module_value in functions[func_name].iteritems(): | |
49 | if (module_value['start'] <= addr) and (addr < module_value['end']): | |
50 | module_value['cycles'] += cycles | |
51 | ||
52 | add_cycles_to_function.prev_func_name = func_name | |
53 | add_cycles_to_function.prev_module_name = module_name | |
54 | add_cycles_to_function.prev_entry = module_value | |
55 | return (func_name, module_name) | |
56 | elif (module_value['end'] == 0): | |
57 | module_value['cycles'] += cycles | |
58 | ||
59 | add_cycles_to_function.prev_func_name = func_name | |
60 | add_cycles_to_function.prev_module_name = module_name | |
61 | add_cycles_to_function.prev_entry = module_value | |
62 | return (func_name, module_name) | |
63 | ||
64 | # Workaround to fix the 'info func' limitation that does not expose the 'static' function | |
65 | module_name = get_module_from_addr(modules, addr) | |
66 | functions[func_name] = {} | |
67 | functions[func_name][module_name] = {} | |
68 | functions[func_name][module_name]['start'] = 0 | |
69 | functions[func_name][module_name]['end'] = 0 | |
70 | functions[func_name][module_name]['cycles'] = cycles | |
71 | functions[func_name][module_name]['count'] = 0 | |
72 | ||
73 | add_cycles_to_function.prev_func_name = func_name | |
74 | add_cycles_to_function.prev_module_name = module_name | |
75 | add_cycles_to_function.prev_entry = functions[func_name][module_name] | |
76 | return (func_name, module_name) | |
77 | else: | |
78 | # Check if we are still in the previous function | |
79 | 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']): | |
80 | add_cycles_to_function.prev_entry['cycles'] += cycles | |
81 | return (add_cycles_to_function.prev_func_name, add_cycles_to_function.prev_module_name) | |
82 | ||
83 | # Generate the key for the given address | |
84 | key = addr & ~0x0FFF | |
85 | ||
86 | if key not in functions_addr.keys(): | |
87 | if 'Unknown' not in functions.keys(): | |
88 | functions['Unknown'] = {} | |
89 | if 'Unknown' not in functions['Unknown'].keys(): | |
90 | functions['Unknown']['Unknown'] = {} | |
91 | functions['Unknown']['Unknown']['cycles'] = 0 | |
92 | functions['Unknown']['Unknown']['count'] = 0 | |
93 | functions['Unknown']['Unknown']['cycles'] += cycles | |
94 | ||
95 | add_cycles_to_function.prev_func_name = None | |
96 | return None | |
97 | ||
98 | for func_key, module in functions_addr[key].iteritems(): | |
99 | for module_key, module_value in module.iteritems(): | |
100 | if (module_value['start'] <= addr) and (addr < module_value['end']): | |
101 | module_value['cycles'] += cycles | |
102 | ||
103 | # In case o <Unknown> we prefer to fallback on the direct search | |
104 | add_cycles_to_function.prev_func_name = func_key | |
105 | add_cycles_to_function.prev_module_name = module_key | |
106 | add_cycles_to_function.prev_entry = module_value | |
107 | return (func_key, module_key) | |
108 | ||
109 | print "Warning: Function %s @ 0x%x not found" % (func_name, addr) | |
110 | ||
111 | add_cycles_to_function.prev_func_name = None | |
112 | return None | |
113 | ||
114 | # Static variables for the previous function | |
115 | add_cycles_to_function.prev_func_name = None | |
116 | add_cycles_to_function.prev_entry = None | |
117 | ||
118 | def trace_read(): | |
119 | global trace_process | |
120 | line = trace.readline() | |
121 | trace_process += len(line) | |
122 | return line | |
123 | ||
124 | # | |
125 | # Parse arguments | |
126 | # | |
127 | trace_name = None | |
128 | symbols_file = None | |
129 | ||
130 | opts,args = getopt.getopt(sys.argv[1:], "ht:vs:v", ["help","trace=","symbols="]) | |
131 | if (opts is None) or (not opts): | |
132 | usage() | |
133 | sys.exit() | |
134 | ||
135 | for o,a in opts: | |
136 | if o in ("-h","--help"): | |
137 | usage() | |
138 | sys.exit() | |
139 | elif o in ("-t","--trace"): | |
140 | trace_name = a | |
141 | elif o in ("-s","--symbols"): | |
142 | symbols_file = a | |
143 | else: | |
144 | assert False, "Unhandled option (%s)" % o | |
145 | ||
146 | # | |
147 | # We try first to see if we run the script from DS-5 | |
148 | # | |
149 | try: | |
150 | from arm_ds.debugger_v1 import Debugger | |
151 | from arm_ds.debugger_v1 import DebugException | |
152 | ||
153 | # Debugger object for accessing the debugger | |
154 | debugger = Debugger() | |
155 | ||
156 | # Initialisation commands | |
157 | ec = debugger.getExecutionContext(0) | |
158 | ec.getExecutionService().stop() | |
159 | ec.getExecutionService().waitForStop() | |
160 | # in case the execution context reference is out of date | |
161 | ec = debugger.getExecutionContext(0) | |
162 | ||
163 | # | |
164 | # Get the module name and their memory range | |
165 | # | |
166 | info_file = ec.executeDSCommand("info file") | |
167 | info_file_str = StringIO(info_file) | |
168 | ||
169 | line = info_file_str.readline().strip('\n') | |
170 | while line != '': | |
171 | if ("Symbols from" in line): | |
172 | # Get the module name from the line 'Symbols from "/home/...."' | |
173 | module_name = line.split("\"")[1].split("/")[-1] | |
174 | modules[module_name] = {} | |
175 | ||
176 | # Look for the text section | |
177 | line = info_file_str.readline().strip('\n') | |
178 | while (line != '') and ("Symbols from" not in line): | |
179 | if ("ER_RO" in line): | |
743a2a55 OM |
180 | modules[module_name]['start'] = get_address_from_string(line.split()[0]) |
181 | modules[module_name]['end'] = get_address_from_string(line.split()[2]) | |
182 | line = info_file_str.readline().strip('\n') | |
183 | break; | |
184 | if (".text" in line): | |
185 | modules[module_name]['start'] = get_address_from_string(line.split()[0]) | |
186 | modules[module_name]['end'] = get_address_from_string(line.split()[2]) | |
8b7f930a OM |
187 | line = info_file_str.readline().strip('\n') |
188 | break; | |
189 | line = info_file_str.readline().strip('\n') | |
190 | line = info_file_str.readline().strip('\n') | |
191 | ||
192 | # | |
193 | # Get the function name and their memory range | |
194 | # | |
195 | info_func = ec.executeDSCommand("info func") | |
196 | info_func_str = StringIO(info_func) | |
197 | ||
198 | # Skip the first line 'Low-level symbols ...' | |
199 | line = info_func_str.readline().strip('\n') | |
200 | func_prev = None | |
201 | while line != '': | |
743a2a55 OM |
202 | # We ignore all the functions after 'Functions in' |
203 | if ("Functions in " in line): | |
204 | line = info_func_str.readline().strip('\n') | |
205 | while line != '': | |
206 | line = info_func_str.readline().strip('\n') | |
207 | line = info_func_str.readline().strip('\n') | |
208 | continue | |
209 | ||
8b7f930a OM |
210 | if ("Low-level symbols" in line): |
211 | # We need to fixup the last function of the module | |
212 | if func_prev is not None: | |
213 | func_prev['end'] = modules[module_name]['end'] | |
214 | func_prev = None | |
215 | ||
216 | line = info_func_str.readline().strip('\n') | |
217 | continue | |
743a2a55 | 218 | |
8b7f930a | 219 | func_name = line.split()[1] |
743a2a55 | 220 | func_start = get_address_from_string(line.split()[0]) |
8b7f930a | 221 | module_name = get_module_from_addr(modules, func_start) |
743a2a55 | 222 | |
8b7f930a OM |
223 | if func_name not in functions.keys(): |
224 | functions[func_name] = {} | |
225 | functions[func_name][module_name] = {} | |
226 | functions[func_name][module_name]['start'] = func_start | |
227 | functions[func_name][module_name]['cycles'] = 0 | |
228 | functions[func_name][module_name]['count'] = 0 | |
229 | ||
230 | # Set the end address of the previous function | |
231 | if func_prev is not None: | |
232 | func_prev['end'] = func_start | |
233 | func_prev = functions[func_name][module_name] | |
234 | ||
235 | line = info_func_str.readline().strip('\n') | |
236 | ||
237 | # Fixup the last function | |
238 | func_prev['end'] = modules[module_name]['end'] | |
239 | ||
240 | if symbols_file is not None: | |
241 | pickle.dump((modules, functions), open(symbols_file, "w")) | |
242 | except: | |
243 | if symbols_file is None: | |
244 | print "Error: Symbols file is required when run out of ARM DS-5" | |
245 | sys.exit() | |
246 | ||
247 | (modules, functions) = pickle.load(open(symbols_file, "r")) | |
248 | ||
249 | # | |
250 | # Build optimized table for the <Unknown> functions | |
251 | # | |
252 | functions_addr = {} | |
253 | for func_key, module in functions.iteritems(): | |
254 | for module_key, module_value in module.iteritems(): | |
255 | key = module_value['start'] & ~0x0FFF | |
256 | if key not in functions_addr.keys(): | |
257 | functions_addr[key] = {} | |
258 | if func_key not in functions_addr[key].keys(): | |
259 | functions_addr[key][func_key] = {} | |
260 | functions_addr[key][func_key][module_key] = module_value | |
261 | ||
262 | # | |
263 | # Process the trace file | |
264 | # | |
265 | if trace_name is None: | |
266 | sys.exit() | |
267 | ||
268 | trace = open(trace_name, "r") | |
269 | trace_size = os.path.getsize(trace_name) | |
270 | trace_process = 0 | |
271 | ||
272 | # Get the column names from the first line | |
273 | columns = trace_read().split() | |
274 | column_addr = columns.index('Address') | |
275 | column_cycles = columns.index('Cycles') | |
276 | column_function = columns.index('Function') | |
277 | ||
278 | line = trace_read() | |
279 | i = 0 | |
280 | prev_callee = None | |
281 | while line: | |
282 | try: | |
283 | func_name = line.split('\t')[column_function].strip() | |
743a2a55 | 284 | address = get_address_from_string(line.split('\t')[column_addr]) |
8b7f930a OM |
285 | cycles = int(line.split('\t')[column_cycles]) |
286 | callee = add_cycles_to_function(functions, func_name, address, cycles) | |
287 | if (prev_callee != None) and (prev_callee != callee): | |
288 | functions[prev_callee[0]][prev_callee[1]]['count'] += 1 | |
289 | prev_callee = callee | |
290 | except ValueError: | |
291 | pass | |
292 | line = trace_read() | |
293 | if ((i % 1000000) == 0) and (i != 0): | |
294 | percent = (trace_process * 100.00) / trace_size | |
295 | print "Processing file ... (%.2f %%)" % (percent) | |
296 | i = i + 1 | |
297 | ||
298 | # Fixup the last callee | |
299 | functions[prev_callee[0]][prev_callee[1]]['count'] += 1 | |
300 | ||
301 | # | |
302 | # Process results | |
303 | # | |
304 | functions_cycles = {} | |
305 | all_functions_cycles = {} | |
306 | total_cycles = 0 | |
307 | ||
308 | for func_key, module in functions.iteritems(): | |
309 | for module_key, module_value in module.iteritems(): | |
310 | key = "%s/%s" % (module_key, func_key) | |
311 | functions_cycles[key] = (module_value['cycles'], module_value['count']) | |
312 | total_cycles += module_value['cycles'] | |
313 | ||
314 | if func_key not in all_functions_cycles.keys(): | |
315 | all_functions_cycles[func_key] = (module_value['cycles'], module_value['count']) | |
316 | else: | |
317 | all_functions_cycles[func_key] = tuple(map(sum, zip(all_functions_cycles[func_key], (module_value['cycles'], module_value['count'])))) | |
318 | ||
319 | sorted_functions_cycles = sorted(functions_cycles.iteritems(), key=operator.itemgetter(1), reverse = True) | |
320 | sorted_all_functions_cycles = sorted(all_functions_cycles.items(), key=operator.itemgetter(1), reverse = True) | |
321 | ||
322 | ||
323 | print "----" | |
324 | for (key,value) in sorted_functions_cycles[:20]: | |
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; | |
329 | print "----" | |
330 | for (key,value) in sorted_all_functions_cycles[:20]: | |
331 | if value[0] != 0: | |
332 | print "%s (cycles: %d - %d%%, count: %d)" % (key, value[0], (value[0] * 100) / total_cycles, value[1]) | |
333 | else: | |
334 | break; |