K1Guitar/tools/rtt_flash_log_dump.py

116 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Dump K1 persisted W25Q field log via J-Link RTT (`log flash dump` / `log flash scan`)."""
from __future__ import annotations
import argparse
import os
import sys
import time
from datetime import datetime
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from rtt_log_dump import ( # noqa: E402
connect_jlink,
find_rtt_control_block,
import_deps,
send_command,
wait_rtt_ready,
)
DUMP_CMD = b"log flash dump\n"
SCAN_CMD = b"log flash scan 65536\n"
def dump_flash_log(out_path: str, device: str, timeout_s: float, cmd: bytes = DUMP_CMD) -> None:
jlink = connect_jlink(device)
lines: list[str] = []
try:
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("SEGGER RTT control block not found in SRAM")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
wait_rtt_ready(jlink)
for _ in range(5):
stale = jlink.rtt_read(0, 4096)
if stale:
text = bytes(stale).decode("utf-8", errors="replace")
if text.strip():
print("RTT0 stale:", text.strip()[:200])
time.sleep(0.05)
print(f"Sending: {cmd!r}")
send_command(jlink, cmd)
buffer = b""
deadline = time.time() + timeout_s
while time.time() < deadline:
chunk = jlink.rtt_read(0, 8192)
if chunk:
buffer += bytes(chunk)
if len(buffer) >= 4096 and (len(buffer) % 8192) < 300:
print(f" received {len(buffer)} bytes...")
if b"LOG_FLASH_DUMP_END" in buffer:
break
else:
time.sleep(0.01)
if b"LOG_FLASH_DUMP_END" not in buffer:
preview = buffer[-500:].decode("utf-8", errors="replace") if buffer else ""
raise SystemExit(
"Timeout waiting for LOG_FLASH_DUMP_END.\n"
f"got {len(buffer)} bytes. tail={preview!r}"
)
text = buffer.decode("utf-8", errors="replace")
capture = False
for line in text.splitlines():
if line.strip() == "LOG_FLASH_DUMP_BEGIN":
capture = True
continue
if line.strip() == "LOG_FLASH_DUMP_END":
break
if capture:
lines.append(line)
header = (
f"# K1 flash log dump {datetime.now().isoformat(timespec='seconds')}\n"
f"# device={device}\n"
f"# cmd={cmd.decode('ascii', errors='replace').strip()}\n"
)
body = "\n".join(lines) + ("\n" if lines else "")
with open(out_path, "w", encoding="utf-8", newline="\n") as f:
f.write(header)
f.write(body)
print(f"Saved {len(lines)} lines to {out_path}")
print(f"Full path: {os.path.abspath(out_path)}")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
def main() -> None:
parser = argparse.ArgumentParser(description="Dump K1 Flash field log via RTT")
parser.add_argument("--out", default="crash.log")
parser.add_argument("--device", default="AT32F403AC")
parser.add_argument("--timeout", type=float, default=300.0)
parser.add_argument(
"--scan",
action="store_true",
help="Ignore write pointer; scan first 64KB data for recoverable lines",
)
args = parser.parse_args()
import_deps()
cmd = SCAN_CMD if args.scan else DUMP_CMD
dump_flash_log(args.out, args.device, args.timeout, cmd=cmd)
if __name__ == "__main__":
main()