diff options
Diffstat (limited to 'extra/stack_analyzer/stack_analyzer.py')
-rwxr-xr-x | extra/stack_analyzer/stack_analyzer.py | 81 |
1 files changed, 54 insertions, 27 deletions
diff --git a/extra/stack_analyzer/stack_analyzer.py b/extra/stack_analyzer/stack_analyzer.py index 0461e2983a..df7b7c8932 100755 --- a/extra/stack_analyzer/stack_analyzer.py +++ b/extra/stack_analyzer/stack_analyzer.py @@ -6,17 +6,23 @@ """Statically analyze stack usage of EC firmware. Example: - extra/stack_analyzer/stack_analyzer.py ./build/elm/RW/ec.RW.elf \ - ./build/elm/RW/ec.RW.taskinfo + extra/stack_analyzer/stack_analyzer.py \ + --export_taskinfo ./build/elm/util/export_taskinfo.so \ + --section RW \ + ./build/elm/RW/ec.RW.elf + """ from __future__ import print_function import argparse +import ctypes import re import subprocess +SECTION_RO = 'RO' +SECTION_RW = 'RW' # TODO(cheyuw): This should depend on the CPU and build options. # The size of extra stack frame needed by interrupts. (on cortex-m with FPU) INTERRUPT_EXTRA_STACK_FRAME = 224 @@ -26,6 +32,17 @@ class StackAnalyzerError(Exception): """Exception class for stack analyzer utility.""" +class TaskInfo(ctypes.Structure): + """Taskinfo ctypes structure. + + The structure definition is corresponding to the "struct taskinfo" + in "util/export_taskinfo.so.c". + """ + _fields_ = [('name', ctypes.c_char_p), + ('routine', ctypes.c_char_p), + ('stack_size', ctypes.c_uint32)] + + class Task(object): """Task information. @@ -640,14 +657,14 @@ class StackAnalyzer(object): cycle_groups = self.AnalyzeCallGraph(function_map) # Print the results of task-aware stack analysis. - # TODO(cheyuw): Resolve and show the allocated task size. for task in self.tasklist: routine_func = function_map[task.routine_address] - print('Task: {}, Max size: {} ({} + {})'.format( + print('Task: {}, Max size: {} ({} + {}), Allocated size: {}'.format( task.name, routine_func.stack_max_usage + INTERRUPT_EXTRA_STACK_FRAME, routine_func.stack_max_usage, - INTERRUPT_EXTRA_STACK_FRAME)) + INTERRUPT_EXTRA_STACK_FRAME, + task.stack_max_size)) print('Call Trace:') curr_func = routine_func @@ -673,24 +690,25 @@ def ParseArgs(): """ parser = argparse.ArgumentParser(description="EC firmware stack analyzer.") parser.add_argument('elf_path', help="the path of EC firmware ELF") - parser.add_argument('taskinfo_path', - help="the path of EC taskinfo generated by Makefile") + parser.add_argument('--export_taskinfo', required=True, + help="the path of export_taskinfo.so utility") + parser.add_argument('--section', required=True, help='the section.', + choices=[SECTION_RO, SECTION_RW]) parser.add_argument('--objdump', default='objdump', help='the path of objdump') parser.add_argument('--addr2line', default='addr2line', help='the path of addr2line') - # TODO(cheyuw): Add an option for dumping stack usage of all - # functions. + # TODO(cheyuw): Add an option for dumping stack usage of all functions. return parser.parse_args() -def ParseSymbolFile(symbol_text): - """Parse the content of the symbol file. +def ParseSymbolText(symbol_text): + """Parse the content of the symbol text. Args: - symbol_text: Text of the symbol file. + symbol_text: Text of the symbols. Returns: symbols: Symbol list. @@ -718,21 +736,31 @@ def ParseSymbolFile(symbol_text): return symbols -def ParseTasklistFile(taskinfo_text, symbols): - """Parse the task information generated by Makefile. +def LoadTasklist(section, export_taskinfo, symbols): + """Load the task information. Args: - taskinfo_text: Text of the taskinfo file. + section: Section (RO | RW). + export_taskinfo: Handle of export_taskinfo.so. symbols: Symbol list. Returns: tasklist: Task list. """ - # Example: ("HOOKS",hook_task,LARGER_TASK_STACK_SIZE) ("USB_CHG_P0", ... - results = re.findall(r'\("([^"]+)", ([^,]+), ([^\)]+)\)', taskinfo_text) + + TaskInfoPointer = ctypes.POINTER(TaskInfo) + taskinfos = TaskInfoPointer() + if section == SECTION_RO: + get_taskinfos_func = export_taskinfo.get_ro_taskinfos + else: + get_taskinfos_func = export_taskinfo.get_rw_taskinfos + + taskinfo_num = get_taskinfos_func(ctypes.pointer(taskinfos)) + tasklist = [] - for name, routine_name, stack_max_size in results: - tasklist.append(Task(name, routine_name, stack_max_size)) + for index in range(taskinfo_num): + taskinfo = taskinfos[index] + tasklist.append(Task(taskinfo.name, taskinfo.routine, taskinfo.stack_size)) # Resolve routine address for each task. It's more efficient to resolve all # routine addresses of tasks together. @@ -759,7 +787,7 @@ def main(): try: options = ParseArgs() - # Generate and parse the symbol file. + # Generate and parse the symbols. try: symbol_text = subprocess.check_output([options.objdump, '-t', @@ -769,16 +797,15 @@ def main(): except OSError: raise StackAnalyzerError('Failed to run objdump.') - symbols = ParseSymbolFile(symbol_text) + symbols = ParseSymbolText(symbol_text) - # Parse the taskinfo file. + # Load the tasklist. try: - with open(options.taskinfo_path, 'r') as taskinfo_file: - taskinfo_text = taskinfo_file.read() - tasklist = ParseTasklistFile(taskinfo_text, symbols) + export_taskinfo = ctypes.CDLL(options.export_taskinfo) + except OSError: + raise StackAnalyzerError('Failed to load export_taskinfo.') - except IOError: - raise StackAnalyzerError('Failed to open taskinfo.') + tasklist = LoadTasklist(options.section, export_taskinfo, symbols) analyzer = StackAnalyzer(options, symbols, tasklist) analyzer.Analyze() |