Skip to main content

foundry_debugger/
debugger.rs

1//! Debugger implementation.
2
3use crate::{DebugNode, DebuggerBuilder, ExitReason, tui::TUI};
4use alloy_primitives::map::AddressHashMap;
5use clap::ValueEnum;
6use eyre::Result;
7use foundry_evm_core::Breakpoints;
8use foundry_evm_traces::debug::ContractSources;
9use std::path::Path;
10
11/// Debugger TUI layout selection.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
13pub enum DebuggerLayout {
14    /// Select horizontal or vertical layout from the terminal size.
15    #[default]
16    Auto,
17    /// Force the two-column debugger layout.
18    Horizontal,
19    /// Force the single-column debugger layout.
20    Vertical,
21}
22
23impl DebuggerLayout {
24    pub(crate) const fn next(self) -> Self {
25        match self {
26            Self::Auto | Self::Vertical => Self::Horizontal,
27            Self::Horizontal => Self::Vertical,
28        }
29    }
30
31    pub(crate) const fn as_str(self) -> &'static str {
32        match self {
33            Self::Auto => "auto",
34            Self::Horizontal => "horizontal",
35            Self::Vertical => "vertical",
36        }
37    }
38}
39
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub struct DebuggerStats {
42    /// Sum of root-call gas used across every trace arena passed to the debugger.
43    pub session_trace_gas_used: u64,
44    /// Number of subcalls in the traces passed to the debugger.
45    pub session_subcalls: usize,
46}
47
48pub struct DebuggerContext {
49    pub debug_arena: Vec<DebugNode>,
50    pub stats: Option<DebuggerStats>,
51    pub identified_contracts: AddressHashMap<String>,
52    /// Source map of contract sources
53    pub contracts_sources: ContractSources,
54    pub breakpoints: Breakpoints,
55    pub layout: DebuggerLayout,
56}
57
58pub struct Debugger {
59    context: DebuggerContext,
60}
61
62impl Debugger {
63    /// Creates a new debugger builder.
64    #[inline]
65    pub fn builder() -> DebuggerBuilder {
66        DebuggerBuilder::new()
67    }
68
69    /// Creates a new debugger.
70    pub const fn new(
71        debug_arena: Vec<DebugNode>,
72        identified_contracts: AddressHashMap<String>,
73        contracts_sources: ContractSources,
74        breakpoints: Breakpoints,
75    ) -> Self {
76        Self {
77            context: DebuggerContext {
78                debug_arena,
79                stats: None,
80                identified_contracts,
81                contracts_sources,
82                breakpoints,
83                layout: DebuggerLayout::Auto,
84            },
85        }
86    }
87
88    pub(crate) const fn new_with_stats(
89        debug_arena: Vec<DebugNode>,
90        stats: DebuggerStats,
91        identified_contracts: AddressHashMap<String>,
92        contracts_sources: ContractSources,
93        breakpoints: Breakpoints,
94        layout: DebuggerLayout,
95    ) -> Self {
96        Self {
97            context: DebuggerContext {
98                debug_arena,
99                stats: Some(stats),
100                identified_contracts,
101                contracts_sources,
102                breakpoints,
103                layout,
104            },
105        }
106    }
107
108    /// Starts the debugger TUI. Terminates the current process on failure or user exit.
109    pub fn run_tui_exit(mut self) -> ! {
110        let code = match self.try_run_tui() {
111            Ok(ExitReason::CharExit) => 0,
112            Err(e) => {
113                let _ = sh_eprintln!("{e}");
114                1
115            }
116        };
117        std::process::exit(code)
118    }
119
120    /// Starts the debugger TUI.
121    pub fn try_run_tui(&mut self) -> Result<ExitReason> {
122        eyre::ensure!(!self.context.debug_arena.is_empty(), "debug arena is empty");
123
124        let mut tui = TUI::new(&mut self.context);
125        tui.try_run()
126    }
127
128    /// Dumps debugger data to file.
129    pub fn dump_to_file(&mut self, path: &Path) -> Result<()> {
130        eyre::ensure!(!self.context.debug_arena.is_empty(), "debug arena is empty");
131        crate::dump::dump(path, &self.context)
132    }
133}