Skip to main content

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}