Skip to content

Commit

Permalink
feat: add debugging commands to manage breakpoints
Browse files Browse the repository at this point in the history
Also:
- add a constructor for `DebugContext`
- don't quit the debugger when finishing execution of the current circuit
  • Loading branch information
ggiraldez committed Oct 14, 2023
1 parent e7bb49d commit 2866771
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 31 deletions.
4 changes: 4 additions & 0 deletions acvm-repo/acvm/src/pwg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> {
status
}

pub fn get_status(&self) -> &ACVMStatus {
&self.status
}

/// Sets the VM status to [ACVMStatus::Failure] using the provided `error`.
/// Returns the new status.
fn fail(&mut self, error: OpcodeResolutionError) -> ACVMStatus {
Expand Down
137 changes: 107 additions & 30 deletions tooling/debugger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use fm::FileId;
use noirc_driver::DebugFile;
use noirc_errors::Location;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;

enum SolveResult {
Done,
Expand Down Expand Up @@ -81,9 +82,26 @@ struct DebugContext<'backend, B: BlackBoxFunctionSolver> {
foreign_call_executor: ForeignCallExecutor,
circuit: &'backend Circuit,
show_output: bool,
breakpoints: HashSet<OpcodeLocation>,
}

impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
fn new(
blackbox_solver: &'backend B,
circuit: &'backend Circuit,
debug_artifact: DebugArtifact,
initial_witness: WitnessMap,
) -> Self {
Self {
acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness),
foreign_call_executor: ForeignCallExecutor::default(),
circuit,
debug_artifact,
show_output: true,
breakpoints: HashSet::new(),
}
}

fn handle_acvm_status(&mut self, status: ACVMStatus) -> Result<SolveResult, NargoError> {
match status {
ACVMStatus::Solved => Ok(SolveResult::Done),
Expand Down Expand Up @@ -130,10 +148,10 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
Some(location) => {
match location {
OpcodeLocation::Acir(ip) => {
println!("Stopped at opcode {}: {}", ip, opcodes[ip])
println!("At opcode {}: {}", ip, opcodes[ip])
}
OpcodeLocation::Brillig { acir_index: ip, brillig_index } => println!(
"Stopped at opcode {} in Brillig block {}: {}",
"At opcode {} in Brillig block {}: {}",
brillig_index, ip, opcodes[ip]
),
}
Expand Down Expand Up @@ -184,7 +202,13 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
fn display_opcodes(&self) {
let current = self.acvm.instruction_pointer();
for (ip, opcode) in self.acvm.opcodes().iter().enumerate() {
let marker = if ip == current { "->" } else { "" };
let marker = if ip == current {
"->"
} else if self.breakpoints.contains(&OpcodeLocation::Acir(ip)) {
"*"
} else {
""
};
println!("{:>3} {:2} {:?}", ip, marker, opcode);
}
}
Expand All @@ -205,19 +229,42 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
SolveResult::Done => break,
SolveResult::Ok => {}
}
if let Some(location) = self.acvm.location() {
if self.breakpoints.contains(&location) {
println!("Stopped at breakpoint in opcode {}", location);
self.show_current_vm_status();
return Ok(SolveResult::Ok);
}
}
}
Ok(SolveResult::Done)
}

fn finalize(self) -> WitnessMap {
self.acvm.finalize()
fn add_breakpoint(&mut self, ip: usize) {
if self.breakpoints.insert(OpcodeLocation::Acir(ip)) {
println!("Added breakpoint at opcode {ip}");
} else {
println!("Breakpoint at opcode {ip} already set");
}
}
}

fn map_command_status(result: SolveResult) -> CommandStatus {
match result {
SolveResult::Ok => CommandStatus::Done,
SolveResult::Done => CommandStatus::Quit,
fn delete_breakpoint(&mut self, ip: usize) {
if self.breakpoints.remove(&OpcodeLocation::Acir(ip)) {
println!("Breakpoint at opcode {ip} deleted");
} else {
println!("Breakpoint at opcode {ip} not set");
}
}

fn finished(&self) -> bool {
!matches!(
self.acvm.get_status(),
ACVMStatus::InProgress | ACVMStatus::RequiresForeignCall { .. }
)
}

fn finalize(self) -> WitnessMap {
self.acvm.finalize()
}
}

Expand All @@ -226,15 +273,9 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(
circuit: &Circuit,
debug_artifact: DebugArtifact,
initial_witness: WitnessMap,
show_output: bool,
) -> Result<Option<WitnessMap>, NargoError> {
let context = RefCell::new(DebugContext {
acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness),
foreign_call_executor: ForeignCallExecutor::default(),
circuit,
debug_artifact,
show_output,
});
let context =
RefCell::new(DebugContext::new(blackbox_solver, circuit, debug_artifact, initial_witness));
let ref_context = &context;

let solved = Cell::new(false);
Expand All @@ -243,7 +284,7 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(

let handle_result = |result| {
solved.set(matches!(result, SolveResult::Done));
Ok(map_command_status(result))
Ok(CommandStatus::Done)
};

let mut repl = Repl::builder()
Expand All @@ -252,9 +293,14 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(
command! {
"step to the next ACIR opcode",
() => || {
let result = ref_context.borrow_mut().step_acir_opcode().into_critical()?;
ref_context.borrow().show_current_vm_status();
handle_result(result)
if ref_context.borrow().finished() {
println!("Execution finished");
Ok(CommandStatus::Done)
} else {
let result = ref_context.borrow_mut().step_acir_opcode().into_critical()?;
ref_context.borrow().show_current_vm_status();
handle_result(result)
}
}
},
)
Expand All @@ -263,9 +309,14 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(
command! {
"step into to the next opcode",
() => || {
let result = ref_context.borrow_mut().step_into_opcode().into_critical()?;
ref_context.borrow().show_current_vm_status();
handle_result(result)
if ref_context.borrow().finished() {
println!("Execution finished");
Ok(CommandStatus::Done)
} else {
let result = ref_context.borrow_mut().step_into_opcode().into_critical()?;
ref_context.borrow().show_current_vm_status();
handle_result(result)
}
}
},
)
Expand All @@ -274,9 +325,15 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(
command! {
"continue execution until the end of the program",
() => || {
println!("(Continuing execution...)");
let result = ref_context.borrow_mut().cont().into_critical()?;
handle_result(result)
if ref_context.borrow().finished() {
println!("Execution finished");
Ok(CommandStatus::Done)
} else {
println!("(Continuing execution...)");
let result = ref_context.borrow_mut().cont().into_critical()?;
ref_context.borrow().show_current_vm_status();
handle_result(result)
}
}
},
)
Expand Down Expand Up @@ -311,15 +368,35 @@ pub fn debug_circuit<B: BlackBoxFunctionSolver>(
},
)
.add(
"current",
"location",
command! {
"display status and next opcode to evaluate",
"display next opcode to evaluate",
() => || {
ref_context.borrow().show_current_vm_status();
Ok(CommandStatus::Done)
}
},
)
.add(
"break",
command! {
"add a breakpoint at an opcode location",
(LOCATION:usize) => |ip| {
ref_context.borrow_mut().add_breakpoint(ip);
Ok(CommandStatus::Done)
}
},
)
.add(
"delete",
command! {
"delete breakpoint at an opcode location",
(LOCATION:usize) => |ip| {
ref_context.borrow_mut().delete_breakpoint(ip);
Ok(CommandStatus::Done)
}
},
)
.build()
.expect("Failed to initialize debugger repl");

Expand Down
1 change: 0 additions & 1 deletion tooling/nargo_cli/src/cli/debug_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ pub(crate) fn debug_program(
&compiled_program.circuit,
debug_artifact,
initial_witness,
true,
)
.map_err(CliError::from)
}

0 comments on commit 2866771

Please sign in to comment.