Skip to content

Commit

Permalink
feat(debugger): Print limited source code context (#3217)
Browse files Browse the repository at this point in the history
  • Loading branch information
mverzilli authored and guipublic committed Oct 24, 2023
1 parent 09f8a6d commit d3f7be2
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 20 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion tooling/debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ acvm.workspace = true
nargo.workspace = true
noirc_printable_type.workspace = true
thiserror.workspace = true
codespan-reporting.workspace = true
easy-repl = "0.2.1"
owo-colors = "3"
owo-colors = "3"
94 changes: 77 additions & 17 deletions tooling/debugger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ use acvm::BlackBoxFunctionSolver;
use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap};

use nargo::artifacts::debug::DebugArtifact;
use nargo::errors::ExecutionError;
use nargo::errors::{ExecutionError, Location};
use nargo::NargoError;

use nargo::ops::ForeignCallExecutor;

use easy_repl::{command, CommandStatus, Critical, Repl};
use std::cell::{Cell, RefCell};
use std::{
cell::{Cell, RefCell},
ops::Range,
};

use owo_colors::OwoColorize;

use codespan_reporting::files::Files;

enum SolveResult {
Done,
Ok,
Expand Down Expand Up @@ -74,25 +79,72 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
println!("Finished execution");
} else {
println!("Stopped at opcode {}: {}", ip, opcodes[ip]);
Self::show_source_code_location(&OpcodeLocation::Acir(ip), &self.debug_artifact);
self.show_source_code_location(&OpcodeLocation::Acir(ip), &self.debug_artifact);
}
}

fn show_source_code_location(location: &OpcodeLocation, debug_artifact: &DebugArtifact) {
fn print_location_path(&self, loc: Location) {
let line_number = self.debug_artifact.location_line_number(loc).unwrap();
let column_number = self.debug_artifact.location_column_number(loc).unwrap();

println!(
"At {}:{line_number}:{column_number}",
self.debug_artifact.name(loc.file).unwrap()
);
}

fn show_source_code_location(&self, location: &OpcodeLocation, debug_artifact: &DebugArtifact) {
let locations = debug_artifact.debug_symbols[0].opcode_location(location);
if let Some(locations) = locations {
for loc in locations {
let file = &debug_artifact.file_map[&loc.file];
let source = &file.source.as_str();
let start = loc.span.start() as usize;
let end = loc.span.end() as usize;
println!("At {}:{start}-{end}", file.path.as_path().display());
println!(
"\n{}{}{}\n",
&source[0..start].to_string().dimmed(),
&source[start..end],
&source[end..].to_string().dimmed(),
);
let Some(locations) = locations else { return };
for loc in locations {
self.print_location_path(loc);

let loc_line_index = debug_artifact.location_line_index(loc).unwrap();

// How many lines before or after the location's line we
// print
let context_lines = 5;

let first_line_to_print =
if loc_line_index < context_lines { 0 } else { loc_line_index - context_lines };

let last_line_index = debug_artifact.last_line_index(loc).unwrap();
let last_line_to_print = std::cmp::min(loc_line_index + context_lines, last_line_index);

let source = debug_artifact.location_source_code(loc).unwrap();
for (current_line_index, line) in source.lines().enumerate() {
let current_line_number = current_line_index + 1;

if current_line_index < first_line_to_print {
// Ignore lines before range starts
continue;
} else if current_line_index == first_line_to_print && current_line_index > 0 {
// Denote that there's more lines before but we're not showing them
print_line_of_ellipsis(current_line_index);
}

if current_line_index > last_line_to_print {
// Denote that there's more lines after but we're not showing them,
// and stop printing
print_line_of_ellipsis(current_line_number);
break;
}

if current_line_index == loc_line_index {
// Highlight current location
let Range { start: loc_start, end: loc_end } =
debug_artifact.location_in_line(loc).unwrap();
println!(
"{:>3} {:2} {}{}{}",
current_line_number,
"->",
&line[0..loc_start].to_string().dimmed(),
&line[loc_start..loc_end],
&line[loc_end..].to_string().dimmed()
);
} else {
print_dimmed_line(current_line_number, line);
}
}
}
}
Expand All @@ -112,6 +164,14 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> {
}
}

fn print_line_of_ellipsis(line_number: usize) {
println!("{}", format!("{:>3} {}", line_number, "...").dimmed());
}

fn print_dimmed_line(line_number: usize, line: &str) {
println!("{}", format!("{:>3} {:2} {}", line_number, "", line).dimmed());
}

fn map_command_status(result: SolveResult) -> CommandStatus {
match result {
SolveResult::Ok => CommandStatus::Done,
Expand Down
49 changes: 48 additions & 1 deletion tooling/nargo/src/artifacts/debug.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use codespan_reporting::files::{Error, Files, SimpleFile};
use noirc_driver::DebugFile;
use noirc_errors::debug_info::DebugInfo;
use noirc_errors::{debug_info::DebugInfo, Location};
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
Expand Down Expand Up @@ -45,6 +45,53 @@ impl DebugArtifact {

Self { debug_symbols, file_map }
}

/// Given a location, returns its file's source code
pub fn location_source_code(&self, location: Location) -> Result<&str, Error> {
self.source(location.file)
}

/// Given a location, returns the index of the line it starts at
pub fn location_line_index(&self, location: Location) -> Result<usize, Error> {
let location_start = location.span.start() as usize;
self.line_index(location.file, location_start)
}

/// Given a location, returns the line number it starts at
pub fn location_line_number(&self, location: Location) -> Result<usize, Error> {
let location_start = location.span.start() as usize;
let line_index = self.line_index(location.file, location_start)?;
self.line_number(location.file, line_index)
}

/// Given a location, returns the column number it starts at
pub fn location_column_number(&self, location: Location) -> Result<usize, Error> {
let location_start = location.span.start() as usize;
let line_index = self.line_index(location.file, location_start)?;
self.column_number(location.file, line_index, location_start)
}

/// Given a location, returns a Span relative to its line's
/// position in the file. This is useful when processing a file's
/// contents on a per-line-basis.
pub fn location_in_line(&self, location: Location) -> Result<Range<usize>, Error> {
let location_start = location.span.start() as usize;
let location_end = location.span.end() as usize;
let line_index = self.line_index(location.file, location_start)?;
let line_span = self.line_range(location.file, line_index)?;

let start_in_line = location_start - line_span.start;
let end_in_line = location_end - line_span.start;

Ok(Range { start: start_in_line, end: end_in_line })
}

/// Given a location, returns the last line index
/// of its file
pub fn last_line_index(&self, location: Location) -> Result<usize, Error> {
let source = self.source(location.file)?;
self.line_index(location.file, source.len())
}
}

impl<'a> Files<'a> for DebugArtifact {
Expand Down
5 changes: 4 additions & 1 deletion tooling/nargo/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use acvm::{
acir::circuit::OpcodeLocation,
pwg::{ErrorLocation, OpcodeResolutionError},
};
use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic, Location};
use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic};

pub use noirc_errors::Location;

use noirc_printable_type::ForeignCallError;
use thiserror::Error;

Expand Down

0 comments on commit d3f7be2

Please sign in to comment.