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