summaryrefslogtreecommitdiff
path: root/ArmPlatformPkg/Scripts/Ds5/profile.py
blob: 2e55993c608b9fda92c70f3aab4d6b3989c058e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/usr/bin/python

#
#  Copyright (c) 2014, ARM Limited. All rights reserved.
#
#  This program and the accompanying materials
#  are licensed and made available under the terms and conditions of the BSD License
#  which accompanies this distribution.  The full text of the license may be found at
#  http://opensource.org/licenses/bsd-license.php
#
#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#

import getopt
import operator
import os
import pickle
import sys
from sys import argv
from cStringIO import StringIO

modules = {}
functions = {}
functions_addr = {}

def usage():
	print "-t,--trace: Location of the Trace file"
	print "-s,--symbols: Location of the symbols and modules"

def get_address_from_string(address):
	return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16)

def get_module_from_addr(modules, addr):
	for key,value in modules.items():
		if (value['start'] <= addr) and (addr <= value['end']):
			return key
	return None

def add_cycles_to_function(functions, func_name, addr, cycles):
	if func_name != "<Unknown>":
		# Check if we are still in the previous function
		if add_cycles_to_function.prev_func_name == func_name:
			add_cycles_to_function.prev_entry['cycles'] += cycles
			return (add_cycles_to_function.prev_func_name, add_cycles_to_function.prev_module_name)

		if func_name in functions.keys():
			for module_name, module_value in functions[func_name].iteritems():
				if (module_value['start'] <= addr) and (addr < module_value['end']):
					module_value['cycles'] += cycles

					add_cycles_to_function.prev_func_name   = func_name
					add_cycles_to_function.prev_module_name = module_name
					add_cycles_to_function.prev_entry       = module_value
					return (func_name, module_name)
				elif (module_value['end'] == 0):
					module_value['cycles'] += cycles

					add_cycles_to_function.prev_func_name   = func_name
					add_cycles_to_function.prev_module_name = module_name
					add_cycles_to_function.prev_entry       = module_value
					return (func_name, module_name)

		# Workaround to fix the 'info func' limitation that does not expose the 'static' function
		module_name = get_module_from_addr(modules, addr)
		functions[func_name] = {}
		functions[func_name][module_name] = {}
		functions[func_name][module_name]['start']  = 0
		functions[func_name][module_name]['end']    = 0
		functions[func_name][module_name]['cycles'] = cycles
		functions[func_name][module_name]['count']  = 0

		add_cycles_to_function.prev_func_name   = func_name
		add_cycles_to_function.prev_module_name = module_name
		add_cycles_to_function.prev_entry       = functions[func_name][module_name]
		return (func_name, module_name)
	else:
		# Check if we are still in the previous function
		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']):
			add_cycles_to_function.prev_entry['cycles'] += cycles
			return (add_cycles_to_function.prev_func_name, add_cycles_to_function.prev_module_name)

		# Generate the key for the given address
		key = addr & ~0x0FFF

		if key not in functions_addr.keys():
			if 'Unknown' not in functions.keys():
				functions['Unknown'] = {}
			if 'Unknown' not in functions['Unknown'].keys():
				functions['Unknown']['Unknown'] = {}
				functions['Unknown']['Unknown']['cycles'] = 0
				functions['Unknown']['Unknown']['count'] = 0
			functions['Unknown']['Unknown']['cycles'] += cycles

			add_cycles_to_function.prev_func_name = None
			return None

		for func_key, module in functions_addr[key].iteritems():
			for module_key, module_value in module.iteritems():
				if (module_value['start'] <= addr) and (addr < module_value['end']):
					module_value['cycles'] += cycles

					# In case o <Unknown> we prefer to fallback on the direct search
					add_cycles_to_function.prev_func_name   = func_key
					add_cycles_to_function.prev_module_name = module_key
					add_cycles_to_function.prev_entry       = module_value
					return (func_key, module_key)

	print "Warning: Function %s @ 0x%x not found" % (func_name, addr)

	add_cycles_to_function.prev_func_name = None
	return None

# Static variables for the previous function
add_cycles_to_function.prev_func_name = None
add_cycles_to_function.prev_entry     = None

def trace_read():
	global trace_process
	line = trace.readline()
	trace_process += len(line)
	return line

#
# Parse arguments
#
trace_name = None
symbols_file = None

opts,args = getopt.getopt(sys.argv[1:], "ht:vs:v", ["help","trace=","symbols="])
if (opts is None) or (not opts):
	usage()
	sys.exit()

for o,a in opts:
    if o in ("-h","--help"):
        usage()
        sys.exit()
    elif o in ("-t","--trace"):
        trace_name = a
    elif o in ("-s","--symbols"):
        symbols_file = a
    else:
        assert False, "Unhandled option (%s)" % o

#
# We try first to see if we run the script from DS-5
#
try:
	from arm_ds.debugger_v1 import Debugger
	from arm_ds.debugger_v1 import DebugException

	# Debugger object for accessing the debugger
	debugger = Debugger()

	# Initialisation commands
	ec = debugger.getExecutionContext(0)
	ec.getExecutionService().stop()
	ec.getExecutionService().waitForStop()
	# in case the execution context reference is out of date
	ec = debugger.getExecutionContext(0)

	#
	# Get the module name and their memory range
	#
	info_file = ec.executeDSCommand("info file")
	info_file_str = StringIO(info_file)

	line = info_file_str.readline().strip('\n')
	while line != '':
		if ("Symbols from" in line):
			# Get the module name from the line 'Symbols from "/home/...."'
			module_name = line.split("\"")[1].split("/")[-1]
			modules[module_name] = {}

			# Look for the text section
			line = info_file_str.readline().strip('\n')
			while (line != '') and ("Symbols from" not in line):
				if ("ER_RO" in line):
					modules[module_name]['start'] = get_address_from_string(line.split()[0])
					modules[module_name]['end']   = get_address_from_string(line.split()[2])
					line = info_file_str.readline().strip('\n')
					break;
				if (".text" in line):
					modules[module_name]['start'] = get_address_from_string(line.split()[0])
					modules[module_name]['end']   = get_address_from_string(line.split()[2])
					line = info_file_str.readline().strip('\n')
					break;
				line = info_file_str.readline().strip('\n')
		line = info_file_str.readline().strip('\n')

	#
	# Get the function name and their memory range
	#
	info_func = ec.executeDSCommand("info func")
	info_func_str = StringIO(info_func)

	# Skip the first line 'Low-level symbols ...'
	line = info_func_str.readline().strip('\n')
	func_prev = None
	while line != '':
		# We ignore all the functions after 'Functions in'
		if ("Functions in " in line):
			line = info_func_str.readline().strip('\n')
			while line != '':
				line = info_func_str.readline().strip('\n')
			line = info_func_str.readline().strip('\n')
			continue

		if ("Low-level symbols" in line):
			# We need to fixup the last function of the module
			if func_prev is not None:
				func_prev['end'] = modules[module_name]['end']
				func_prev = None

			line = info_func_str.readline().strip('\n')
			continue

		func_name = line.split()[1]
		func_start = get_address_from_string(line.split()[0])
		module_name = get_module_from_addr(modules, func_start)

		if func_name not in functions.keys():
			functions[func_name] = {}
		functions[func_name][module_name] = {}
		functions[func_name][module_name]['start'] = func_start
		functions[func_name][module_name]['cycles'] = 0
		functions[func_name][module_name]['count'] = 0

		# Set the end address of the previous function
		if func_prev is not None:
			func_prev['end'] = func_start
		func_prev = functions[func_name][module_name]

		line = info_func_str.readline().strip('\n')

	# Fixup the last function
	func_prev['end'] = modules[module_name]['end']

	if symbols_file is not None:
		pickle.dump((modules, functions), open(symbols_file, "w"))
except:
	if symbols_file is None:
		print "Error: Symbols file is required when run out of ARM DS-5"
		sys.exit()

	(modules, functions) = pickle.load(open(symbols_file, "r"))

#
# Build optimized table for the <Unknown> functions
#
functions_addr = {}
for func_key, module in functions.iteritems():
	for module_key, module_value in module.iteritems():
		key = module_value['start'] & ~0x0FFF
		if key not in functions_addr.keys():
			functions_addr[key] = {}
		if func_key not in functions_addr[key].keys():
			functions_addr[key][func_key] = {}
		functions_addr[key][func_key][module_key] = module_value

#
# Process the trace file
#
if trace_name is None:
	sys.exit()

trace = open(trace_name, "r")
trace_size = os.path.getsize(trace_name)
trace_process = 0

# Get the column names from the first line
columns = trace_read().split()
column_addr     = columns.index('Address')
column_cycles   = columns.index('Cycles')
column_function = columns.index('Function')

line = trace_read()
i = 0
prev_callee = None
while line:
	try:
		func_name = line.split('\t')[column_function].strip()
		address   = get_address_from_string(line.split('\t')[column_addr])
		cycles    = int(line.split('\t')[column_cycles])
		callee = add_cycles_to_function(functions, func_name, address, cycles)
		if (prev_callee != None) and (prev_callee != callee):
			functions[prev_callee[0]][prev_callee[1]]['count'] += 1
		prev_callee = callee
	except ValueError:
		pass
	line = trace_read()
	if ((i % 1000000) == 0) and (i != 0):
		percent = (trace_process * 100.00) / trace_size
		print "Processing file ... (%.2f %%)" % (percent)
	i = i + 1

# Fixup the last callee
functions[prev_callee[0]][prev_callee[1]]['count'] += 1

#
# Process results
#
functions_cycles     = {}
all_functions_cycles = {}
total_cycles         = 0

for func_key, module in functions.iteritems():
	for module_key, module_value in module.iteritems():
		key = "%s/%s" % (module_key, func_key)
		functions_cycles[key] = (module_value['cycles'], module_value['count'])
		total_cycles += module_value['cycles']

		if func_key not in all_functions_cycles.keys():
			all_functions_cycles[func_key] = (module_value['cycles'], module_value['count'])
		else:
			all_functions_cycles[func_key] = tuple(map(sum, zip(all_functions_cycles[func_key], (module_value['cycles'], module_value['count']))))

sorted_functions_cycles     = sorted(functions_cycles.iteritems(), key=operator.itemgetter(1), reverse = True)
sorted_all_functions_cycles = sorted(all_functions_cycles.items(), key=operator.itemgetter(1), reverse = True)

print
print "----"
for (key,value) in sorted_functions_cycles[:20]:
	if value[0] != 0:
		print "%s (cycles: %d - %d%%, count: %d)" % (key, value[0], (value[0] * 100) / total_cycles, value[1])
	else:
		break;
print "----"
for (key,value) in sorted_all_functions_cycles[:20]: 
	if value[0] != 0:
		print "%s (cycles: %d - %d%%, count: %d)" % (key, value[0], (value[0] * 100) / total_cycles, value[1])
	else:
		break;