]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPlatformPkg/Ds5: Added script to profile EDK2 with ARM DSTREAM
authorOlivier Martin <olivier.martin@arm.com>
Tue, 3 Jun 2014 16:35:32 +0000 (16:35 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Tue, 3 Jun 2014 16:35:32 +0000 (16:35 +0000)
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15550 6f19259b-4bc3-4df7-8a09-765794883524

ArmPlatformPkg/Scripts/Ds5/profile.py [new file with mode: 0644]

diff --git a/ArmPlatformPkg/Scripts/Ds5/profile.py b/ArmPlatformPkg/Scripts/Ds5/profile.py
new file mode 100644 (file)
index 0000000..c595dac
--- /dev/null
@@ -0,0 +1,316 @@
+#!/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_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'] = int(line.split()[0].strip("S:"), 16)
+                                       modules[module_name]['end']   = int(line.split()[2].strip("S:"), 16)
+                                       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 != '':
+               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 = int(line.split()[0].strip("S:"), 16)
+               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   = int(line.split('\t')[column_addr].strip("S:"), 16)
+               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;