circuitpython/tools/swo_function_trace.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

144 lines
3.6 KiB
Python
Raw Normal View History

"""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