#!/usr/bin/python # # Copyright (c) 2014, ARM Limited. All rights reserved. # # SPDX-License-Identifier: BSD-2-Clause-Patent # 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 != "": # 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 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 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;