]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Scripts/Ds5/profile.py
ArmPlatformPkg/Scripts: Update the profiling script to work on AArch64 with the lates...
[mirror_edk2.git] / ArmPlatformPkg / Scripts / Ds5 / profile.py
CommitLineData
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
15import getopt
16import operator
17import os
18import pickle
19import sys
20from sys import argv
21from cStringIO import StringIO
22
23modules = {}
24functions = {}
25functions_addr = {}
26
27def usage():
28 print "-t,--trace: Location of the Trace file"
29 print "-s,--symbols: Location of the symbols and modules"
30
743a2a55
OM
31def get_address_from_string(address):
32 return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16)
33
8b7f930a
OM
34def 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
40def 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
115add_cycles_to_function.prev_func_name = None
116add_cycles_to_function.prev_entry = None
117
118def trace_read():
119 global trace_process
120 line = trace.readline()
121 trace_process += len(line)
122 return line
123
124#
125# Parse arguments
126#
127trace_name = None
128symbols_file = None
129
130opts,args = getopt.getopt(sys.argv[1:], "ht:vs:v", ["help","trace=","symbols="])
131if (opts is None) or (not opts):
132 usage()
133 sys.exit()
134
135for 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#
149try:
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"))
242except:
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#
252functions_addr = {}
253for 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#
265if trace_name is None:
266 sys.exit()
267
268trace = open(trace_name, "r")
269trace_size = os.path.getsize(trace_name)
270trace_process = 0
271
272# Get the column names from the first line
273columns = trace_read().split()
274column_addr = columns.index('Address')
275column_cycles = columns.index('Cycles')
276column_function = columns.index('Function')
277
278line = trace_read()
279i = 0
280prev_callee = None
281while 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
299functions[prev_callee[0]][prev_callee[1]]['count'] += 1
300
301#
302# Process results
303#
304functions_cycles = {}
305all_functions_cycles = {}
306total_cycles = 0
307
308for 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
319sorted_functions_cycles = sorted(functions_cycles.iteritems(), key=operator.itemgetter(1), reverse = True)
320sorted_all_functions_cycles = sorted(all_functions_cycles.items(), key=operator.itemgetter(1), reverse = True)
321
322print
323print "----"
324for (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;
329print "----"
330for (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;