ca13734bf0
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers, middleware (CAN stack, diagnostics, safety), applications, board support, build/test tooling, and documentation.
205 lines
6.9 KiB
Python
Executable File
205 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
@file stack_usage_analyzer.py
|
|
@brief Stack usage analyzer for Automotive RTOS
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import argparse
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
|
|
class StackUsageAnalyzer:
|
|
"""Analyze stack usage from map files and source code"""
|
|
|
|
def __init__(self, project_root):
|
|
self.project_root = Path(project_root)
|
|
self.stack_usage = {}
|
|
self.function_sizes = {}
|
|
self.call_graph = defaultdict(list)
|
|
|
|
def parse_map_file(self, map_file):
|
|
"""Parse linker map file for symbol information"""
|
|
print(f"Parsing map file: {map_file}")
|
|
|
|
symbol_pattern = re.compile(
|
|
r'^\s+0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\w+)\s+(\w+)$'
|
|
)
|
|
|
|
with open(map_file, 'r') as f:
|
|
for line in f:
|
|
match = symbol_pattern.match(line)
|
|
if match:
|
|
address = int(match.group(1), 16)
|
|
size = int(match.group(2), 16)
|
|
section = match.group(3)
|
|
name = match.group(4)
|
|
|
|
if section in ['.text', '.data', '.bss']:
|
|
self.function_sizes[name] = {
|
|
'size': size,
|
|
'section': section,
|
|
'address': address
|
|
}
|
|
|
|
def analyze_source_files(self):
|
|
"""Analyze source files for stack allocation"""
|
|
print("Analyzing source files for stack usage...")
|
|
|
|
source_patterns = [
|
|
self.project_root / 'kernel' / 'src' / '**' / '*.c',
|
|
self.project_root / 'drivers' / 'src' / '**' / '*.c',
|
|
self.project_root / 'middleware' / '**' / 'src' / '**' / '*.c',
|
|
self.project_root / 'applications' / '**' / 'src' / '**' / '*.c',
|
|
]
|
|
|
|
stack_var_pattern = re.compile(
|
|
r'\b(?:uint8_t|uint16_t|uint32_t|int8_t|int16_t|int32_t|'
|
|
r'char|short|int|long|float|double|struct|union)\s+'
|
|
r'(\w+)\s*\[(\d+)\]'
|
|
)
|
|
|
|
for pattern in source_patterns:
|
|
for source_file in self.project_root.glob(str(pattern)):
|
|
self._analyze_source_file(source_file, stack_var_pattern)
|
|
|
|
def _analyze_source_file(self, source_file, pattern):
|
|
"""Analyze individual source file"""
|
|
try:
|
|
with open(source_file, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Find all stack variables
|
|
for match in pattern.finditer(content):
|
|
var_name = match.group(1)
|
|
array_size = int(match.group(2))
|
|
|
|
# Estimate variable size
|
|
var_size = self._estimate_variable_size(match.group(0))
|
|
|
|
if var_size > 0:
|
|
print(f" {source_file.name}: {var_name}[{array_size}] = {var_size} bytes")
|
|
except Exception as e:
|
|
print(f" Warning: Could not analyze {source_file}: {e}")
|
|
|
|
def _estimate_variable_size(self, declaration):
|
|
"""Estimate variable size from declaration"""
|
|
size_map = {
|
|
'uint8_t': 1, 'int8_t': 1, 'char': 1,
|
|
'uint16_t': 2, 'int16_t': 2, 'short': 2,
|
|
'uint32_t': 4, 'int32_t': 4, 'int': 4, 'float': 4,
|
|
'uint64_t': 8, 'int64_t': 8, 'double': 8, 'long': 8,
|
|
}
|
|
|
|
for type_name, size in size_map.items():
|
|
if type_name in declaration:
|
|
# Find array size
|
|
array_match = re.search(r'\[(\d+)\]', declaration)
|
|
if array_match:
|
|
return size * int(array_match.group(1))
|
|
return size
|
|
|
|
return 0
|
|
|
|
def analyze_task_stacks(self):
|
|
"""Analyze task stack configurations"""
|
|
print("\nAnalyzing task stack configurations...")
|
|
|
|
task_config_file = self.project_root / 'config' / 'task_config.h'
|
|
if not task_config_file.exists():
|
|
print(" Task configuration file not found")
|
|
return
|
|
|
|
with open(task_config_file, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Find stack size definitions
|
|
stack_pattern = re.compile(
|
|
r'#define\s+TASK_STACK_(\w+)\s+(\d+)'
|
|
)
|
|
|
|
total_stack = 0
|
|
task_count = 0
|
|
|
|
print("\n Task Stack Allocations:")
|
|
print(" " + "-" * 40)
|
|
|
|
for match in stack_pattern.finditer(content):
|
|
task_name = match.group(1)
|
|
stack_size = int(match.group(2))
|
|
total_stack += stack_size
|
|
task_count += 1
|
|
|
|
print(f" {task_name:30s} {stack_size:8d} bytes")
|
|
|
|
print(" " + "-" * 40)
|
|
print(f" Total: {total_stack} bytes across {task_count} tasks")
|
|
|
|
def generate_report(self, output_file):
|
|
"""Generate stack usage report"""
|
|
print(f"\nGenerating report: {output_file}")
|
|
|
|
with open(output_file, 'w') as f:
|
|
f.write("=" * 80 + "\n")
|
|
f.write("Stack Usage Analysis Report\n")
|
|
f.write("=" * 80 + "\n\n")
|
|
|
|
# Function sizes
|
|
f.write("Function Sizes:\n")
|
|
f.write("-" * 40 + "\n")
|
|
f.write(f"{'Function':30s} {'Size':>8s} {'Section':10s}\n")
|
|
f.write("-" * 40 + "\n")
|
|
|
|
for name, info in sorted(self.function_sizes.items(),
|
|
key=lambda x: x[1]['size'], reverse=True):
|
|
f.write(f"{name:30s} {info['size']:8d} {info['section']:10s}\n")
|
|
|
|
f.write("\n\n")
|
|
|
|
# Recommendations
|
|
f.write("Recommendations:\n")
|
|
f.write("-" * 40 + "\n")
|
|
f.write("1. Review functions with large stack allocations\n")
|
|
f.write("2. Consider using static allocation for large buffers\n")
|
|
f.write("3. Monitor stack usage during runtime\n")
|
|
f.write("4. Ensure stack overflow detection is enabled\n")
|
|
|
|
def analyze(self, map_file=None):
|
|
"""Run complete analysis"""
|
|
print("=" * 60)
|
|
print("Stack Usage Analyzer")
|
|
print("=" * 60)
|
|
|
|
# Parse map file if provided
|
|
if map_file and os.path.exists(map_file):
|
|
self.parse_map_file(map_file)
|
|
|
|
# Analyze source files
|
|
self.analyze_source_files()
|
|
|
|
# Analyze task stacks
|
|
self.analyze_task_stacks()
|
|
|
|
# Generate report
|
|
report_file = self.project_root / 'build' / 'stack_usage_report.txt'
|
|
report_file.parent.mkdir(parents=True, exist_ok=True)
|
|
self.generate_report(report_file)
|
|
|
|
print(f"\nReport saved to: {report_file}")
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Stack usage analyzer')
|
|
parser.add_argument('--project', default='.', help='Project root directory')
|
|
parser.add_argument('--map-file', help='Linker map file')
|
|
parser.add_argument('--output', help='Output report file')
|
|
|
|
args = parser.parse_args()
|
|
|
|
analyzer = StackUsageAnalyzer(args.project)
|
|
analyzer.analyze(args.map_file)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|