diff options
| author | blasty <blasty@local> | 2026-07-27 21:43:57 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-27 21:43:57 +0200 |
| commit | fe717b7ea9bce9abc816c7b35a4c23bccf0d7f74 (patch) | |
| tree | 26ba158d8c04e45f9cc2876e76263075fd4d73e4 /README.md | |
| parent | trace: read Tenet execution traces (model layer) (diff) | |
| download | ida-tui-fe717b7ea9bce9abc816c7b35a4c23bccf0d7f74.tar.gz ida-tui-fe717b7ea9bce9abc816c7b35a4c23bccf0d7f74.tar.xz ida-tui-fe717b7ea9bce9abc816c7b35a4c23bccf0d7f74.zip | |
trace: docked registers + timeline, and stepping through time
M0 of the trace viewer. --trace FILE loads a Tenet trace beside the binary and
docks a pane on the right: where you are in time, the register state there, and
a timeline.
Docked rather than modal on purpose. A trace turns every other view into "state
at time T", so time and registers are context you read WHILE looking at code,
not something you open and dismiss.
The registers the current instruction WROTE are highlighted. That difference is
the entire reason a delta trace is readable, and it's free — the trace already
says which registers each line changed.
] / [ step one instruction. } / { step over, by following the stack pointer: a
call pushes, so the callee runs with SP below where we started, and stepping
until SP comes back up lands after the return. That's cheaper and more portable
than recognising call instructions per architecture, and it degrades correctly —
on an instruction that calls nothing, SP is already >= the start and it's one
step. Verified on a real call: t=13 -> 18, past 5 instructions, where a plain
step gives 14.
The load waits for the function index because rebasing needs the database's
addresses: our echo trace runs at 0x7ffff6faa000 and the same code sits at
0x2000 in the database. Rebased -0x7ffff6fa8000, 12 functions touched.
Register values stay as the trace recorded them (they're machine state) while
everything else on screen is in database addresses, so the header shows both —
"pc 0x2aed (trace 0x7ffff6faaaed)" — rather than leaving the two to be puzzled
over side by side.
tests: test_trace_ui.py (13) records its own trace with the QEMU tracer and
drives the real UI — loads, rebases onto real functions, the dock renders, ] and
[ step and the code view follows, and } steps OVER a call found in that trace
rather than at a hardcoded index. Skips with a message if the tracer isn't
built. 209/0 scenarios.
One thing worth recording: my first attempt to add the key bindings SILENTLY did
nothing — the pattern contained a literal \\u2026 where the file has a real
ellipsis, so the replace matched nothing and the bindings never appeared. The
action worked when called directly, which made it look like a key-routing
problem. Assert on the replacement, not on the diff looking plausible.
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 20 |
1 files changed, 20 insertions, 0 deletions
@@ -112,6 +112,26 @@ multi-image firmware wants. They apply to the first open only — after that the > reopen. Delete those stale files (never the `.i64`) and retry — `ida-tui` does > this automatically. +## Execution traces + +Load a [Tenet](https://github.com/gaasedelen/tenet) trace alongside the binary +and explore it in time: + +```sh +./ida-tui /path/to/binary --trace trace.0.log +``` + +A docked pane on the right shows the registers at the current timestamp (the +ones the current instruction wrote are highlighted) and a timeline. `]` and `[` +step one instruction forward and back; `}` and `{` step over a call by following +the stack pointer. The code view follows. + +Trace addresses are rebased onto the database automatically — a traced process +is relocated, so nothing lines up until that's solved. + +Traces are recorded separately; see `~/dev/tenet/tenet-original/tracers/` for the +QEMU tracer. + ## RPC / driving the TUI Give the TUI `--rpc <sock>` to expose a unix-socket control channel, then drive |
