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.
214 lines
7.0 KiB
Python
Executable File
214 lines
7.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
@file trace_analyzer.py
|
|
@brief Trace analyzer for Automotive RTOS debugging
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import argparse
|
|
import json
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
class TraceAnalyzer:
|
|
"""Analyze RTOS trace data"""
|
|
|
|
def __init__(self, trace_file):
|
|
self.trace_file = Path(trace_file)
|
|
self.events = []
|
|
self.task_timeline = defaultdict(list)
|
|
self.context_switches = []
|
|
self.interrupts = []
|
|
|
|
def parse_trace_file(self):
|
|
"""Parse trace file"""
|
|
print(f"Parsing trace file: {self.trace_file}")
|
|
|
|
if not self.trace_file.exists():
|
|
print(f"Error: Trace file not found")
|
|
return False
|
|
|
|
event_pattern = re.compile(
|
|
r'\[(\d+\.?\d*)\]\s+(\w+):\s+(.*)'
|
|
)
|
|
|
|
with open(self.trace_file, 'r') as f:
|
|
for line in f:
|
|
match = event_pattern.match(line)
|
|
if match:
|
|
timestamp = float(match.group(1))
|
|
event_type = match.group(2)
|
|
event_data = match.group(3)
|
|
|
|
event = {
|
|
'timestamp': timestamp,
|
|
'type': event_type,
|
|
'data': event_data
|
|
}
|
|
|
|
self.events.append(event)
|
|
|
|
# Categorize events
|
|
if event_type == 'TASK_SWITCH':
|
|
self.context_switches.append(event)
|
|
elif event_type == 'IRQ_ENTER':
|
|
self.interrupts.append(event)
|
|
elif event_type == 'TASK_START':
|
|
task_name = event_data.split(':')[0].strip()
|
|
self.task_timeline[task_name].append({
|
|
'type': 'start',
|
|
'timestamp': timestamp
|
|
})
|
|
elif event_type == 'TASK_END':
|
|
task_name = event_data.split(':')[0].strip()
|
|
self.task_timeline[task_name].append({
|
|
'type': 'end',
|
|
'timestamp': timestamp
|
|
})
|
|
|
|
print(f"Parsed {len(self.events)} events")
|
|
return True
|
|
|
|
def analyze_context_switches(self):
|
|
"""Analyze context switches"""
|
|
print("\nContext Switch Analysis:")
|
|
print("-" * 60)
|
|
|
|
if not self.context_switches:
|
|
print("No context switches found")
|
|
return
|
|
|
|
# Calculate intervals
|
|
intervals = []
|
|
for i in range(1, len(self.context_switches)):
|
|
interval = (self.context_switches[i]['timestamp'] -
|
|
self.context_switches[i-1]['timestamp'])
|
|
intervals.append(interval)
|
|
|
|
if intervals:
|
|
avg_interval = sum(intervals) / len(intervals)
|
|
min_interval = min(intervals)
|
|
max_interval = max(intervals)
|
|
|
|
print(f"Total context switches: {len(self.context_switches)}")
|
|
print(f"Average interval: {avg_interval:.3f} ms")
|
|
print(f"Minimum interval: {min_interval:.3f} ms")
|
|
print(f"Maximum interval: {max_interval:.3f} ms")
|
|
|
|
def analyze_task_execution(self):
|
|
"""Analyze task execution times"""
|
|
print("\nTask Execution Analysis:")
|
|
print("-" * 60)
|
|
print(f"{'Task Name':25s} {'Executions':>10s} {'Total Time':>10s} {'Avg Time':>10s}")
|
|
print("-" * 60)
|
|
|
|
for task_name, events in self.task_timeline.items():
|
|
executions = 0
|
|
total_time = 0
|
|
|
|
for i in range(0, len(events) - 1, 2):
|
|
if i + 1 < len(events):
|
|
if events[i]['type'] == 'start' and events[i+1]['type'] == 'end':
|
|
duration = events[i+1]['timestamp'] - events[i]['timestamp']
|
|
total_time += duration
|
|
executions += 1
|
|
|
|
if executions > 0:
|
|
avg_time = total_time / executions
|
|
print(f"{task_name:25s} {executions:10d} {total_time:10.3f} {avg_time:10.3f}")
|
|
|
|
def analyze_interrupts(self):
|
|
"""Analyze interrupt handling"""
|
|
print("\nInterrupt Analysis:")
|
|
print("-" * 40)
|
|
|
|
if not self.interrupts:
|
|
print("No interrupts found")
|
|
return
|
|
|
|
irq_count = defaultdict(int)
|
|
for irq in self.interrupts:
|
|
irq_name = irq['data'].strip()
|
|
irq_count[irq_name] += 1
|
|
|
|
for irq_name, count in irq_count.items():
|
|
print(f"{irq_name:20s} {count:5d} occurrences")
|
|
|
|
def generate_timeline(self, output_file):
|
|
"""Generate task timeline visualization"""
|
|
print(f"\nGenerating timeline: {output_file}")
|
|
|
|
with open(output_file, 'w') as f:
|
|
f.write("RTOS Task Timeline\n")
|
|
f.write("=" * 80 + "\n\n")
|
|
|
|
# Find time range
|
|
if not self.events:
|
|
return
|
|
|
|
start_time = self.events[0]['timestamp']
|
|
end_time = self.events[-1]['timestamp']
|
|
duration = end_time - start_time
|
|
|
|
f.write(f"Time range: {start_time:.3f} to {end_time:.3f} ms\n")
|
|
f.write(f"Duration: {duration:.3f} ms\n\n")
|
|
|
|
# Generate ASCII timeline
|
|
timeline_width = 80
|
|
time_per_char = duration / timeline_width if duration > 0 else 1
|
|
|
|
for task_name, events in self.task_timeline.items():
|
|
f.write(f"\n{task_name}:\n")
|
|
|
|
# Create timeline
|
|
timeline = [' '] * timeline_width
|
|
|
|
for i in range(0, len(events) - 1, 2):
|
|
if i + 1 < len(events):
|
|
start_pos = int((events[i]['timestamp'] - start_time) / time_per_char)
|
|
end_pos = int((events[i+1]['timestamp'] - start_time) / time_per_char)
|
|
|
|
for pos in range(start_pos, min(end_pos, timeline_width)):
|
|
if 0 <= pos < timeline_width:
|
|
timeline[pos] = '#'
|
|
|
|
f.write(''.join(timeline) + '\n')
|
|
|
|
f.write("\nLegend: '#' = task running, ' ' = task not running\n")
|
|
|
|
def analyze(self):
|
|
"""Run complete analysis"""
|
|
print("=" * 60)
|
|
print("Trace Analyzer")
|
|
print("=" * 60)
|
|
|
|
if not self.parse_trace_file():
|
|
return
|
|
|
|
self.analyze_context_switches()
|
|
self.analyze_task_execution()
|
|
self.analyze_interrupts()
|
|
|
|
# Generate timeline
|
|
output_dir = self.trace_file.parent
|
|
timeline_file = output_dir / 'timeline.txt'
|
|
self.generate_timeline(timeline_file)
|
|
|
|
print(f"\nTimeline saved to: {timeline_file}")
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Trace analyzer')
|
|
parser.add_argument('trace_file', help='Trace file to analyze')
|
|
parser.add_argument('--output', help='Output directory')
|
|
|
|
args = parser.parse_args()
|
|
|
|
analyzer = TraceAnalyzer(args.trace_file)
|
|
analyzer.analyze()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|