• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
31def get_address_from_string(address):
32	return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16)
33
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):
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])
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 != '':
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
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
218
219		func_name = line.split()[1]
220		func_start = get_address_from_string(line.split()[0])
221		module_name = get_module_from_addr(modules, func_start)
222
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()
284		address   = get_address_from_string(line.split('\t')[column_addr])
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;
335