Home | History | Annotate | Download | only in Ds5
      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 
     31 def get_address_from_string(address):
     32 	return int(address.strip("S:").strip("N:").strip("EL2:").strip("EL1:"), 16)
     33 
     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):
    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"))
    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()
    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
    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 print
    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;
    335