summaryrefslogtreecommitdiff
path: root/extra/stack_analyzer/stack_analyzer.py
diff options
context:
space:
mode:
Diffstat (limited to 'extra/stack_analyzer/stack_analyzer.py')
-rwxr-xr-xextra/stack_analyzer/stack_analyzer.py81
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()