Skip to content

Commit

Permalink
feat: Add break <line-number> command
Browse files Browse the repository at this point in the history
This makes it much easier to find the correct place to add a break point
while debugging a program.

For example, now one can do the following:

```
$ cd test_programs/execution_success/merkle_insert/
$ nargo debug
[merkle_insert] Starting debugger
At opcode 0: BRILLIG CALL func 0: PREDICATE = %EXPR [ 1 ]%
inputs: [Single(Expression { mul_terms: [], linear_combina [...]
> next
At opcode 0.17: Const { destination: MemoryAddress(21), bi [...]
At [...]/test_programs/execution_success/merkle_insert/src/main.nr:12:3
  7    ...
  8        new_root: pub Field,
  9        leaf: Field,
 10        index: Field,
 11        mimc_input: [Field; 4]
 12 -> ) {
 13        assert(old_root == std::merkle::compute_merkle_ [...]
 14
 15        let calculated_root = std::merkle::compute_merk [...]
 16        assert(new_root == calculated_root);
 17
 18        let h = mimc::mimc_bn254(mimc_input);
 19        // Regression test for PR noir-lang#891
 20        std::println(h);
 21        assert(h == 18226366069841799622585958305961373 [...]
 22    }
> break 18
Added breakpoint at opcode 0.1274
> continue
(Continuing execution...)
Stopped at breakpoint in opcode 0.1274
At opcode 0.1274: Const { destination: MemoryAddress(36),  [...]
At /home/stan/code/repos/noir/test_programs/execution_succ [...]
 13    ...
 14
 15        let calculated_root = std::merkle::compute_merk [...]
 16        assert(new_root == calculated_root);
 17
 18 ->     let h = mimc::mimc_bn254(mimc_input);
 19        // Regression test for PR noir-lang#891
 20        std::println(h);
 21        assert(h == 18226366069841799622585958305961373 [...]
 22    }
>
```
  • Loading branch information
smanilov committed May 13, 2024
1 parent 9c6de4b commit cc9347d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
11 changes: 11 additions & 0 deletions tooling/debugger/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
.filter(|v: &Vec<Location>| !v.is_empty())
}

/// Returns the `FileId` of the file associated with the innermost function on the call stack.
pub(super) fn get_current_file(&mut self) -> Option<FileId> {
match self.get_current_source_location() {
Some(locations) => match locations.last() {
Some(location) => Some(location.file),
None => None,
},
None => None,
}
}

/// Returns the (possible) stack of source locations corresponding to the
/// given opcode location. Due to compiler inlining it's possible for this
/// function to return multiple source locations. An empty vector means that
Expand Down
74 changes: 74 additions & 0 deletions tooling/debugger/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,70 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> {
}
}

fn add_breakpoint_at_line(&mut self, mut line_number: usize) {
// Make line number 0-indexed.
line_number -= 1;

let current_file = match self.context.get_current_file() {
Some(file) => file.clone(),
None => {
println!("No current file.");
return;
}
};

let mut best_start: usize = 0;
let mut best_location: Option<OpcodeLocation> = None;

// Helper function that first finds the source locations associated with the given opcode.
// Then, it finds the start and end lines of the source location and
// updates the best_start and best_location temporaries, if the given opcode_location
// matches the desired break-line better than the previous best match.
let mut update_best_match = |opcode_location: OpcodeLocation| {
let source_locations =
self.context.get_source_location_for_opcode_location(&opcode_location);
// Last means inner-most in stack trace.
match source_locations.last() {
Some(source_location) => {
let start = self.debug_artifact.location_line_index(*source_location).unwrap();
let end =
self.debug_artifact.location_end_line_index(*source_location).unwrap();
if source_location.file == current_file
&& start <= line_number
&& line_number <= end
&& line_number - start < line_number - best_start
{
best_start = start;
best_location = Some(opcode_location);
}
}
None => (),
}
};

let opcodes = self.context.get_opcodes();
for (acir_index, opcode) in opcodes.iter().enumerate() {
match &opcode {
Opcode::BrilligCall { id, .. } => {
let bytecode = &self.unconstrained_functions[*id as usize].bytecode;
for (brillig_index, ..) in bytecode.iter().enumerate() {
let opcode_location = OpcodeLocation::Brillig { acir_index, brillig_index };
update_best_match(opcode_location);
}
}
_ => {
println!("Not supported: break-line for {:?}", opcode);
return;
}
}
}

match best_location {
Some(location) => self.add_breakpoint_at(location),
None => println!("No opcode at line {}", line_number),
}
}

fn delete_breakpoint_at(&mut self, location: OpcodeLocation) {
if self.context.delete_breakpoint(&location) {
println!("Breakpoint at opcode {location} deleted");
Expand Down Expand Up @@ -475,6 +539,16 @@ pub fn run<B: BlackBoxFunctionSolver>(
}
},
)
.add(
"break",
command! {
"add a breakpoint at a line of the current file",
(line_number: usize) => |line_number| {
ref_context.borrow_mut().add_breakpoint_at_line(line_number);
Ok(CommandStatus::Done)
}
},
)
.add(
"break",
command! {
Expand Down

0 comments on commit cc9347d

Please sign in to comment.