Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(debugger): Print limited source code context #3217

Merged
merged 10 commits into from
Oct 24, 2023
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"
95 changes: 80 additions & 15 deletions tooling/debugger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
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,77 @@
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}",
Files::name(&self.debug_artifact, loc.file).unwrap()
mverzilli marked this conversation as resolved.
Show resolved Hide resolved
);
}

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 {
mverzilli marked this conversation as resolved.
Show resolved Hide resolved
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(),
);
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 = if loc_line_index + context_lines > last_line_index {
last_line_index
} else {
loc_line_index + context_lines
};
mverzilli marked this conversation as resolved.
Show resolved Hide resolved

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_ellipsized_line(current_line_index);

Check warning on line 127 in tooling/debugger/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (ellipsized)
mverzilli marked this conversation as resolved.
Show resolved Hide resolved
}

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_ellipsized_line(current_line_number);

Check warning on line 133 in tooling/debugger/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (ellipsized)
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 +169,14 @@
}
}

fn print_ellipsized_line(line_number: usize) {

Check warning on line 172 in tooling/debugger/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (ellipsized)
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> {
Files::source(self, location.file)
mverzilli marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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;
Files::line_index(self, 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 = Files::line_index(self, location.file, location_start).unwrap();
mverzilli marked this conversation as resolved.
Show resolved Hide resolved
Files::line_number(self, 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 = Files::line_index(self, location.file, location_start).unwrap();
Files::column_number(self, 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 = Files::line_index(self, location.file, location_start).unwrap();
let line_span = DebugArtifact::line_range(self, location.file, line_index).unwrap();

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).unwrap();
Files::line_index(self, 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
Loading