anchor_cli/debugger/model.rs
1//! Data model consumed by the debugger TUI.
2//!
3//! A [`DebugSession`] is the output of a single `anchor debugger` run,
4//! holding every `(test, tx)` pair the profile callback captured. Each
5//! [`DebugTx`] contains one [`DebugNode`] per program invocation (top-level
6//! + CPIs) in that tx; each node owns the stream of [`DebugStep`]s it executed.
7
8use {
9 ratatui::text::Span,
10 std::{collections::BTreeMap, path::PathBuf},
11};
12
13/// Top-level debugger state: every trace captured across every test.
14pub struct DebugSession {
15 pub txs: Vec<DebugTx>,
16 /// Directories to try when resolving a `SrcLoc` whose file path is
17 /// relative — LLVM sometimes emits paths relative to the workspace root
18 /// rather than joining them with `DW_AT_comp_dir`. Checked in order.
19 pub src_roots: Vec<PathBuf>,
20 /// `(prefix → replacement)` rewrites applied to absolute DWARF paths
21 /// before lookup. Used to map the CI build path the local toolchain was
22 /// compiled under (e.g. `/home/runner/work/platform-tools/…` on Linux,
23 /// `/Users/runner/…` on macOS) to the source tree shipped with it.
24 pub path_rewrites: Vec<(PathBuf, PathBuf)>,
25 /// The directory the debugger was invoked from. Used by the source pane
26 /// to show paths relative to the user's CWD (e.g. `src/lib.rs` instead
27 /// of the full absolute path).
28 pub cwd: Option<PathBuf>,
29 /// Static disassembly per program, keyed by base58 program id. The
30 /// instructions pane reads the current step's PC, looks up the active
31 /// node's program in this map, and renders a window of PCs in memory
32 /// order — so j/k stepping reveals the actual code layout instead of
33 /// the chronological trace.
34 pub programs: BTreeMap<String, ProgramDisasm>,
35}
36
37/// Pre-rendered static disassembly for one program. Built once at
38/// arena-build time; the TUI only reads from it.
39pub struct ProgramDisasm {
40 /// Every traced-PC-eligible instruction in text-section order.
41 pub insns: Vec<StaticInsn>,
42 /// `pc → index into insns`. PCs the program never uses (data section,
43 /// padding) won't be in this map; the TUI falls back to the closest
44 /// preceding PC when that happens.
45 pub pc_to_idx: BTreeMap<u64, usize>,
46 /// True when the program's ELF carried readable DWARF line info.
47 /// Lets the source pane distinguish "rebuild needed" (no DWARF
48 /// anywhere) from "this PC has no source mapping" (DWARF present,
49 /// but LLVM didn't emit a line entry for this PC — common for
50 /// inlined frames, compiler-generated stubs, and `.text` padding).
51 pub has_dwarf: bool,
52}
53
54/// One row in the static disasm view.
55pub struct StaticInsn {
56 pub pc: u64,
57 /// Pre-highlighted spans — same syntect path as the trace cache.
58 pub disasm_spans: Vec<Span<'static>>,
59 /// Symbol name when this PC is a function entrypoint. Drives the
60 /// "--- handler @ pc N ---" header rows in the rendered view.
61 pub func_label: Option<String>,
62}
63
64/// One outer transaction's worth of traced execution.
65pub struct DebugTx {
66 pub test_name: String,
67 /// 1-indexed tx number within its test (the `txN` from the trace filename).
68 pub tx_seq: u32,
69 /// Sum of `cu_cost` across all steps in all nodes.
70 pub total_cu: u64,
71 /// One node per invocation in call order (top-level first, then CPIs).
72 pub nodes: Vec<DebugNode>,
73}
74
75/// One program invocation: either the top-level program call or a CPI.
76pub struct DebugNode {
77 /// Human-readable program label (e.g. `"vault_v2 (Vaul…dGpx)"` or
78 /// `"[unresolved <short-pid>]"`).
79 pub program_label: String,
80 /// Base58 program id as written by the profile callback.
81 pub program_id: String,
82 pub steps: Vec<DebugStep>,
83}
84
85/// One traced SBPF instruction plus everything the TUI panes need to render
86/// it.
87#[derive(Clone)]
88pub struct DebugStep {
89 pub pc: u64,
90 pub regs: [u64; 12],
91 /// Raw 8-byte SBPF instruction.
92 pub insn: [u8; 8],
93 /// Pretty disassembly (sbpf `Analysis::disassemble_instruction`).
94 pub disasm: String,
95 /// Pre-highlighted disasm spans — built once at arena-build time so the
96 /// instruction pane doesn't re-run syntect on every redraw. Held-down
97 /// j/k stays smooth even with hundreds of visible steps because the
98 /// hot path is now a clone of immutable `Span<'static>`s.
99 pub disasm_spans: Vec<Span<'static>>,
100 /// Resolved (short) function name containing `pc`.
101 pub func: String,
102 /// Call-stack depth at this step (1 = top-level function). Used for
103 /// step-over/out navigation.
104 pub call_depth: usize,
105 /// 1 for plain insn, or `ComputeBudget` syscall base cost.
106 pub cu_cost: u64,
107 /// Cumulative CU consumed through (and including) this step, within its
108 /// enclosing [`DebugNode`]. Handy for the status line.
109 pub cu_cumulative: u64,
110 /// `Some(name)` iff this is a syscall leaf step.
111 pub syscall: Option<String>,
112 /// Source location resolved via DWARF, if debug info was available.
113 pub src_loc: Option<SrcLoc>,
114}
115
116#[derive(Clone, Debug)]
117pub struct SrcLoc {
118 pub file: PathBuf,
119 pub line: u32,
120}