Skip to content

Commit

Permalink
refactor(debugger): use context when drawing (#6520)
Browse files Browse the repository at this point in the history
* refactor: name `DebugArena::flatten` return type

* refactor: use context in draw

* refactor: move terminal out of debugger struct

* dedup

* stuff

* fix: draw initial state before any event is received

* fix: breakpoint handling
  • Loading branch information
DaniPopes authored Dec 4, 2023
1 parent adf3099 commit 677e810
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 379 deletions.
2 changes: 1 addition & 1 deletion crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ pub async fn handle_traces(
.debug_arena(&result.debug)
.decoder(&decoder)
.sources(sources)
.build()?;
.build();
debugger.try_run()?;
} else {
print_traces(&mut result, &decoder, verbose).await?;
Expand Down
10 changes: 3 additions & 7 deletions crates/debugger/src/tui/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

use crate::Debugger;
use alloy_primitives::Address;
use eyre::Result;
use foundry_common::{compile::ContractSources, evm::Breakpoints, get_contract_name};
use foundry_evm_core::{
debug::{DebugArena, DebugStep},
utils::CallKind,
};
use foundry_evm_core::debug::{DebugArena, DebugNodeFlat};
use foundry_evm_traces::CallTraceDecoder;
use std::collections::HashMap;

Expand All @@ -16,7 +12,7 @@ use std::collections::HashMap;
#[must_use = "builders do nothing unless you call `build` on them"]
pub struct DebuggerBuilder {
/// Debug traces returned from the EVM execution.
debug_arena: Vec<(Address, Vec<DebugStep>, CallKind)>,
debug_arena: Vec<DebugNodeFlat>,
/// Identified contracts.
identified_contracts: HashMap<Address, String>,
/// Map of source files.
Expand Down Expand Up @@ -90,7 +86,7 @@ impl DebuggerBuilder {

/// Builds the debugger.
#[inline]
pub fn build(self) -> Result<Debugger> {
pub fn build(self) -> Debugger {
let Self { debug_arena, identified_contracts, sources, breakpoints } = self;
Debugger::new(debug_arena, identified_contracts, sources, breakpoints)
}
Expand Down
108 changes: 45 additions & 63 deletions crates/debugger/src/tui/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
use crate::{Debugger, ExitReason};
use alloy_primitives::Address;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
use eyre::Result;
use foundry_evm_core::{debug::DebugStep, utils::CallKind};
use std::ops::ControlFlow;
use foundry_evm_core::{
debug::{DebugNodeFlat, DebugStep},
utils::CallKind,
};
use std::{cell::RefCell, ops::ControlFlow};

/// This is currently used to remember last scroll position so screen doesn't wiggle as much.
#[derive(Default)]
pub(crate) struct DrawMemory {
pub(crate) current_startline: usize,
// TODO
pub(crate) current_startline: RefCell<usize>,
pub(crate) inner_call_index: usize,
pub(crate) current_mem_startline: usize,
pub(crate) current_stack_startline: usize,
Expand Down Expand Up @@ -49,37 +52,51 @@ impl<'a> DebuggerContext<'a> {
}
}

pub(crate) fn init(&mut self) -> Result<()> {
self.debugger.terminal.clear()?;
pub(crate) fn init(&mut self) {
self.gen_opcode_list();
Ok(())
}

fn debug_arena(&self) -> &[(Address, Vec<DebugStep>, CallKind)] {
pub(crate) fn debug_arena(&self) -> &[DebugNodeFlat] {
&self.debugger.debug_arena
}

pub(crate) fn debug_call(&self) -> &DebugNodeFlat {
&self.debug_arena()[self.draw_memory.inner_call_index]
}

/// Returns the current call address.
pub(crate) fn address(&self) -> &Address {
&self.debug_call().address
}

/// Returns the current call kind.
pub(crate) fn call_kind(&self) -> CallKind {
self.debug_call().kind
}

/// Returns the current debug steps.
pub(crate) fn debug_steps(&self) -> &[DebugStep] {
&self.debug_call().steps
}

/// Returns the current debug step.
pub(crate) fn current_step(&self) -> &DebugStep {
&self.debug_steps()[self.current_step]
}

fn gen_opcode_list(&mut self) {
self.opcode_list = self.opcode_list();
}

fn opcode_list(&self) -> Vec<String> {
self.debugger.debug_arena[self.draw_memory.inner_call_index]
.1
.iter()
.map(DebugStep::pretty_opcode)
.collect()
self.debug_steps().iter().map(DebugStep::pretty_opcode).collect()
}
}

impl DebuggerContext<'_> {
pub(crate) fn handle_event(&mut self, event: Event) -> ControlFlow<ExitReason> {
if self.last_index != self.draw_memory.inner_call_index {
self.opcode_list = self.debug_arena()[self.draw_memory.inner_call_index]
.1
.iter()
.map(|step| step.pretty_opcode())
.collect();
self.gen_opcode_list();
self.last_index = self.draw_memory.inner_call_index;
}

Expand All @@ -92,7 +109,7 @@ impl DebuggerContext<'_> {

fn handle_key_event(&mut self, event: KeyEvent) -> ControlFlow<ExitReason> {
if let KeyCode::Char(c) = event.code {
if c.is_alphanumeric() || c == '\'' {
if c.is_alphabetic() && self.key_buffer.starts_with('\'') {
self.handle_breakpoint(c);
return ControlFlow::Continue(());
}
Expand All @@ -106,12 +123,7 @@ impl DebuggerContext<'_> {
// Grab number of times to do it
for _ in 0..buffer_as_number(&self.key_buffer, 1) {
if event.modifiers.contains(KeyModifiers::CONTROL) {
let max_mem = (self.debug_arena()[self.draw_memory.inner_call_index].1
[self.current_step]
.memory
.len() /
32)
.saturating_sub(1);
let max_mem = (self.current_step().memory.len() / 32).saturating_sub(1);
if self.draw_memory.current_mem_startline < max_mem {
self.draw_memory.current_mem_startline += 1;
}
Expand All @@ -126,11 +138,7 @@ impl DebuggerContext<'_> {
}
KeyCode::Char('J') => {
for _ in 0..buffer_as_number(&self.key_buffer, 1) {
let max_stack = self.debug_arena()[self.draw_memory.inner_call_index].1
[self.current_step]
.stack
.len()
.saturating_sub(1);
let max_stack = self.current_step().stack.len().saturating_sub(1);
if self.draw_memory.current_stack_startline < max_stack {
self.draw_memory.current_stack_startline += 1;
}
Expand All @@ -147,8 +155,7 @@ impl DebuggerContext<'_> {
self.current_step -= 1;
} else if self.draw_memory.inner_call_index > 0 {
self.draw_memory.inner_call_index -= 1;
self.current_step =
self.debug_arena()[self.draw_memory.inner_call_index].1.len() - 1;
self.current_step = self.debug_steps().len() - 1;
}
}
self.key_buffer.clear();
Expand All @@ -169,16 +176,14 @@ impl DebuggerContext<'_> {
// Go to bottom of file
KeyCode::Char('G') => {
self.draw_memory.inner_call_index = self.debug_arena().len() - 1;
self.current_step =
self.debug_arena()[self.draw_memory.inner_call_index].1.len() - 1;
self.current_step = self.debug_steps().len() - 1;
self.key_buffer.clear();
}
// Go to previous call
KeyCode::Char('c') => {
self.draw_memory.inner_call_index =
self.draw_memory.inner_call_index.saturating_sub(1);
self.current_step =
self.debug_arena()[self.draw_memory.inner_call_index].1.len() - 1;
self.current_step = self.debug_steps().len() - 1;
self.key_buffer.clear();
}
// Go to next call
Expand Down Expand Up @@ -261,9 +266,9 @@ impl DebuggerContext<'_> {
// Find the location of the called breakpoint in the whole debug arena (at this address with
// this pc)
if let Some((caller, pc)) = self.debugger.breakpoints.get(&c) {
for (i, (_caller, debug_steps, _)) in self.debug_arena().iter().enumerate() {
if _caller == caller {
if let Some(step) = debug_steps.iter().position(|step| step.pc == *pc) {
for (i, node) in self.debug_arena().iter().enumerate() {
if node.address == *caller {
if let Some(step) = node.steps.iter().position(|step| step.pc == *pc) {
self.draw_memory.inner_call_index = i;
self.current_step = step;
break
Expand All @@ -283,8 +288,7 @@ impl DebuggerContext<'_> {
self.draw_memory.inner_call_index -= 1;
self.draw_memory.current_mem_startline = 0;
self.draw_memory.current_stack_startline = 0;
self.current_step =
self.debug_arena()[self.draw_memory.inner_call_index].1.len() - 1;
self.current_step = self.debug_steps().len() - 1;
}
}
MouseEventKind::ScrollDown => {
Expand All @@ -302,28 +306,6 @@ impl DebuggerContext<'_> {

ControlFlow::Continue(())
}

pub(crate) fn draw(&mut self) -> Result<()> {
self.debugger.terminal.draw(|f| {
let debug_arena = &self.debugger.debug_arena;
Debugger::draw_layout(
f,
debug_arena[self.draw_memory.inner_call_index].0,
&self.debugger.identified_contracts,
&self.debugger.pc_ic_maps,
&self.debugger.contracts_sources,
&debug_arena[self.draw_memory.inner_call_index].1[..],
&self.opcode_list,
self.current_step,
debug_arena[self.draw_memory.inner_call_index].2,
&mut self.draw_memory,
self.stack_labels,
self.mem_utf,
self.show_shortcuts,
)
})?;
Ok(())
}
}

/// Grab number from buffer. Used for something like '10k' to move up 10 operations
Expand Down
Loading

0 comments on commit 677e810

Please sign in to comment.