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