Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions tooling/debugger/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ impl<'a, B: BlackBoxFunctionSolver<FieldElement>> 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<FieldElement>> 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<FieldElement>>(
}
},
)
.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