144 lines
3.6 KiB
Python
144 lines
3.6 KiB
Python
"""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
|