forked from adafruit/circuitpython
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathswo_function_trace.py
143 lines (118 loc) · 3.56 KB
/
swo_function_trace.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""This prints out Chrome Trace Formatted json that can be viewed in Perfetto or Spall.
https://ui.perfetto.dev/
https://gravitymoth.com/spall/spall.html
Format:
https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#
Connect a USB to Serial converter to the SWO pin and then provide the serial
device to this script. It should be 1MBaud SWO signal. CTRL-C when you've captured enough data and
then it'll process and output.
pip install pysigrok-libsigrokdecode
python tools/swo_function_trace.py /dev/ttyACM0 build-metro_m7_1011/firmware.elf > trace.json
"""
import serial
import sys
import sigrokdecode
import time
import json
from elftools.elf.elffile import ELFFile
f = open(sys.argv[-1], "rb")
ef = ELFFile(f)
symtab = ef.get_section_by_name(".symtab")
symbols = {}
for s in symtab.iter_symbols():
addr = s.entry["st_value"]
symbols[addr] = s.name
f.close()
# sys.exit(0)
decoder = sigrokdecode.get_decoder("arm_itm")()
decoder.reset()
decoder.options = {"objdump": "", "elffile": ""}
decoder.start()
dwt_timestamp = 0
last_dwt_timestamp = 0
streak = 0
print("[")
stack = []
def emit(ts, addr, channel):
s = None
if addr in symbols:
s = symbols[addr]
else:
s = hex(addr)
if addr < 0x6000_0000:
s = "R:" + s
else:
s = "F:" + s
if channel[0] == "3":
stack.append(addr)
else:
if not stack or stack[-1] != addr:
return
stack.pop()
event = {
"name": s,
"ph": "B" if channel[0] == "3" else "E",
"ts": ts,
"pid": 0,
"tid": 0,
}
print(json.dumps(event), ",")
def decoder_cb(ss, es, data):
global streak
global last_dwt_timestamp
# print(ss, es, data)
ptype = data[0]
ts = (dwt_timestamp + (streak * 32)) / 500
if ptype == 0:
event = {"name": data[1][0], "ph": "i", "ts": ts, "pid": 0, "tid": 0, "s": "g"}
print(json.dumps(event), ",")
if data[1][0] == "Overflow":
while stack:
emit(ts, stack[-1], "4:")
if ptype in (0, 1):
return
if ptype == 2 and (data[1][0].startswith("3:") or data[1][0].startswith("4:")):
channel, addr = data[1][0].split()
addr = int(addr[2:], 16)
# if addr & 0x1 != 0:
# addr -= 1
# print(dwt_timestamp + streak, channel, symbols[addr], hex(addr))
emit(ts, addr, channel)
else:
# print(dwt_timestamp + streak, data)
pass
if dwt_timestamp == last_dwt_timestamp:
streak += 1
else:
streak = 0
if last_dwt_timestamp > dwt_timestamp:
raise RuntimeError()
last_dwt_timestamp = dwt_timestamp
decoder.add_callback(sigrokdecode.OUTPUT_ANN, None, decoder_cb)
s = serial.Serial(sys.argv[-2], 1000000)
buffers = []
while True:
try:
start_ts = time.monotonic_ns()
b = s.read(s.in_waiting)
if b:
end_ts = time.monotonic_ns()
buffers.append((start_ts, end_ts, b))
# print(len(b))
# if len(buffers) > 10:
# break
except KeyboardInterrupt:
break
time_per_bit = 1_000_000_000 / 1000000
min_gap = 100000000
total_bytes = 0
for start_ts, end_ts, buf in buffers:
# print(total_bytes, start_ts, end_ts, buf)
ts_per_byte = (end_ts - start_ts) / len(buf)
for i, b in enumerate(buf):
# print(total_bytes, hex(b))
total_bytes += 1
decoder.decode(
start_ts + ts_per_byte * i, start_ts + ts_per_byte * (i + 1), ("DATA", None, (b,))
)
dwt_timestamp = decoder.dwt_timestamp