| #!/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; |