From 6126471bdba5a970534cee7164f1a3169a29f5fe Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 13 Feb 2025 12:26:35 -0300 Subject: [PATCH 01/60] initial version: debug tests with `nargo debug --test-name` --- tooling/debugger/src/repl.rs | 13 +- tooling/nargo/src/ops/mod.rs | 3 +- tooling/nargo/src/ops/test.rs | 6 +- tooling/nargo_cli/src/cli/dap_cmd.rs | 10 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 364 +++++++++++++++++++------ tooling/nargo_cli/src/errors.rs | 3 + 6 files changed, 304 insertions(+), 95 deletions(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 08156146985..8fdbfe92187 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -449,6 +449,13 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { fn finalize(self) -> WitnessStack { self.context.finalize() } + + fn last_error(self) -> Option> { + match self.last_result { + DebugCommandResult::Error(error) => Some(error), + _ => None, + } + } } pub fn run>( @@ -667,6 +674,10 @@ pub fn run>( let solved_witness_stack = context.into_inner().finalize(); Ok(Some(solved_witness_stack)) } else { - Ok(None) + match context.into_inner().last_error() { + // Expose the last known error + Some(error) => Err(error), + None => Ok(None), + } } } diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 7ce34b1acd2..4da66b615e9 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -7,7 +7,8 @@ pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; -pub use self::test::{TestStatus, run_test}; +pub use self::test::{check_expected_failure_message, run_test, test_status_program_compile_fail, + test_status_program_compile_pass, TestStatus,}; mod check; mod compile; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index c4adaa5cfaa..061ab21cc81 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -175,7 +175,7 @@ where /// that a constraint was never satisfiable. /// An example of this is the program `assert(false)` /// In that case, we check if the test function should fail, and if so, we return `TestStatus::Pass`. -fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunction) -> TestStatus { +pub fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunction) -> TestStatus { // The test has failed compilation, but it should never fail. Report error. if !test_function.should_fail() { return TestStatus::CompileError(err.into()); @@ -188,7 +188,7 @@ fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunct /// /// We now check whether execution passed/failed and whether it should have /// passed/failed to determine the test status. -fn test_status_program_compile_pass( +pub fn test_status_program_compile_pass( test_function: &TestFunction, abi: &Abi, debug: &[DebugInfo], @@ -228,7 +228,7 @@ fn test_status_program_compile_pass( ) } -fn check_expected_failure_message( +pub fn check_expected_failure_message( test_function: &TestFunction, failed_assertion: Option, error_diagnostic: Option, diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 8987ed80d3e..06df4a3c99e 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -19,7 +19,7 @@ use dap::server::Server; use dap::types::Capabilities; use serde_json::Value; -use super::debug_cmd::compile_bin_package_for_debugging; +use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging}; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -113,16 +113,16 @@ fn load_and_compile_project( .find(|p| p.is_binary()) .ok_or(LoadError::Generic("No matching binary packages found in workspace".into()))?; + let compile_options = + compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); let compiled_program = compile_bin_package_for_debugging( &workspace, package, - acir_mode, - skip_instrumentation, - CompileOptions::default(), + &compile_options, + expression_width, ) .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; - let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); let (inputs_map, _) = read_inputs_from_file( &package.root_dir.join(prover_name).with_extension("toml"), diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index f9303180fc0..c3cadf4d879 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,26 +1,37 @@ use std::path::Path; use acvm::FieldElement; -use acvm::acir::native_types::WitnessStack; +use acvm::acir::circuit::ExpressionWidth; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; - use fm::FileManager; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::CompileError; -use nargo::ops::{compile_program, compile_program_with_debug_instrumenter, report_errors}; +use nargo::ops::{ + TestStatus, compile_program, compile_program_with_debug_instrumenter, report_errors, + test_status_program_compile_fail, test_status_program_compile_pass, +}; use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; -use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; +use nargo::{ + NargoError, insert_all_files_for_workspace_into_file_manager, parse_all, prepare_package, +}; use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noirc_abi::InputMap; +use noirc_abi::Abi; use noirc_abi::input_parser::InputValue; -use noirc_driver::{CompileOptions, CompiledProgram, file_manager_with_stdlib}; +use noirc_driver::{ + CompileOptions, CompiledProgram, compile_no_check, + file_manager_with_stdlib, link_to_debug_crate, +}; use noirc_frontend::debug::DebugInstrumenter; -use noirc_frontend::hir::ParsedFiles; +use noirc_frontend::graph::CrateId; +use noirc_frontend::hir::def_map::TestFunction; +use noirc_frontend::hir::{Context, FunctionNameMatch, ParsedFiles}; +use super::check_cmd::check_crate_and_report_errors; use super::compile_cmd::get_target_width; use super::{LockType, WorkspaceCommand}; use crate::errors::CliError; @@ -53,6 +64,21 @@ pub(crate) struct DebugCommand { /// Raw string printing of source for testing #[clap(long, hide = true)] raw_source_printing: Option, + + /// Name (or substring) of the test function to debug + #[clap(long)] + test_name: Option, +} + +struct ExecutionParams<'a>{ + prover_name: String, + witness_name: Option, + target_dir: &'a Path, + // FIXME: perhaps this doesn't belong here + // since it is for configuring the Bn254BlackBoxSolver + // TODO: we should probably add the foreign call config in the same place + pedantic_solving: bool, + raw_source_printing: bool, } impl WorkspaceCommand for DebugCommand { @@ -75,55 +101,67 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr let skip_instrumentation = args.skip_instrumentation.unwrap_or(acir_mode); let target_dir = &workspace.target_directory_path(); - let Some(package) = workspace.into_iter().find(|p| p.is_binary()) else { + let execution_params = ExecutionParams { + prover_name: args.prover_name, + witness_name: args.witness_name, + target_dir, + pedantic_solving: args.compile_options.pedantic_solving, + raw_source_printing: args.raw_source_printing.unwrap_or(false), + }; + let workspace_clone = workspace.clone(); + + let Some(package) = workspace_clone.into_iter().find(|p| p.is_binary()) else { println!( "No matching binary packages found in workspace. Only binary packages can be debugged." ); return Ok(()); }; - let compiled_program = compile_bin_package_for_debugging( - &workspace, - package, - acir_mode, - skip_instrumentation, - args.compile_options.clone(), - )?; + let compile_options = + compile_options_for_debugging(acir_mode, skip_instrumentation, args.compile_options); - let target_width = - get_target_width(package.expression_width, args.compile_options.expression_width); + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); - let compiled_program = nargo::ops::transform_program(compiled_program, target_width); + if let Some(test_name) = args.test_name { + debug_test( + test_name, + package, + workspace, + compile_options, + execution_params, + expression_width, + ) + } else { + debug_main(package, workspace, compile_options, execution_params, expression_width) + } +} - run_async( - package, - compiled_program, - &args.prover_name, - &args.witness_name, - target_dir, - args.compile_options.pedantic_solving, - args.raw_source_printing.unwrap_or(false), - ) +pub(crate) fn compile_options_for_debugging( + acir_mode: bool, + skip_instrumentation: bool, + compile_options: CompileOptions, +) -> CompileOptions { + CompileOptions { + instrument_debug: !skip_instrumentation, + force_brillig: !acir_mode, + ..compile_options + } } pub(crate) fn compile_bin_package_for_debugging( workspace: &Workspace, package: &Package, - acir_mode: bool, - skip_instrumentation: bool, - compile_options: CompileOptions, + compile_options: &CompileOptions, + expression_width: ExpressionWidth, ) -> Result { - let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); + // TODO: extract fileManager creation + insert files into single function build_workspace_file_manager + let mut workspace_file_manager: FileManager = file_manager_with_stdlib(Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); - let mut parsed_files = parse_all(&workspace_file_manager); - let compile_options = CompileOptions { - instrument_debug: !skip_instrumentation, - force_brillig: !acir_mode, - ..compile_options - }; + let mut parsed_files = parse_all(&workspace_file_manager); - let compilation_result = if !skip_instrumentation { + let compilation_result = if compile_options.instrument_debug { let debug_state = instrument_package_files(&mut parsed_files, &workspace_file_manager, package); @@ -153,6 +191,7 @@ pub(crate) fn compile_bin_package_for_debugging( compile_options.deny_warnings, compile_options.silence_warnings, ) + .map(|compiled_program| nargo::ops::transform_program(compiled_program, expression_width)) } /// Add debugging instrumentation to all parsed files belonging to the package @@ -184,90 +223,245 @@ fn instrument_package_files( debug_instrumenter } +fn debug_main( + package: &Package, + workspace: Workspace, + compile_options: CompileOptions, + execution_params: ExecutionParams, + expression_width: ExpressionWidth, +) -> Result<(), CliError> { + let compiled_program = + compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; + + run_async(package, compiled_program, execution_params).map(|_| ()) +} + +fn debug_test( + test_name: String, + package: &Package, + workspace: Workspace, + compile_options: CompileOptions, + execution_params: ExecutionParams, + expression_width: ExpressionWidth, +) -> Result<(), CliError> { + // let workspace_file_manager = build_workspace_file_manager(&workspace.root_dir, &workspace); + // TODO: extract fileManager creation + insert files into single function build_workspace_file_manager + let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); + insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + + let mut parsed_files = parse_all(&workspace_file_manager); + let (mut context, crate_id) = + prepare_package_for_debug(&workspace_file_manager, &mut parsed_files, package); + + check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; + let test_function = get_test_function(crate_id, &context, &test_name)?; + + // TODO: see if we can replace with compile_bin_for_debugging + let compiled_program = + compile_no_check(&mut context, &compile_options, test_function.get_id(), None, false); + + let test_status = match compiled_program { + Ok(compiled_program) => { + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, + // otherwise constraints involving these expressions will not error. + let compiled_program = + nargo::ops::transform_program(compiled_program, expression_width); + + let abi = compiled_program.abi.clone(); + let debug = compiled_program.debug.clone(); + + // Debug test + let debug_result = run_async(package, compiled_program, execution_params); + + match debug_result { + Ok(result) => { + test_status_program_compile_pass(&test_function, &abi, &debug, &result) + } + Err(error) => TestStatus::Fail { + message: format!("Debugger failed: {}", error), + error_diagnostic: None, + }, + } + } + Err(err) => test_status_program_compile_fail(err, &test_function), + }; + // TODO: use prettyFormatter for showing test results + match &test_status { + TestStatus::Pass { .. } => println!("OK"), + TestStatus::Fail { message, .. } => println!("FAIL\n{message}\n"), + TestStatus::Skipped => println!("skipped"), + TestStatus::CompileError(err) => { + noirc_errors::reporter::report_all( + workspace_file_manager.as_file_map(), + &[err.clone()], + compile_options.deny_warnings, + compile_options.silence_warnings, + ); + } + } + Ok(()) +} + +// TODO: move to nargo::ops and reuse in test_cmd? +fn get_test_function( + crate_id: CrateId, + context: &Context, + test_name: &str, +) -> Result { + // TODO: review Contains signature and check if its ok to send test_name as single element + let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); + + let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); + + let test_function = match test_functions { + matchings if matchings.is_empty() => { + return Err(CliError::Generic(format!( + "`{}` does not match with any test function", + test_name + ))); + } + matchings if matchings.len() == 1 => { + let (_, test_func) = matchings.into_iter().next().unwrap(); + test_func + } + _ => { + return Err(CliError::Generic(format!( + "`{}` matches with more than one test function", + test_name + ))); + } + }; + + let test_function_has_arguments = !context + .def_interner + .function_meta(&test_function.get_id()) + .function_signature() + .0 + .is_empty(); + + if test_function_has_arguments { + return Err(CliError::Generic(String::from("Cannot debug tests with arguments"))); + } + Ok(test_function) +} + +pub(crate) fn prepare_package_for_debug<'a>( + file_manager: &'a FileManager, + parsed_files: &'a mut ParsedFiles, + package: &'a Package, +) -> (Context<'a, 'a>, CrateId) { + let debug_instrumenter = instrument_package_files(parsed_files, file_manager, package); + + // -- This :down: is from nargo::ops(compile).compile_program_with_debug_instrumenter + let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); + link_to_debug_crate(&mut context, crate_id); + context.debug_instrumenter = debug_instrumenter; + (context, crate_id) +} + +type DebugResult = Result, NargoError>; +// FIXME: You shouldn't need this. CliError already has a variant which transparently can carry a NargoError. +type ExecutionResult = + Result<(Option, WitnessStack), NargoError>; fn run_async( package: &Package, program: CompiledProgram, - prover_name: &str, - witness_name: &Option, - target_dir: &Path, - pedantic_solving: bool, - raw_source_printing: bool, -) -> Result<(), CliError> { + execution_params: ExecutionParams, +) -> Result { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, witness_stack) = debug_program_and_decode( + let debug_result = debug_program_and_decode( program, package, - prover_name, - pedantic_solving, - raw_source_printing, + &execution_params.prover_name, + execution_params.pedantic_solving, + execution_params.raw_source_printing, )?; - if let Some(solved_witness_stack) = witness_stack { - println!("[{}] Circuit witness successfully solved", package.name); - - if let Some(return_value) = return_value { - println!("[{}] Circuit output: {return_value:?}", package.name); + match debug_result { + Ok((return_value, witness_stack)) => { + let witness_stack_result = witness_stack.clone(); + println!("[{}] Circuit witness successfully solved", package.name); + + if let Some(return_value) = return_value { + println!("[{}] Circuit output: {return_value:?}", package.name); + } + + if let Some(witness_name) = execution_params.witness_name { + let witness_path = match save_witness_to_dir( + &witness_stack, + &witness_name, + &execution_params.target_dir, + ) { + Ok(path) => path, + Err(err) => return Err(CliError::from(err)), + }; + + println!("[{}] Witness saved to {}", package.name, witness_path.display()); + } + Ok(Ok(witness_stack_result)) } - - if let Some(witness_name) = witness_name { - let witness_path = - save_witness_to_dir(&solved_witness_stack, witness_name, target_dir)?; - - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - } - } else { - println!("Debugger execution halted."); + Err(error) => Ok(Err(error)), } - - Ok(()) }) } +// FIXME: We have nested results to differentiate between the execution result (the inner one - Nargo) +// and setting up the debugger errors (outer one - CliErrors) fn debug_program_and_decode( program: CompiledProgram, package: &Package, prover_name: &str, pedantic_solving: bool, raw_source_printing: bool, -) -> Result<(Option, Option>), CliError> { +) -> Result { + let program_abi = program.abi.clone(); + let initial_witness = parse_initial_witness(package, prover_name, &program.abi)?; + let debug_result = + debug_program(program, initial_witness, pedantic_solving, raw_source_printing); + match debug_result { + Ok(witness_stack) => match witness_stack { + Some(witness_stack) => { + let main_witness = &witness_stack + .peek() + .expect("Should have at least one witness on the stack") + .witness; + let (_, return_value) = program_abi.decode(main_witness)?; + Ok(Ok((return_value, witness_stack))) + } + None => Err(CliError::ExecutionHalted), + }, + Err(error) => Ok(Err(error)), + } +} + +fn parse_initial_witness( + package: &Package, + prover_name: &str, + abi: &Abi, +) -> Result, CliError> { // Parse the initial witness values from Prover.toml let (inputs_map, _) = read_inputs_from_file( &package.root_dir.join(prover_name).with_extension("toml"), - &program.abi, + abi, )?; - let program_abi = program.abi.clone(); - let witness_stack = debug_program(program, &inputs_map, pedantic_solving, raw_source_printing)?; - - match witness_stack { - Some(witness_stack) => { - let main_witness = &witness_stack - .peek() - .expect("Should have at least one witness on the stack") - .witness; - let (_, return_value) = program_abi.decode(main_witness)?; - Ok((return_value, Some(witness_stack))) - } - None => Ok((None, None)), - } + let initial_witness = abi.encode(&inputs_map, None)?; + Ok(initial_witness) } pub(crate) fn debug_program( compiled_program: CompiledProgram, - inputs_map: &InputMap, + initial_witness: WitnessMap, pedantic_solving: bool, raw_source_printing: bool, -) -> Result>, CliError> { - let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - +) -> Result>, NargoError> { noir_debugger::run_repl_session( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, raw_source_printing, ) - .map_err(CliError::from) } diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index fb241bcbd29..8b174cf781f 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -42,4 +42,7 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), + + #[error("Execution halted")] + ExecutionHalted, } From 63c98da56ece362c573e4a455984594289777fcf Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 7 Feb 2025 16:16:57 -0300 Subject: [PATCH 02/60] Configure oracle-resolver in DebugForeignCallExecutor --- tooling/debugger/src/context.rs | 8 +++- tooling/debugger/src/dap.rs | 1 + tooling/debugger/src/foreign_calls.rs | 38 +++++++++++++++--- tooling/debugger/src/lib.rs | 3 +- tooling/debugger/src/repl.rs | 5 +++ tooling/nargo/src/foreign_calls/default.rs | 6 +++ tooling/nargo/src/ops/mod.rs | 6 ++- tooling/nargo/src/ops/test.rs | 5 ++- tooling/nargo_cli/src/cli/dap_cmd.rs | 12 ++---- tooling/nargo_cli/src/cli/debug_cmd.rs | 46 ++++++++++++---------- 10 files changed, 92 insertions(+), 38 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 79e03672e8d..d324d7ae0f2 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -301,6 +301,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.witness_map() } + pub fn get_foreign_call_resolver_url(&self) -> Option { + self.foreign_call_executor.as_ref().get_foreign_call_resolver_url() + } + pub(super) fn overwrite_witness( &mut self, witness: Witness, @@ -1035,6 +1039,7 @@ mod tests { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, + None, debug_artifact, )); let mut context = DebugContext::new( @@ -1203,6 +1208,7 @@ mod tests { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, + None, debug_artifact, )); let brillig_funcs = &[brillig_bytecode]; @@ -1299,7 +1305,7 @@ mod tests { &circuits, &debug_artifact, WitnessMap::new(), - Box::new(DefaultDebugForeignCallExecutor::new(PrintOutput::Stdout)), + Box::new(DefaultDebugForeignCallExecutor::new(PrintOutput::Stdout, None)), brillig_funcs, ); diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 1df27d8ea6f..b929f5b8ab7 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -74,6 +74,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< initial_witness, Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, + None, // TODO: set oracle_resolver url debug_artifact, )), unconstrained_functions, diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index efae3df407a..48a3f6cfe8c 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -43,33 +43,54 @@ impl DebugForeignCall { pub trait DebugForeignCallExecutor: ForeignCallExecutor { fn get_variables(&self) -> Vec>; fn current_stack_frame(&self) -> Option>; + fn get_foreign_call_resolver_url(&self) -> Option; } #[derive(Default)] pub struct DefaultDebugForeignCallExecutor { pub debug_vars: DebugVars, + pub foreign_call_resolver_url: Option, } impl DefaultDebugForeignCallExecutor { fn make( output: PrintOutput<'_>, + resolver_url: Option, ex: DefaultDebugForeignCallExecutor, ) -> impl DebugForeignCallExecutor + '_ { - DefaultForeignCallBuilder::default().with_output(output).build().add_layer(ex) + DefaultForeignCallBuilder::default() + .with_output(output) + .with_resolver_url(resolver_url) + .build() + .add_layer(ex) } #[allow(clippy::new_ret_no_self, dead_code)] - pub fn new(output: PrintOutput<'_>) -> impl DebugForeignCallExecutor + '_ { - Self::make(output, Self::default()) + pub fn new( + output: PrintOutput<'_>, + resolver_url: Option, + ) -> impl DebugForeignCallExecutor + '_ { + Self::make( + output, + resolver_url.clone(), + DefaultDebugForeignCallExecutor { + foreign_call_resolver_url: resolver_url, + ..Self::default() + }, + ) } pub fn from_artifact<'a>( output: PrintOutput<'a>, + resolver_url: Option, artifact: &DebugArtifact, ) -> impl DebugForeignCallExecutor + use<'a> { - let mut ex = Self::default(); + let mut ex = DefaultDebugForeignCallExecutor { + foreign_call_resolver_url: resolver_url.clone(), + ..Self::default() + }; ex.load_artifact(artifact); - Self::make(output, ex) + Self::make(output, resolver_url, ex) } pub fn load_artifact(&mut self, artifact: &DebugArtifact) { @@ -90,6 +111,10 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { fn current_stack_frame(&self) -> Option> { self.debug_vars.current_stack_frame() } + + fn get_foreign_call_resolver_url(&self) -> Option { + self.foreign_call_resolver_url.clone() + } } fn debug_var_id(value: &FieldElement) -> DebugVarId { @@ -192,4 +217,7 @@ where fn current_stack_frame(&self) -> Option> { self.handler().current_stack_frame() } + fn get_foreign_call_resolver_url(&self) -> Option { + self.handler().get_foreign_call_resolver_url() + } } diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index f0dc859beb3..9cd6a0f8a6b 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -20,8 +20,9 @@ pub fn run_repl_session>( program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, + foreign_call_resolver_url: Option, ) -> Result>, NargoError> { - repl::run(solver, program, initial_witness, raw_source_printing) + repl::run(solver, program, initial_witness, raw_source_printing, foreign_call_resolver_url) } pub fn run_dap_loop>( diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 8fdbfe92187..b42c73854d9 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -46,9 +46,11 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], raw_source_printing: bool, + foreign_call_resolver_url: Option, ) -> Self { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, + foreign_call_resolver_url, debug_artifact, )); let context = DebugContext::new( @@ -345,6 +347,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let breakpoints: Vec = self.context.iterate_breakpoints().copied().collect(); let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, + self.context.get_foreign_call_resolver_url(), self.debug_artifact, )); self.context = DebugContext::new( @@ -463,6 +466,7 @@ pub fn run>( program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, + foreign_call_resolver_url: Option, ) -> Result>, NargoError> { let circuits = &program.program.functions; let debug_artifact = @@ -475,6 +479,7 @@ pub fn run>( initial_witness, unconstrained_functions, raw_source_printing, + foreign_call_resolver_url, )); let ref_context = &context; diff --git a/tooling/nargo/src/foreign_calls/default.rs b/tooling/nargo/src/foreign_calls/default.rs index c6053a1175e..606f819ddc8 100644 --- a/tooling/nargo/src/foreign_calls/default.rs +++ b/tooling/nargo/src/foreign_calls/default.rs @@ -54,6 +54,12 @@ impl<'a> DefaultForeignCallBuilder<'a> { self } + /// Override the output. + pub fn with_resolver_url(mut self, resolver_url: Option) -> Self { + self.resolver_url = resolver_url; + self + } + /// Enable or disable mocks. pub fn with_mocks(mut self, enabled: bool) -> Self { self.enable_mocks = enabled; diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 4da66b615e9..0b523b5c033 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -7,8 +7,10 @@ pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; -pub use self::test::{check_expected_failure_message, run_test, test_status_program_compile_fail, - test_status_program_compile_pass, TestStatus,}; +pub use self::test::{ + check_expected_failure_message, run_test, test_status_program_compile_fail, + test_status_program_compile_pass, TestStatus, +}; mod check; mod compile; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 061ab21cc81..29e9fdfaa08 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -175,7 +175,10 @@ where /// that a constraint was never satisfiable. /// An example of this is the program `assert(false)` /// In that case, we check if the test function should fail, and if so, we return `TestStatus::Pass`. -pub fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunction) -> TestStatus { +pub fn test_status_program_compile_fail( + err: CompileError, + test_function: &TestFunction, +) -> TestStatus { // The test has failed compilation, but it should never fail. Report error. if !test_function.should_fail() { return TestStatus::CompileError(err.into()); diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 06df4a3c99e..6733e11140e 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -113,15 +113,11 @@ fn load_and_compile_project( .find(|p| p.is_binary()) .ok_or(LoadError::Generic("No matching binary packages found in workspace".into()))?; - let compile_options = + let compile_options = compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); - let compiled_program = compile_bin_package_for_debugging( - &workspace, - package, - &compile_options, - expression_width, - ) - .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + let compiled_program = + compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) + .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; let (inputs_map, _) = read_inputs_from_file( diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index c3cadf4d879..3657c070c2f 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -20,11 +20,11 @@ use nargo::{ use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noirc_abi::Abi; use noirc_abi::input_parser::InputValue; +use noirc_abi::Abi; use noirc_driver::{ - CompileOptions, CompiledProgram, compile_no_check, - file_manager_with_stdlib, link_to_debug_crate, + compile_no_check, file_manager_with_stdlib, link_to_debug_crate, CompileOptions, + CompiledProgram, }; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::graph::CrateId; @@ -68,6 +68,10 @@ pub(crate) struct DebugCommand { /// Name (or substring) of the test function to debug #[clap(long)] test_name: Option, + + /// JSON RPC url to solve oracle calls + #[clap(long)] + oracle_resolver: Option, } struct ExecutionParams<'a>{ @@ -79,6 +83,7 @@ struct ExecutionParams<'a>{ // TODO: we should probably add the foreign call config in the same place pedantic_solving: bool, raw_source_printing: bool, + oracle_resolver_url: Option, } impl WorkspaceCommand for DebugCommand { @@ -99,20 +104,21 @@ impl WorkspaceCommand for DebugCommand { pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliError> { let acir_mode = args.acir_mode; let skip_instrumentation = args.skip_instrumentation.unwrap_or(acir_mode); - let target_dir = &workspace.target_directory_path(); let execution_params = ExecutionParams { prover_name: args.prover_name, witness_name: args.witness_name, - target_dir, + target_dir: &workspace.target_directory_path(), pedantic_solving: args.compile_options.pedantic_solving, raw_source_printing: args.raw_source_printing.unwrap_or(false), + oracle_resolver_url: args.oracle_resolver, }; let workspace_clone = workspace.clone(); - let Some(package) = workspace_clone.into_iter().find(|p| p.is_binary()) else { + let Some(package) = workspace_clone.into_iter().find(|p| p.is_binary() || p.is_contract()) + else { println!( - "No matching binary packages found in workspace. Only binary packages can be debugged." + "No matching binary or contract packages found in workspace. Only these packages can be debugged." ); return Ok(()); }; @@ -373,13 +379,7 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let debug_result = debug_program_and_decode( - program, - package, - &execution_params.prover_name, - execution_params.pedantic_solving, - execution_params.raw_source_printing, - )?; + let debug_result = debug_program_and_decode(program, package, &execution_params)?; match debug_result { Ok((return_value, witness_stack)) => { @@ -414,14 +414,18 @@ fn run_async( fn debug_program_and_decode( program: CompiledProgram, package: &Package, - prover_name: &str, - pedantic_solving: bool, - raw_source_printing: bool, + execution_params: &ExecutionParams, ) -> Result { let program_abi = program.abi.clone(); - let initial_witness = parse_initial_witness(package, prover_name, &program.abi)?; - let debug_result = - debug_program(program, initial_witness, pedantic_solving, raw_source_printing); + let initial_witness = + parse_initial_witness(package, &execution_params.prover_name, &program.abi)?; + let debug_result = debug_program( + program, + initial_witness, + execution_params.pedantic_solving, + execution_params.raw_source_printing, + execution_params.oracle_resolver_url.clone(), + ); match debug_result { Ok(witness_stack) => match witness_stack { Some(witness_stack) => { @@ -457,11 +461,13 @@ pub(crate) fn debug_program( initial_witness: WitnessMap, pedantic_solving: bool, raw_source_printing: bool, + foreign_call_resolver_url: Option, ) -> Result>, NargoError> { noir_debugger::run_repl_session( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, raw_source_printing, + foreign_call_resolver_url, ) } From 866833bac258eed76c4410f0af32e5a6e5af9287 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Feb 2025 16:07:40 -0300 Subject: [PATCH 03/60] Spawn debugger in different thread than REPL the repl communicates with the debugger through mpsc channels --- Cargo.lock | 1 + tooling/debugger/Cargo.toml | 2 + tooling/debugger/src/context.rs | 274 +++++++++++++++++++++-- tooling/debugger/src/foreign_calls.rs | 9 + tooling/debugger/src/lib.rs | 6 +- tooling/debugger/src/repl.rs | 297 ++++++++++++++++--------- tooling/nargo_cli/src/cli/debug_cmd.rs | 3 +- 7 files changed, 469 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca3fe90ee5d..4587bbb2fa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3216,6 +3216,7 @@ version = "1.0.0-beta.3" dependencies = [ "acvm", "assert_cmd", + "bn254_blackbox_solver", "build-data", "codespan-reporting", "dap", diff --git a/tooling/debugger/Cargo.toml b/tooling/debugger/Cargo.toml index b9b83d86836..88047a0c281 100644 --- a/tooling/debugger/Cargo.toml +++ b/tooling/debugger/Cargo.toml @@ -28,6 +28,8 @@ dap.workspace = true easy-repl = "0.2.1" owo-colors = "3" serde_json.workspace = true +bn254_blackbox_solver.workspace = true + [dev-dependencies] assert_cmd = "2.0.12" diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index d324d7ae0f2..e5831653d50 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -10,6 +10,7 @@ use acvm::pwg::{ }; use acvm::{BlackBoxFunctionSolver, FieldElement}; +use bn254_blackbox_solver::Bn254BlackBoxSolver; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; use nargo::NargoError; @@ -17,10 +18,12 @@ use nargo::errors::{ExecutionError, Location}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; +use noirc_printable_type::{PrintableType, PrintableValue}; use thiserror::Error; use std::collections::BTreeMap; -use std::collections::{HashSet, hash_set::Iter}; +use std::collections::HashSet; +use std::sync::mpsc::{Receiver, Sender}; /// A Noir program is composed by /// `n` ACIR circuits @@ -227,6 +230,74 @@ pub(super) enum DebugCommandResult { Error(NargoError), } +#[derive(Debug)] +pub struct DebugStackFrame { + pub function_name: String, + pub function_params: Vec, + pub variables: Vec<(String, PrintableValue, PrintableType)>, +} + +impl From<&StackFrame<'_, F>> for DebugStackFrame { + fn from(value: &StackFrame) -> Self { + DebugStackFrame { + function_name: value.function_name.to_string(), + function_params: value.function_params.iter().map(|param| param.to_string()).collect(), + variables: value + .variables + .iter() + .map(|(name, value, var_type)| { + (name.to_string(), (**value).clone(), (*var_type).clone()) + }) + .collect(), + } + } +} + +#[derive(Debug)] +pub(super) enum DebugCommandAPIResult { + DebugCommandResult(DebugCommandResult), + DebugLocation(Option), + Opcodes(Vec>), + Locations(Vec), + DebugLocations(Vec), + Bool(bool), + WitnessMap(WitnessMap), + MemoryValue(Option>>), + Unit, + Variables(Vec>), + WitnessStack(WitnessStack), + Field(Option), +} + +#[derive(Debug)] +pub(super) enum DebugCommandAPI { + GetCurrentDebugLocation, + GetOpcodes, + GetOpcodesOfCircuit(u32), + GetSourceLocationForDebugLocation(DebugLocation), + GetCallStack, + IsBreakpointSet(DebugLocation), + IsValidDebugLocation(DebugLocation), + AddBreakpoint(DebugLocation), + DeleteBreakpoint(DebugLocation), + GetWitnessMap, + IsExecutingBrillig, + GetBrilligMemory, + WriteBrilligMemory(usize, FieldElement, BitSize), + OverwriteWitness(Witness, FieldElement), + GetVariables, + IsSolved, + Restart, + Finalize, + // execution control + StepAcirOpcode, + StepIntoOpcode, + NextInto, + NextOver, + NextOut, + Cont, +} + pub struct ExecutionFrame<'a, B: BlackBoxFunctionSolver> { circuit_id: u32, acvm: ACVM<'a, FieldElement, B>, @@ -251,6 +322,148 @@ pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { unconstrained_functions: &'a [BrilligBytecode], acir_opcode_addresses: AddressMap, + initial_witness: WitnessMap, +} + +pub(super) fn start_debugger<'a>( + command_rx: Receiver, + result_tx: Sender, + circuits: Vec>, + debug_artifact: &'a DebugArtifact, + initial_witness: WitnessMap, + foreign_call_executor: Box, + unconstrained_functions: Vec>, +) { + // We are using the default Bn254BlackBoxSolver since BlackBoxFunctionSolver are not Send + // so we cannot send it between threads + let blackbox_solver = Bn254BlackBoxSolver::default(); // TODO: receive and configure pedantic_solving + let mut context = DebugContext::new( + &blackbox_solver, + &circuits, + debug_artifact, + initial_witness.clone(), + foreign_call_executor, + &unconstrained_functions, + ); + + println!("Debugger ready for receiving messages.."); + loop { + // recv blocks until it receives message + if let Ok(received) = command_rx.recv() { + let result = match received { + DebugCommandAPI::GetCurrentDebugLocation => { + DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) + } + DebugCommandAPI::GetOpcodes => { + DebugCommandAPIResult::Opcodes(to_vec(context.get_opcodes())) + } + DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => DebugCommandAPIResult::Opcodes( + to_vec(context.get_opcodes_of_circuit(circuit_id)), + ), + DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { + DebugCommandAPIResult::Locations( + context.get_source_location_for_debug_location(&debug_location), + ) + } + DebugCommandAPI::GetCallStack => { + DebugCommandAPIResult::DebugLocations(context.get_call_stack()) + } + DebugCommandAPI::IsBreakpointSet(debug_location) => { + DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) + } + DebugCommandAPI::IsValidDebugLocation(debug_location) => { + DebugCommandAPIResult::Bool(context.is_valid_debug_location(&debug_location)) + } + DebugCommandAPI::AddBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) + } + DebugCommandAPI::DeleteBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) + } + DebugCommandAPI::Restart => { + context.restart(); + DebugCommandAPIResult::Unit + } + DebugCommandAPI::GetWitnessMap => { + DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) + } + DebugCommandAPI::IsExecutingBrillig => { + DebugCommandAPIResult::Bool(context.is_executing_brillig()) + } + DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( + context.get_brillig_memory().map(|values| values.to_vec()), + ), + DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { + context.write_brillig_memory(ptr, value, bit_size); + DebugCommandAPIResult::Unit + } + DebugCommandAPI::OverwriteWitness(witness, value) => { + DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) + } + + DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( + context.get_variables().iter().map(|var| DebugStackFrame::from(var)).collect(), + ), + DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), + DebugCommandAPI::StepAcirOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) + } + DebugCommandAPI::StepIntoOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) + } + DebugCommandAPI::NextInto => { + DebugCommandAPIResult::DebugCommandResult(context.next_into()) + } + DebugCommandAPI::NextOver => { + DebugCommandAPIResult::DebugCommandResult(context.next_over()) + } + DebugCommandAPI::NextOut => { + DebugCommandAPIResult::DebugCommandResult(context.next_out()) + } + DebugCommandAPI::Cont => DebugCommandAPIResult::DebugCommandResult(context.cont()), + DebugCommandAPI::Finalize => { + let witness_stack = context.finalize(); + let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); + // We need to stop the 'event loop' since `finalize` consumes the context + drop(result_tx); + drop(command_rx); + break; + } + }; + let Ok(()) = result_tx.send(result) else { + drop(result_tx); + drop(command_rx); + break; + }; + } else { + println!("Upstream channel closed. Terminating debugger"); + drop(result_tx); + drop(command_rx); + break; + } + } +} + +fn to_vec(opcodes: &[Opcode]) -> Vec> { + opcodes.iter().map(|op| op.clone()).collect() +} + +fn initialize_acvm<'a, B: BlackBoxFunctionSolver>( + backend: &'a B, + circuits: &'a [Circuit], + initial_witness: WitnessMap, + unconstrained_functions: &'a [BrilligBytecode], +) -> ACVM<'a, FieldElement, B> { + let current_circuit_id: u32 = 0; + let initial_circuit = &circuits[current_circuit_id as usize]; + + ACVM::new( + backend, + &initial_circuit.opcodes, + initial_witness, + unconstrained_functions, + &initial_circuit.assert_messages, + ) } impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { @@ -264,16 +477,8 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { ) -> Self { let source_to_opcodes = build_source_to_opcode_debug_mappings(debug_artifact); let current_circuit_id: u32 = 0; - let initial_circuit = &circuits[current_circuit_id as usize]; let acir_opcode_addresses = AddressMap::new(circuits, unconstrained_functions); Self { - acvm: ACVM::new( - blackbox_solver, - &initial_circuit.opcodes, - initial_witness, - unconstrained_functions, - &initial_circuit.assert_messages, - ), current_circuit_id, brillig_solver: None, witness_stack: WitnessStack::default(), @@ -286,6 +491,13 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { circuits, unconstrained_functions, acir_opcode_addresses, + initial_witness: initial_witness.clone(), // keeping it to be able to restart + acvm: initialize_acvm( + &blackbox_solver, + circuits, + initial_witness, + unconstrained_functions, + ), } } @@ -301,10 +513,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.witness_map() } - pub fn get_foreign_call_resolver_url(&self) -> Option { - self.foreign_call_executor.as_ref().get_foreign_call_resolver_url() - } - pub(super) fn overwrite_witness( &mut self, witness: Witness, @@ -553,7 +761,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, foreign_call: ForeignCallWaitInfo, ) -> DebugCommandResult { + println!("About to call foreign call executor"); let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); + println!("Executor "); match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { @@ -848,10 +1058,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.breakpoints.remove(location) } - pub(super) fn iterate_breakpoints(&self) -> Iter<'_, DebugLocation> { - self.breakpoints.iter() - } - pub(super) fn clear_breakpoints(&mut self) { self.breakpoints.clear(); } @@ -865,6 +1071,34 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.witness_stack.push(0, last_witness_map); self.witness_stack } + + fn restart(&mut self) { + // let breakpoints: Vec = self.iterate_breakpoints().copied().collect(); + // let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( + // PrintOutput::Stdout, + // self.context.get_foreign_call_resolver_url(), + // self.debug_artifact, + // )); + // self.context = DebugContext::new( + // self.blackbox_solver, + // self.circuits, + // self.debug_artifact, + // self.initial_witness.clone(), + // foreign_call_executor, + // self.unconstrained_functions, + // ); + // for debug_location in breakpoints { + // self.context.add_breakpoint(debug_location); + // } + self.current_circuit_id = 0; + self.foreign_call_executor.restart(&self.debug_artifact); + self.acvm = initialize_acvm( + self.backend, + self.circuits, + self.initial_witness.clone(), + self.unconstrained_functions, + ); + } } fn is_debug_file_in_debug_crate(debug_file: &DebugFile) -> bool { @@ -1042,7 +1276,7 @@ mod tests { None, debug_artifact, )); - let mut context = DebugContext::new( + let mut context = DebugContext::::new( &solver, circuits, debug_artifact, @@ -1212,7 +1446,7 @@ mod tests { debug_artifact, )); let brillig_funcs = &[brillig_bytecode]; - let mut context = DebugContext::new( + let mut context = DebugContext::::new( &solver, circuits, debug_artifact, @@ -1300,7 +1534,7 @@ mod tests { let debug_artifact = DebugArtifact { debug_symbols: vec![], file_map: BTreeMap::new() }; let brillig_funcs = &[brillig_one, brillig_two]; - let context = DebugContext::new( + let context = DebugContext::::new( &solver, &circuits, &debug_artifact, diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 48a3f6cfe8c..a8c10f8bc64 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -44,6 +44,7 @@ pub trait DebugForeignCallExecutor: ForeignCallExecutor { fn get_variables(&self) -> Vec>; fn current_stack_frame(&self) -> Option>; fn get_foreign_call_resolver_url(&self) -> Option; + fn restart(&mut self, artifact: &DebugArtifact) -> (); } #[derive(Default)] @@ -115,6 +116,11 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { fn get_foreign_call_resolver_url(&self) -> Option { self.foreign_call_resolver_url.clone() } + + fn restart(&mut self, artifact: &DebugArtifact) -> () { + self.debug_vars = DebugVars::default(); + self.load_artifact(artifact); + } } fn debug_var_id(value: &FieldElement) -> DebugVarId { @@ -220,4 +226,7 @@ where fn get_foreign_call_resolver_url(&self) -> Option { self.handler().get_foreign_call_resolver_url() } + fn restart(&mut self, artifact: &DebugArtifact) -> () { + (&mut self.handler).restart(artifact); + } } diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 9cd6a0f8a6b..ddced83d013 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -15,14 +15,14 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::NargoError; use noirc_driver::CompiledProgram; -pub fn run_repl_session>( - solver: &B, +pub fn run_repl_session( + // solver: &B, program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, ) -> Result>, NargoError> { - repl::run(solver, program, initial_witness, raw_source_printing, foreign_call_resolver_url) + repl::run(program, initial_witness, raw_source_printing, foreign_call_resolver_url) } pub fn run_dap_loop>( diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index b42c73854d9..e83af872a33 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,4 +1,7 @@ -use crate::context::{DebugCommandResult, DebugContext, DebugLocation}; +use crate::context::{ + start_debugger, DebugCommandAPI, DebugCommandAPIResult, DebugCommandResult, DebugLocation, + DebugStackFrame, +}; use acvm::AcirField; use acvm::acir::brillig::BitSize; @@ -7,7 +10,7 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; -use acvm::{BlackBoxFunctionSolver, FieldElement}; +use acvm::FieldElement; use nargo::{NargoError, PrintOutput}; use noirc_driver::CompiledProgram; @@ -17,14 +20,18 @@ use noirc_artifacts::debug::DebugArtifact; use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; +use std::sync::mpsc::{self, Receiver, Sender}; +use std::thread; use crate::source_code_printer::print_source_code_location; -pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { - context: DebugContext<'a, B>, - blackbox_solver: &'a B, +pub struct ReplDebugger<'a> { + // context: DebugContext<'a, B>, + command_sender: Sender, + result_receiver: Receiver, + // blackbox_solver: &'a B, debug_artifact: &'a DebugArtifact, - initial_witness: WitnessMap, + // initial_witness: WitnessMap, last_result: DebugCommandResult, // ACIR functions to debug @@ -38,55 +45,49 @@ pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { raw_source_printing: bool, } -impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { +impl<'a> ReplDebugger<'a> { pub fn new( - blackbox_solver: &'a B, circuits: &'a [Circuit], debug_artifact: &'a DebugArtifact, - initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], raw_source_printing: bool, - foreign_call_resolver_url: Option, + command_sender: Sender, + result_receiver: Receiver, ) -> Self { - let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( - PrintOutput::Stdout, - foreign_call_resolver_url, - debug_artifact, - )); - let context = DebugContext::new( - blackbox_solver, - circuits, - debug_artifact, - initial_witness.clone(), - foreign_call_executor, - unconstrained_functions, - ); - let last_result = if context.get_current_debug_location().is_none() { - // handle circuit with no opcodes - DebugCommandResult::Done - } else { - DebugCommandResult::Ok - }; + let last_result = DebugCommandResult::Ok; // TODO: handle circuit with no opcodes + Self { - context, - blackbox_solver, + command_sender, + result_receiver, circuits, debug_artifact, - initial_witness, last_result, unconstrained_functions, raw_source_printing, } } + // FIXME: this probably reads better with match expression + fn call_debugger(&self, command: DebugCommandAPI) -> DebugCommandAPIResult { + if let Ok(()) = self.command_sender.send(command) { + let Ok(result) = self.result_receiver.recv() else { + panic!("Debugger closed connection unexpectedly"); + }; + result + } else { + panic!("Could not communicate with debugger") + } + } + pub fn show_current_vm_status(&self) { - let location = self.context.get_current_debug_location(); + let location = self.get_current_debug_location(); match location { None => println!("Finished execution"), Some(location) => { let circuit_id = location.circuit_id; - let opcodes = self.context.get_opcodes_of_circuit(circuit_id); + let opcodes = self.get_opcodes_of_circuit(circuit_id); + match &location.opcode_location { OpcodeLocation::Acir(ip) => { println!("At opcode {} :: {}", location, opcodes[*ip]); @@ -104,18 +105,107 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { ); } } - let locations = self.context.get_source_location_for_debug_location(&location); - print_source_code_location( - self.debug_artifact, - &locations, - self.raw_source_printing, - ); + let result = self + .call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(location)); + let DebugCommandAPIResult::Locations(locations) = result else { + panic!("Unwanted result") + }; + + print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } } } + fn send_execution_control_command(&self, command: DebugCommandAPI) -> DebugCommandResult { + let result = self.call_debugger(command); + let DebugCommandAPIResult::DebugCommandResult(command_result) = result else { + panic!("Unwanted result") + }; + command_result + } + + // TODO: find a better name + fn send_bool_command(&self, command: DebugCommandAPI) -> bool { + let result = self.call_debugger(command); + let DebugCommandAPIResult::Bool(status) = result else { panic!("Unwanted result") }; + status + } + + fn get_opcodes_of_circuit(&self, circuit_id: u32) -> Vec> { + let result = self.call_debugger(DebugCommandAPI::GetOpcodesOfCircuit(circuit_id)); + let DebugCommandAPIResult::Opcodes(opcodes) = result else { panic!("Unwanted result") }; + opcodes + } + + fn get_current_debug_location(&self) -> Option { + let result = self.call_debugger(DebugCommandAPI::GetCurrentDebugLocation); + let DebugCommandAPIResult::DebugLocation(location) = result else { + panic!("Unwanted result") + }; + location + } + + fn is_breakpoint_set(&self, debug_location: DebugLocation) -> bool { + self.send_bool_command(DebugCommandAPI::IsBreakpointSet(debug_location)) + } + + fn is_valid_debug_location(&self, debug_location: DebugLocation) -> bool { + self.send_bool_command(DebugCommandAPI::IsValidDebugLocation(debug_location)) + } + + fn add_breakpoint(&self, debug_location: DebugLocation) -> bool { + self.send_bool_command(DebugCommandAPI::AddBreakpoint(debug_location)) + } + + fn delete_breakpoint(&self, debug_location: DebugLocation) -> bool { + self.send_bool_command(DebugCommandAPI::DeleteBreakpoint(debug_location)) + } + + fn is_executing_brillig(&self) -> bool { + self.send_bool_command(DebugCommandAPI::IsExecutingBrillig) + } + fn get_brillig_memory(&self) -> Option>> { + let result = self.call_debugger(DebugCommandAPI::GetBrilligMemory); + let DebugCommandAPIResult::MemoryValue(mem) = result else { panic!("Unwanted result") }; + mem + } + fn get_variables(&self) -> Vec> { + let result = self.call_debugger(DebugCommandAPI::GetVariables); + let DebugCommandAPIResult::Variables(vars) = result else { panic!("Unwanted result") }; + vars + } + + fn overwrite_witness(&self, witness: Witness, value: FieldElement) -> Option { + let result = self.call_debugger(DebugCommandAPI::OverwriteWitness(witness, value)); + let DebugCommandAPIResult::Field(field) = result else { panic!("Unwanted result") }; + field + } + + fn is_solved(&self) -> bool { + self.send_bool_command(DebugCommandAPI::IsSolved) + } + + fn restart_debugger(&self) { + let result = self.call_debugger(DebugCommandAPI::Restart); + let DebugCommandAPIResult::Unit = result else { panic!("Unwanted result") }; + } + + fn get_witness_map(&self) -> WitnessMap { + let result = self.call_debugger(DebugCommandAPI::GetWitnessMap); + let DebugCommandAPIResult::WitnessMap(witness_map) = result else { + panic!("Unwanted result") + }; + witness_map + } + fn finalize(self) -> WitnessStack { + let result = self.call_debugger(DebugCommandAPI::Finalize); + let DebugCommandAPIResult::WitnessStack(stack) = result else { panic!("Unwanted result") }; + stack + } + fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { - let opcodes = self.context.get_opcodes(); + let result = self.call_debugger(DebugCommandAPI::GetOpcodes); + let DebugCommandAPIResult::Opcodes(opcodes) = result else { panic!("Unwanted result") }; match &debug_location.opcode_location { OpcodeLocation::Acir(instruction_pointer) => { println!( @@ -136,12 +226,21 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { ); } } - let locations = self.context.get_source_location_for_debug_location(debug_location); + // todo: should we clone the debug_location so it can be sent? + let result = + self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(*debug_location)); + let DebugCommandAPIResult::Locations(locations) = result else { panic!("Unwanted result") }; + print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } pub fn show_current_call_stack(&self) { - let call_stack = self.context.get_call_stack(); + // let call_stack = self.context.get_ca + let result = self.call_debugger(DebugCommandAPI::GetCallStack); + let DebugCommandAPIResult::DebugLocations(call_stack) = result else { + panic!("Unwanted result") + }; + if call_stack.is_empty() { println!("Finished execution. Call stack empty."); return; @@ -160,15 +259,15 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { fn display_opcodes_of_circuit(&self, circuit_id: u32) { let current_opcode_location = - self.context.get_current_debug_location().and_then(|debug_location| { + self.get_current_debug_location().and_then(|debug_location| { if debug_location.circuit_id == circuit_id { Some(debug_location.opcode_location) } else { None } }); - let opcodes = self.context.get_opcodes_of_circuit(circuit_id); - let current_acir_index = match current_opcode_location { + let opcodes = self.get_opcodes_of_circuit(circuit_id); + let current_acir_index: Option = match current_opcode_location { Some(OpcodeLocation::Acir(ip)) => Some(ip), Some(OpcodeLocation::Brillig { acir_index, .. }) => Some(acir_index), None => None, @@ -180,7 +279,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let outer_marker = |acir_index| { if current_acir_index == Some(acir_index) { "->" - } else if self.context.is_breakpoint_set(&DebugLocation { + } else if self.is_breakpoint_set(DebugLocation { circuit_id, opcode_location: OpcodeLocation::Acir(acir_index), brillig_function_id: None, @@ -193,7 +292,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let brillig_marker = |acir_index, brillig_index, brillig_function_id| { if current_acir_index == Some(acir_index) && brillig_index == current_brillig_index { "->" - } else if self.context.is_breakpoint_set(&DebugLocation { + } else if self.is_breakpoint_set(DebugLocation { circuit_id, opcode_location: OpcodeLocation::Brillig { acir_index, brillig_index }, brillig_function_id: Some(brillig_function_id), @@ -236,9 +335,9 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } fn add_breakpoint_at(&mut self, location: DebugLocation) { - if !self.context.is_valid_debug_location(&location) { + if !self.is_valid_debug_location(location) { println!("Invalid location {location}"); - } else if self.context.add_breakpoint(location) { + } else if self.add_breakpoint(location) { println!("Added breakpoint at {location}"); } else { println!("Breakpoint at {location} already set"); @@ -264,7 +363,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } fn delete_breakpoint_at(&mut self, location: DebugLocation) { - if self.context.delete_breakpoint(&location) { + if self.delete_breakpoint(location) { println!("Breakpoint at {location} deleted"); } else { println!("Breakpoint at {location} not set"); @@ -302,35 +401,35 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { fn step_acir_opcode(&mut self) { if self.validate_in_progress() { - let result = self.context.step_acir_opcode(); + let result = self.send_execution_control_command(DebugCommandAPI::StepAcirOpcode); self.handle_debug_command_result(result); } } fn step_into_opcode(&mut self) { if self.validate_in_progress() { - let result = self.context.step_into_opcode(); + let result = self.send_execution_control_command(DebugCommandAPI::StepIntoOpcode); self.handle_debug_command_result(result); } } fn next_into(&mut self) { if self.validate_in_progress() { - let result = self.context.next_into(); + let result = self.send_execution_control_command(DebugCommandAPI::NextInto); self.handle_debug_command_result(result); } } fn next_over(&mut self) { if self.validate_in_progress() { - let result = self.context.next_over(); + let result = self.send_execution_control_command(DebugCommandAPI::NextOver); self.handle_debug_command_result(result); } } fn next_out(&mut self) { if self.validate_in_progress() { - let result = self.context.next_out(); + let result = self.send_execution_control_command(DebugCommandAPI::NextOut); self.handle_debug_command_result(result); } } @@ -338,36 +437,20 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { fn cont(&mut self) { if self.validate_in_progress() { println!("(Continuing execution...)"); - let result = self.context.cont(); + let result = self.send_execution_control_command(DebugCommandAPI::Cont); self.handle_debug_command_result(result); } } fn restart_session(&mut self) { - let breakpoints: Vec = self.context.iterate_breakpoints().copied().collect(); - let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( - PrintOutput::Stdout, - self.context.get_foreign_call_resolver_url(), - self.debug_artifact, - )); - self.context = DebugContext::new( - self.blackbox_solver, - self.circuits, - self.debug_artifact, - self.initial_witness.clone(), - foreign_call_executor, - self.unconstrained_functions, - ); - for debug_location in breakpoints { - self.context.add_breakpoint(debug_location); - } + self.restart_debugger(); self.last_result = DebugCommandResult::Ok; println!("Restarted debugging session."); self.show_current_vm_status(); } pub fn show_witness_map(&self) { - let witness_map = self.context.get_witness_map(); + let witness_map = self.get_witness_map(); // NOTE: we need to clone() here to get the iterator for (witness, value) in witness_map.clone().into_iter() { println!("_{} = {value}", witness.witness_index()); @@ -375,7 +458,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } pub fn show_witness(&self, index: u32) { - if let Some(value) = self.context.get_witness_map().get_index(index) { + if let Some(value) = self.get_witness_map().get_index(index) { println!("_{} = {value}", index); } } @@ -387,17 +470,17 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { }; let witness = Witness::from(index); - _ = self.context.overwrite_witness(witness, field_value); + _ = self.overwrite_witness(witness, field_value); println!("_{} = {value}", index); } pub fn show_brillig_memory(&self) { - if !self.context.is_executing_brillig() { + if !self.is_executing_brillig() { println!("Not executing a Brillig block"); return; } - let Some(memory) = self.context.get_brillig_memory() else { + let Some(memory) = self.get_brillig_memory() else { // this can happen when just entering the Brillig block since ACVM // would have not initialized the Brillig VM yet; in fact, the // Brillig code may be skipped altogether @@ -427,15 +510,17 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { return; }; - if !self.context.is_executing_brillig() { + if !self.is_executing_brillig() { println!("Not executing a Brillig block"); return; } - self.context.write_brillig_memory(index, field_value, bit_size); + let result = + self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, field_value, bit_size)); + let DebugCommandAPIResult::Unit = result else { panic!("Unwanted result") }; } pub fn show_vars(&self) { - for frame in self.context.get_variables() { + for frame in self.get_variables() { println!("{}({})", frame.function_name, frame.function_params.join(", ")); for (var_name, value, var_type) in frame.variables.iter() { let printable_value = @@ -445,14 +530,6 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } - fn is_solved(&self) -> bool { - self.context.is_solved() - } - - fn finalize(self) -> WitnessStack { - self.context.finalize() - } - fn last_error(self) -> Option> { match self.last_result { DebugCommandResult::Error(error) => Some(error), @@ -461,25 +538,48 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } -pub fn run>( - blackbox_solver: &B, +pub fn run( + // blackbox_solver: &B, program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, ) -> Result>, NargoError> { + let debugger_circuits = program.program.functions.clone(); let circuits = &program.program.functions; - let debug_artifact = - &DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; + let debugger_artifact = + DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; + let debug_artifact = DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; + let debugger_unconstrained_functions = program.program.unconstrained_functions.clone(); let unconstrained_functions = &program.program.unconstrained_functions; + + let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( + PrintOutput::Stdout, + foreign_call_resolver_url, + &debugger_artifact, + )); + + let (command_tx, command_rx) = mpsc::channel::(); + let (result_tx, result_rx) = mpsc::channel::(); + let debugger_handler = thread::spawn(move || { + start_debugger( + command_rx, + result_tx, + debugger_circuits, + &debugger_artifact, + initial_witness, + foreign_call_executor, + debugger_unconstrained_functions, + ); + }); + let context = RefCell::new(ReplDebugger::new( - blackbox_solver, circuits, - debug_artifact, - initial_witness, + &debug_artifact, unconstrained_functions, raw_source_printing, - foreign_call_resolver_url, + command_tx, + result_rx, )); let ref_context = &context; @@ -670,7 +770,6 @@ pub fn run>( .expect("Failed to initialize debugger repl"); repl.run().expect("Debugger error"); - // REPL execution has finished. // Drop it so that we can move fields out from `context` again. drop(repl); diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 3657c070c2f..dd9dbaf805e 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -4,6 +4,7 @@ use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; +use acvm::FieldElement; use clap::Args; use fm::FileManager; use nargo::constants::PROVER_INPUT_FILE; @@ -464,7 +465,7 @@ pub(crate) fn debug_program( foreign_call_resolver_url: Option, ) -> Result>, NargoError> { noir_debugger::run_repl_session( - &Bn254BlackBoxSolver(pedantic_solving), + // &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, raw_source_printing, From 517647a6359643677f8f9f9b1d37242b59540d6f Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 13 Feb 2025 16:28:26 -0300 Subject: [PATCH 04/60] fix: set root_path and package_name on ForeignCallResolver from debugger --- tooling/debugger/src/context.rs | 13 ++++++++-- tooling/debugger/src/dap.rs | 9 +++++++ tooling/debugger/src/foreign_calls.rs | 28 ++++++++++++++++------ tooling/debugger/src/lib.rs | 9 +++++-- tooling/debugger/src/repl.rs | 7 +++++- tooling/nargo/src/foreign_calls/default.rs | 6 ----- tooling/nargo/src/foreign_calls/rpc.rs | 13 ++++++---- tooling/nargo_cli/src/cli/dap_cmd.rs | 10 ++++---- tooling/nargo_cli/src/cli/debug_cmd.rs | 14 ++++++++--- 9 files changed, 80 insertions(+), 29 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index e5831653d50..d27cc783ef3 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -763,7 +763,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { ) -> DebugCommandResult { println!("About to call foreign call executor"); let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); - println!("Executor "); + println!("Executor response: {foreign_call_result:?}"); match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { @@ -1275,6 +1275,8 @@ mod tests { PrintOutput::Stdout, None, debug_artifact, + None, + String::new(), )); let mut context = DebugContext::::new( &solver, @@ -1444,6 +1446,8 @@ mod tests { PrintOutput::Stdout, None, debug_artifact, + None, + String::new(), )); let brillig_funcs = &[brillig_bytecode]; let mut context = DebugContext::::new( @@ -1539,7 +1543,12 @@ mod tests { &circuits, &debug_artifact, WitnessMap::new(), - Box::new(DefaultDebugForeignCallExecutor::new(PrintOutput::Stdout, None)), + Box::new(DefaultDebugForeignCallExecutor::new( + PrintOutput::Stdout, + None, + None, + String::new(), + )), brillig_funcs, ); diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index b929f5b8ab7..4304bf5887b 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; +use std::path::PathBuf; use acvm::acir::circuit::Circuit; use acvm::acir::circuit::brillig::BrilligBytecode; @@ -66,6 +67,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], + root_path: Option, + package_name: String, ) -> Self { let context = DebugContext::new( solver, @@ -76,6 +79,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< PrintOutput::Stdout, None, // TODO: set oracle_resolver url debug_artifact, + root_path, + package_name )), unconstrained_functions, ); @@ -612,6 +617,8 @@ pub fn run_session>( solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + root_path: Option, + package_name: String, ) -> Result<(), ServerError> { let debug_artifact = DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; let mut session = DapSession::new( @@ -621,6 +628,8 @@ pub fn run_session>( &debug_artifact, initial_witness, &program.program.unconstrained_functions, + root_path, + package_name ); session.run_loop() diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index a8c10f8bc64..dd27174ab53 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use acvm::{ AcirField, FieldElement, acir::brillig::{ForeignCallParam, ForeignCallResult}, @@ -58,18 +60,26 @@ impl DefaultDebugForeignCallExecutor { output: PrintOutput<'_>, resolver_url: Option, ex: DefaultDebugForeignCallExecutor, + root_path: Option, + package_name: String, ) -> impl DebugForeignCallExecutor + '_ { - DefaultForeignCallBuilder::default() - .with_output(output) - .with_resolver_url(resolver_url) - .build() - .add_layer(ex) + DefaultForeignCallBuilder { + output: output, + enable_mocks: true, + resolver_url: resolver_url, + root_path: root_path.clone(), + package_name: Some(package_name), + } + .build() + .add_layer(ex) } #[allow(clippy::new_ret_no_self, dead_code)] pub fn new( output: PrintOutput<'_>, resolver_url: Option, + root_path: Option, + package_name: String, ) -> impl DebugForeignCallExecutor + '_ { Self::make( output, @@ -78,6 +88,8 @@ impl DefaultDebugForeignCallExecutor { foreign_call_resolver_url: resolver_url, ..Self::default() }, + root_path, + package_name, ) } @@ -85,13 +97,15 @@ impl DefaultDebugForeignCallExecutor { output: PrintOutput<'a>, resolver_url: Option, artifact: &DebugArtifact, - ) -> impl DebugForeignCallExecutor + use<'a> { + root_path: Option, + package_name: String, + ) -> impl DebugForeignCallExecutor + use<'a> { let mut ex = DefaultDebugForeignCallExecutor { foreign_call_resolver_url: resolver_url.clone(), ..Self::default() }; ex.load_artifact(artifact); - Self::make(output, resolver_url, ex) + Self::make(output, resolver_url, ex, root_path, package_name) } pub fn load_artifact(&mut self, artifact: &DebugArtifact) { diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index ddced83d013..96a5e365887 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -6,6 +6,7 @@ mod repl; mod source_code_printer; use std::io::{Read, Write}; +use std::path::PathBuf; use ::dap::errors::ServerError; use ::dap::server::Server; @@ -21,8 +22,10 @@ pub fn run_repl_session( initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, + root_path: Option, + package_name: String, ) -> Result>, NargoError> { - repl::run(program, initial_witness, raw_source_printing, foreign_call_resolver_url) + repl::run(program, initial_witness, raw_source_printing, foreign_call_resolver_url, root_path, package_name) } pub fn run_dap_loop>( @@ -30,6 +33,8 @@ pub fn run_dap_loop>( solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + root_path: Option, + package_name: String, ) -> Result<(), ServerError> { - dap::run_session(server, solver, program, initial_witness) + dap::run_session(server, solver, program, initial_witness, root_path, package_name) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index e83af872a33..7da60fd6c73 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -20,6 +20,7 @@ use noirc_artifacts::debug::DebugArtifact; use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; +use std::path::PathBuf; use std::sync::mpsc::{self, Receiver, Sender}; use std::thread; @@ -544,6 +545,8 @@ pub fn run( initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, + root_path: Option, + package_name: String, ) -> Result>, NargoError> { let debugger_circuits = program.program.functions.clone(); let circuits = &program.program.functions; @@ -557,11 +560,13 @@ pub fn run( PrintOutput::Stdout, foreign_call_resolver_url, &debugger_artifact, + root_path, + package_name, )); let (command_tx, command_rx) = mpsc::channel::(); let (result_tx, result_rx) = mpsc::channel::(); - let debugger_handler = thread::spawn(move || { + thread::spawn(move || { start_debugger( command_rx, result_tx, diff --git a/tooling/nargo/src/foreign_calls/default.rs b/tooling/nargo/src/foreign_calls/default.rs index 606f819ddc8..c6053a1175e 100644 --- a/tooling/nargo/src/foreign_calls/default.rs +++ b/tooling/nargo/src/foreign_calls/default.rs @@ -54,12 +54,6 @@ impl<'a> DefaultForeignCallBuilder<'a> { self } - /// Override the output. - pub fn with_resolver_url(mut self, resolver_url: Option) -> Self { - self.resolver_url = resolver_url; - self - } - /// Enable or disable mocks. pub fn with_mocks(mut self, enabled: bool) -> Self { self.enable_mocks = enabled; diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index 6e485812885..4990576ce34 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -97,17 +97,22 @@ where /// This method cannot be called from inside a `tokio` runtime, for that to work /// we need to offload the execution into a different thread; see the tests. fn execute(&mut self, foreign_call: &ForeignCallWaitInfo) -> ResolveForeignCallResult { - let encoded_params = rpc_params!(ResolveForeignCallRequest { + let params = ResolveForeignCallRequest { session_id: self.id, function_call: foreign_call.clone(), - root_path: self.root_path.clone().map(|path| path.to_str().unwrap().to_string()), - package_name: self.package_name.clone(), - }); + root_path: self.root_path.clone().map(|path| path.to_str().unwrap().to_string()).or(Some(String::new())), + package_name: self.package_name.clone().or(Some(String::new())), + }; + let serialized = serde_json::to_string(¶ms)?; + println!("Serialized params: {serialized}"); + let encoded_params = rpc_params!(params); let parsed_response = self.runtime.block_on(async { self.external_resolver.request("resolve_foreign_call", encoded_params).await })?; + println!("RPC Response: {parsed_response:?}"); + Ok(parsed_response) } } diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 6733e11140e..ea621b09d44 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -11,7 +11,7 @@ use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING use noirc_frontend::graph::CrateName; use std::io::{BufReader, BufWriter, Read, Write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use dap::requests::Command; use dap::responses::ResponseBody; @@ -105,7 +105,7 @@ fn load_and_compile_project( expression_width: ExpressionWidth, acir_mode: bool, skip_instrumentation: bool, -) -> Result<(CompiledProgram, WitnessMap), LoadError> { +) -> Result<(CompiledProgram, WitnessMap, PathBuf, String), LoadError> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace @@ -132,7 +132,7 @@ fn load_and_compile_project( .encode(&inputs_map, None) .map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?; - Ok((compiled_program, initial_witness)) + Ok((compiled_program, initial_witness, workspace.root_dir.clone(), package.name.to_string())) } fn loop_uninitialized_dap( @@ -189,7 +189,7 @@ fn loop_uninitialized_dap( generate_acir, skip_instrumentation, ) { - Ok((compiled_program, initial_witness)) => { + Ok((compiled_program, initial_witness, root_path, package_name)) => { server.respond(req.ack()?)?; noir_debugger::run_dap_loop( @@ -197,6 +197,8 @@ fn loop_uninitialized_dap( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, + Some(root_path), + package_name, )?; break; } diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index dd9dbaf805e..1af845307f8 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -240,7 +240,7 @@ fn debug_main( let compiled_program = compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; - run_async(package, compiled_program, execution_params).map(|_| ()) + run_async(package, compiled_program, &workspace, execution_params).map(|_| ()) } fn debug_test( @@ -278,7 +278,7 @@ fn debug_test( let debug = compiled_program.debug.clone(); // Debug test - let debug_result = run_async(package, compiled_program, execution_params); + let debug_result = run_async(package, compiled_program, &workspace, execution_params); match debug_result { Ok(result) => { @@ -373,6 +373,7 @@ type ExecutionResult = fn run_async( package: &Package, program: CompiledProgram, + workspace: &Workspace, execution_params: ExecutionParams, ) -> Result { use tokio::runtime::Builder; @@ -380,7 +381,7 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let debug_result = debug_program_and_decode(program, package, &execution_params)?; + let debug_result = debug_program_and_decode(program, package, workspace, &execution_params)?; match debug_result { Ok((return_value, witness_stack)) => { @@ -415,6 +416,7 @@ fn run_async( fn debug_program_and_decode( program: CompiledProgram, package: &Package, + workspace: &Workspace, execution_params: &ExecutionParams, ) -> Result { let program_abi = program.abi.clone(); @@ -426,6 +428,8 @@ fn debug_program_and_decode( execution_params.pedantic_solving, execution_params.raw_source_printing, execution_params.oracle_resolver_url.clone(), + Some(workspace.root_dir.clone()), + package.name.to_string() ); match debug_result { Ok(witness_stack) => match witness_stack { @@ -463,6 +467,8 @@ pub(crate) fn debug_program( pedantic_solving: bool, raw_source_printing: bool, foreign_call_resolver_url: Option, + root_path: Option, + package_name: String, ) -> Result>, NargoError> { noir_debugger::run_repl_session( // &Bn254BlackBoxSolver(pedantic_solving), @@ -470,5 +476,7 @@ pub(crate) fn debug_program( initial_witness, raw_source_printing, foreign_call_resolver_url, + root_path, + package_name, ) } From 6ea307cbbfd6e46b2777491760241b1bd8b7856e Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 13 Feb 2025 18:32:33 -0300 Subject: [PATCH 05/60] debug println! --- tooling/debugger/src/context.rs | 2 +- tooling/nargo/src/foreign_calls/rpc.rs | 5 ----- tooling/nargo_cli/src/cli/debug_cmd.rs | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index d27cc783ef3..c5aec1fbfa3 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -761,7 +761,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, foreign_call: ForeignCallWaitInfo, ) -> DebugCommandResult { - println!("About to call foreign call executor"); + println!("Foreign call: {foreign_call:?}"); let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); println!("Executor response: {foreign_call_result:?}"); match foreign_call_result { diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index 4990576ce34..12adb074762 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -103,16 +103,11 @@ where root_path: self.root_path.clone().map(|path| path.to_str().unwrap().to_string()).or(Some(String::new())), package_name: self.package_name.clone().or(Some(String::new())), }; - let serialized = serde_json::to_string(¶ms)?; - println!("Serialized params: {serialized}"); let encoded_params = rpc_params!(params); - let parsed_response = self.runtime.block_on(async { self.external_resolver.request("resolve_foreign_call", encoded_params).await })?; - println!("RPC Response: {parsed_response:?}"); - Ok(parsed_response) } } diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 1af845307f8..5e66c5e0a2b 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -295,7 +295,7 @@ fn debug_test( // TODO: use prettyFormatter for showing test results match &test_status { TestStatus::Pass { .. } => println!("OK"), - TestStatus::Fail { message, .. } => println!("FAIL\n{message}\n"), + TestStatus::Fail { message, error_diagnostic } => println!("FAIL\n{message}. {error_diagnostic:?}\n"), TestStatus::Skipped => println!("skipped"), TestStatus::CompileError(err) => { noirc_errors::reporter::report_all( From 84e2cf7099e36c9223f4ecae0be23ccf7a93fe88 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 14 Feb 2025 12:47:24 -0300 Subject: [PATCH 06/60] run cargo fmt --- tooling/debugger/src/dap.rs | 4 ++-- tooling/nargo/src/foreign_calls/rpc.rs | 6 +++++- tooling/nargo_cli/src/cli/dap_cmd.rs | 2 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 11 +++++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 4304bf5887b..709eb943338 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -80,7 +80,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< None, // TODO: set oracle_resolver url debug_artifact, root_path, - package_name + package_name, )), unconstrained_functions, ); @@ -629,7 +629,7 @@ pub fn run_session>( initial_witness, &program.program.unconstrained_functions, root_path, - package_name + package_name, ); session.run_loop() diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index 12adb074762..bc4b8bd451a 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -100,7 +100,11 @@ where let params = ResolveForeignCallRequest { session_id: self.id, function_call: foreign_call.clone(), - root_path: self.root_path.clone().map(|path| path.to_str().unwrap().to_string()).or(Some(String::new())), + root_path: self + .root_path + .clone() + .map(|path| path.to_str().unwrap().to_string()) + .or(Some(String::new())), package_name: self.package_name.clone().or(Some(String::new())), }; let encoded_params = rpc_params!(params); diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index ea621b09d44..291e1099af7 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -197,7 +197,7 @@ fn loop_uninitialized_dap( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, - Some(root_path), + Some(root_path), package_name, )?; break; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 5e66c5e0a2b..ad12a80b0a3 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -285,7 +285,7 @@ fn debug_test( test_status_program_compile_pass(&test_function, &abi, &debug, &result) } Err(error) => TestStatus::Fail { - message: format!("Debugger failed: {}", error), + message: format!("Debugger failed: {:?}", error), error_diagnostic: None, }, } @@ -295,7 +295,9 @@ fn debug_test( // TODO: use prettyFormatter for showing test results match &test_status { TestStatus::Pass { .. } => println!("OK"), - TestStatus::Fail { message, error_diagnostic } => println!("FAIL\n{message}. {error_diagnostic:?}\n"), + TestStatus::Fail { message, error_diagnostic } => { + println!("FAIL\n{message}. {error_diagnostic:?}\n") + } TestStatus::Skipped => println!("skipped"), TestStatus::CompileError(err) => { noirc_errors::reporter::report_all( @@ -381,7 +383,8 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let debug_result = debug_program_and_decode(program, package, workspace, &execution_params)?; + let debug_result = + debug_program_and_decode(program, package, workspace, &execution_params)?; match debug_result { Ok((return_value, witness_stack)) => { @@ -429,7 +432,7 @@ fn debug_program_and_decode( execution_params.raw_source_printing, execution_params.oracle_resolver_url.clone(), Some(workspace.root_dir.clone()), - package.name.to_string() + package.name.to_string(), ); match debug_result { Ok(witness_stack) => match witness_stack { From f25241e1495e9fe4e4e6b71885b531ba3aaab2d9 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 14 Feb 2025 12:48:16 -0300 Subject: [PATCH 07/60] Map opcode resulution erros in debugger as well --- tooling/debugger/src/context.rs | 24 ++++++++++++++++----- tooling/nargo/src/errors.rs | 33 ++++++++++++++++++++++++++++ tooling/nargo/src/ops/execute.rs | 37 +++++++------------------------- 3 files changed, 60 insertions(+), 34 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index c5aec1fbfa3..cc8204c6383 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -1,7 +1,7 @@ use crate::foreign_calls::DebugForeignCallExecutor; use acvm::acir::brillig::BitSize; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; -use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; +use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation, ResolvedOpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ @@ -13,8 +13,8 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; +use nargo::errors::{map_execution_error, ExecutionError, Location}; use nargo::NargoError; -use nargo::errors::{ExecutionError, Location}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; @@ -751,9 +751,23 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.brillig_solver = Some(solver); self.handle_foreign_call(foreign_call) } - Err(err) => DebugCommandResult::Error(NargoError::ExecutionError( - ExecutionError::SolvingError(err, None), - )), + Err(err) => { + let resolved_call_stack: Vec = self + .get_call_stack() + .iter() + .map(|debug_loc| { + ResolvedOpcodeLocation { + acir_function_index: usize::try_from(debug_loc.circuit_id).unwrap(), // FIXME: is this ok? why circuit_id is u32? + opcode_location: debug_loc.opcode_location, + } + }) + .collect(); + + DebugCommandResult::Error(NargoError::ExecutionError(map_execution_error( + err, + &resolved_call_stack, + ))) + } } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index f1743af79ca..5d76fb12ba6 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -242,3 +242,36 @@ pub fn try_to_diagnose_runtime_error( let error = CustomDiagnostic::simple_error(message, String::new(), location); Some(error.with_call_stack(source_locations)) } + +pub fn map_execution_error( + error: OpcodeResolutionError, + call_stack: &Vec, +) -> ExecutionError { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { .. } + | OpcodeResolutionError::IndexOutOfBounds { .. } + | OpcodeResolutionError::InvalidInputBitSize { .. } + | OpcodeResolutionError::BrilligFunctionFailed { .. } => Some(call_stack.clone()), + _ => None, + }; + + let assertion_payload: Option> = match &error { + OpcodeResolutionError::BrilligFunctionFailed { payload, .. } + | OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => payload.clone(), + _ => None, + }; + + let brillig_function_id = match &error { + OpcodeResolutionError::BrilligFunctionFailed { function_id, .. } => Some(*function_id), + _ => None, + }; + + return match assertion_payload { + Some(payload) => ExecutionError::AssertionFailed( + payload, + call_stack.expect("Should have call stack for an assertion failure"), + brillig_function_id, + ), + None => ExecutionError::SolvingError(error, call_stack), + }; +} diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index b12871c75e4..f23262f165b 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,6 +1,6 @@ use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::{ - OpcodeLocation, Program, ResolvedAssertionPayload, ResolvedOpcodeLocation, + OpcodeLocation, Program, ResolvedOpcodeLocation, }; use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ @@ -10,7 +10,7 @@ use acvm::{AcirField, BlackBoxFunctionSolver}; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::NargoError; -use crate::errors::ExecutionError; +use crate::errors::{map_execution_error, ExecutionError}; use crate::foreign_calls::ForeignCallExecutor; struct ProgramExecutor<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { @@ -89,7 +89,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> unreachable!("Execution should not stop while in `InProgress` state.") } ACVMStatus::Failure(error) => { - let call_stack = match &error { + match &error { OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Resolved(opcode_location), .. @@ -107,7 +107,6 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> opcode_location: *opcode_location, }; self.call_stack.push(resolved_location); - Some(self.call_stack.clone()) } OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { let brillig_call_stack = @@ -116,34 +115,14 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> opcode_location: *location, }); self.call_stack.extend(brillig_call_stack); - Some(self.call_stack.clone()) } - _ => None, + _ => (), }; - let assertion_payload: Option> = match &error { - OpcodeResolutionError::BrilligFunctionFailed { payload, .. } - | OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => { - payload.clone() - } - _ => None, - }; - - let brillig_function_id = match &error { - OpcodeResolutionError::BrilligFunctionFailed { function_id, .. } => { - Some(*function_id) - } - _ => None, - }; - - return Err(NargoError::ExecutionError(match assertion_payload { - Some(payload) => ExecutionError::AssertionFailed( - payload, - call_stack.expect("Should have call stack for an assertion failure"), - brillig_function_id, - ), - None => ExecutionError::SolvingError(error, call_stack), - })); + return Err(NargoError::ExecutionError(map_execution_error( + error, + &self.call_stack, + ))); } ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = self.foreign_call_executor.execute(&foreign_call)?; From f59e08e5bc7479ec313e968dbec7b56876675c9a Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 14 Feb 2025 15:25:21 -0300 Subject: [PATCH 08/60] Use PrettyFormatter for printing test result in debug_cmd --- tooling/nargo_cli/src/cli/debug_cmd.rs | 49 ++++++++++--------- tooling/nargo_cli/src/cli/test_cmd.rs | 14 +++++- .../nargo_cli/src/cli/test_cmd/formatters.rs | 4 +- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index ad12a80b0a3..6783e7cb0e3 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,4 +1,6 @@ use std::path::Path; +use std::path::PathBuf; +use std::time::Duration; use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; @@ -34,7 +36,11 @@ use noirc_frontend::hir::{Context, FunctionNameMatch, ParsedFiles}; use super::check_cmd::check_crate_and_report_errors; use super::compile_cmd::get_target_width; +use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; +use super::test_cmd::formatters::Formatter; +use super::test_cmd::TestResult; use super::{LockType, WorkspaceCommand}; +use crate::cli::test_cmd::formatters::PrettyFormatter; use crate::errors::CliError; /// Executes a circuit in debug mode @@ -251,6 +257,7 @@ fn debug_test( execution_params: ExecutionParams, expression_width: ExpressionWidth, ) -> Result<(), CliError> { + let package_name = package.name.to_string(); // let workspace_file_manager = build_workspace_file_manager(&workspace.root_dir, &workspace); // TODO: extract fileManager creation + insert files into single function build_workspace_file_manager let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); @@ -261,7 +268,7 @@ fn debug_test( prepare_package_for_debug(&workspace_file_manager, &mut parsed_files, package); check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; - let test_function = get_test_function(crate_id, &context, &test_name)?; + let (test_name, test_function) = get_test_function(crate_id, &context, &test_name)?; // TODO: see if we can replace with compile_bin_for_debugging let compiled_program = @@ -284,6 +291,7 @@ fn debug_test( Ok(result) => { test_status_program_compile_pass(&test_function, &abi, &debug, &result) } + // Debugger failed Err(error) => TestStatus::Fail { message: format!("Debugger failed: {:?}", error), error_diagnostic: None, @@ -292,22 +300,19 @@ fn debug_test( } Err(err) => test_status_program_compile_fail(err, &test_function), }; - // TODO: use prettyFormatter for showing test results - match &test_status { - TestStatus::Pass { .. } => println!("OK"), - TestStatus::Fail { message, error_diagnostic } => { - println!("FAIL\n{message}. {error_diagnostic:?}\n") - } - TestStatus::Skipped => println!("skipped"), - TestStatus::CompileError(err) => { - noirc_errors::reporter::report_all( - workspace_file_manager.as_file_map(), - &[err.clone()], - compile_options.deny_warnings, - compile_options.silence_warnings, - ); - } - } + let test_result = TestResult::new( + test_name, + package_name, + test_status, + String::new(), + Duration::from_secs(1), // FIXME: hardcoded value + ); + + let formatter: Box = Box::new(PrettyFormatter); + formatter + .test_end_sync(&test_result, 1, 1, &workspace_file_manager, true, false, false) + .expect("Could not display test result"); + Ok(()) } @@ -316,13 +321,13 @@ fn get_test_function( crate_id: CrateId, context: &Context, test_name: &str, -) -> Result { +) -> Result<(String, TestFunction), CliError> { // TODO: review Contains signature and check if its ok to send test_name as single element let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); - let test_function = match test_functions { + let (test_name, test_function) = match test_functions { matchings if matchings.is_empty() => { return Err(CliError::Generic(format!( "`{}` does not match with any test function", @@ -330,8 +335,8 @@ fn get_test_function( ))); } matchings if matchings.len() == 1 => { - let (_, test_func) = matchings.into_iter().next().unwrap(); - test_func + let (name, test_func) = matchings.into_iter().next().unwrap(); + (name, test_func) } _ => { return Err(CliError::Generic(format!( @@ -351,7 +356,7 @@ fn get_test_function( if test_function_has_arguments { return Err(CliError::Generic(String::from("Cannot debug tests with arguments"))); } - Ok(test_function) + Ok((test_name, test_function)) } pub(crate) fn prepare_package_for_debug<'a>( diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 13c6efe2aee..2a5a2a9173b 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -116,7 +116,7 @@ struct Test<'a> { runner: Box (TestStatus, String) + Send + UnwindSafe + 'a>, } -struct TestResult { +pub(crate) struct TestResult { name: String, package_name: String, status: TestStatus, @@ -124,6 +124,18 @@ struct TestResult { time_to_run: Duration, } +impl TestResult { + pub(crate) fn new( + name: String, + package_name: String, + status: TestStatus, + output: String, + time_to_run: Duration, + ) -> Self { + TestResult { name, package_name, status, output, time_to_run } + } +} + const STACK_SIZE: usize = 4 * 1024 * 1024; pub(crate) fn run(args: TestCommand, workspace: Workspace) -> Result<(), CliError> { diff --git a/tooling/nargo_cli/src/cli/test_cmd/formatters.rs b/tooling/nargo_cli/src/cli/test_cmd/formatters.rs index 68628129245..97622fc9097 100644 --- a/tooling/nargo_cli/src/cli/test_cmd/formatters.rs +++ b/tooling/nargo_cli/src/cli/test_cmd/formatters.rs @@ -24,7 +24,7 @@ use super::TestResult; /// to humans rely on the `sync` events to show a more predictable output (package by package), /// and formatters that output to a machine-readable format (like JSON) rely on the `async` /// events to show things as soon as they happen, regardless of a package ordering. -pub(super) trait Formatter: Send + Sync + RefUnwindSafe { +pub(crate) trait Formatter: Send + Sync + RefUnwindSafe { fn package_start_async(&self, package_name: &str, test_count: usize) -> std::io::Result<()>; fn package_start_sync(&self, package_name: &str, test_count: usize) -> std::io::Result<()>; @@ -64,7 +64,7 @@ pub(super) trait Formatter: Send + Sync + RefUnwindSafe { ) -> std::io::Result<()>; } -pub(super) struct PrettyFormatter; +pub(crate) struct PrettyFormatter; impl Formatter for PrettyFormatter { fn package_start_async(&self, _package_name: &str, _test_count: usize) -> std::io::Result<()> { From 2b08935e827c6bd20916aeda2689842735d98e25 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 18 Feb 2025 12:59:46 -0300 Subject: [PATCH 09/60] Instrument contract functions + set package_build_path in context --- compiler/noirc_frontend/src/debug/mod.rs | 20 +++++++++++++++++++- tooling/nargo/src/ops/execute.rs | 4 +--- tooling/nargo_cli/src/cli/debug_cmd.rs | 4 +++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 48bed1c4199..2635cc1f87d 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -1,7 +1,7 @@ use crate::ast::PathSegment; use crate::parse_program; -use crate::parser::ParsedModule; use crate::signed_field::SignedField; +use crate::parser::{ParsedModule, ParsedSubModule}; use crate::{ ast, ast::Path, @@ -63,7 +63,25 @@ impl DebugInstrumenter { if let Item { kind: ItemKind::Function(f), .. } = item { self.walk_fn(&mut f.def); } + if let Item { + kind: + ItemKind::Submodules(ParsedSubModule { + is_contract: true, + contents: contract_module @ ParsedModule { .. }, + .. + }), + .. + } = item + { + contract_module.items.iter_mut().for_each(|item| { + if let Item { kind: ItemKind::Function(f), .. } = item { + self.walk_fn(&mut f.def); + } + }); + self.insert_state_set_oracle(contract_module, 8, file); + } }); + // this part absolutely must happen after ast traversal above // so that oracle functions don't get wrapped, resulting in infinite recursion: self.insert_state_set_oracle(module, 8, file); diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index f23262f165b..49cd7c1209f 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,7 +1,5 @@ use acvm::acir::circuit::brillig::BrilligBytecode; -use acvm::acir::circuit::{ - OpcodeLocation, Program, ResolvedOpcodeLocation, -}; +use acvm::acir::circuit::{OpcodeLocation, Program, ResolvedOpcodeLocation}; use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ ACVM, ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 6783e7cb0e3..a520c187c25 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -265,7 +265,7 @@ fn debug_test( let mut parsed_files = parse_all(&workspace_file_manager); let (mut context, crate_id) = - prepare_package_for_debug(&workspace_file_manager, &mut parsed_files, package); + prepare_package_for_debug(&workspace_file_manager, &mut parsed_files, package, &workspace); check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; let (test_name, test_function) = get_test_function(crate_id, &context, &test_name)?; @@ -363,6 +363,7 @@ pub(crate) fn prepare_package_for_debug<'a>( file_manager: &'a FileManager, parsed_files: &'a mut ParsedFiles, package: &'a Package, + workspace: &Workspace, ) -> (Context<'a, 'a>, CrateId) { let debug_instrumenter = instrument_package_files(parsed_files, file_manager, package); @@ -370,6 +371,7 @@ pub(crate) fn prepare_package_for_debug<'a>( let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); link_to_debug_crate(&mut context, crate_id); context.debug_instrumenter = debug_instrumenter; + context.package_build_path = workspace.package_build_path(package); (context, crate_id) } From 0bba2ec605d42be0451cf639c0ae52a4c21838d6 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 18 Feb 2025 15:22:23 -0300 Subject: [PATCH 10/60] set pedantic_solving when creating the BlackBoxSolver --- tooling/debugger/src/context.rs | 5 ++--- tooling/debugger/src/lib.rs | 12 ++++++++++-- tooling/debugger/src/repl.rs | 3 ++- tooling/nargo_cli/src/cli/debug_cmd.rs | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index cc8204c6383..c785d8b5af6 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -333,10 +333,9 @@ pub(super) fn start_debugger<'a>( initial_witness: WitnessMap, foreign_call_executor: Box, unconstrained_functions: Vec>, + pedantic_solving: bool, ) { - // We are using the default Bn254BlackBoxSolver since BlackBoxFunctionSolver are not Send - // so we cannot send it between threads - let blackbox_solver = Bn254BlackBoxSolver::default(); // TODO: receive and configure pedantic_solving + let blackbox_solver = Bn254BlackBoxSolver(pedantic_solving); let mut context = DebugContext::new( &blackbox_solver, &circuits, diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 96a5e365887..33679e234ef 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -17,15 +17,23 @@ use nargo::NargoError; use noirc_driver::CompiledProgram; pub fn run_repl_session( - // solver: &B, program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, root_path: Option, package_name: String, + pedantic_solving: bool, ) -> Result>, NargoError> { - repl::run(program, initial_witness, raw_source_printing, foreign_call_resolver_url, root_path, package_name) + repl::run( + program, + initial_witness, + raw_source_printing, + foreign_call_resolver_url, + root_path, + package_name, + pedantic_solving, + ) } pub fn run_dap_loop>( diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 7da60fd6c73..fd5f6f12742 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -540,13 +540,13 @@ impl<'a> ReplDebugger<'a> { } pub fn run( - // blackbox_solver: &B, program: CompiledProgram, initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, root_path: Option, package_name: String, + pedantic_solving: bool, ) -> Result>, NargoError> { let debugger_circuits = program.program.functions.clone(); let circuits = &program.program.functions; @@ -575,6 +575,7 @@ pub fn run( initial_witness, foreign_call_executor, debugger_unconstrained_functions, + pedantic_solving, ); }); diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index a520c187c25..79058254b40 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -481,12 +481,12 @@ pub(crate) fn debug_program( package_name: String, ) -> Result>, NargoError> { noir_debugger::run_repl_session( - // &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, raw_source_printing, foreign_call_resolver_url, root_path, package_name, + pedantic_solving, ) } From 7ead7105f553b99b0d1766b121f8e7b51187d5a2 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 18 Feb 2025 15:54:59 -0300 Subject: [PATCH 11/60] avoid printing __debug foreign calls --- tooling/debugger/src/context.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index c785d8b5af6..40e3398259f 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -774,9 +774,14 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, foreign_call: ForeignCallWaitInfo, ) -> DebugCommandResult { - println!("Foreign call: {foreign_call:?}"); + if !foreign_call.function.starts_with("__debug") { + println!("Foreign call: {foreign_call:?}"); + } let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); - println!("Executor response: {foreign_call_result:?}"); + + if !foreign_call.function.starts_with("__debug") { + println!("Executor response: {foreign_call_result:?}"); + } match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { From 2ce58afb8056797941ff5b781b7e320f2d224dd6 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Feb 2025 13:14:49 -0300 Subject: [PATCH 12/60] workaround: retry rpc request restarting http_client on transport error --- tooling/debugger/src/context.rs | 6 --- tooling/nargo/src/foreign_calls/rpc.rs | 72 ++++++++++++++++++-------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 40e3398259f..6c5a93c6d6c 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -774,14 +774,8 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, foreign_call: ForeignCallWaitInfo, ) -> DebugCommandResult { - if !foreign_call.function.starts_with("__debug") { - println!("Foreign call: {foreign_call:?}"); - } let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); - if !foreign_call.function.starts_with("__debug") { - println!("Executor response: {foreign_call_result:?}"); - } match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index bc4b8bd451a..2a92f2a32c7 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -19,6 +19,8 @@ pub struct RPCForeignCallExecutor { id: u64, /// JSON RPC client to resolve foreign calls external_resolver: HttpClient, + + resolver_url: String, /// Root path to the program or workspace in execution. root_path: Option, /// Name of the package in execution @@ -59,17 +61,7 @@ impl RPCForeignCallExecutor { root_path: Option, package_name: Option, ) -> Self { - let mut client_builder = HttpClientBuilder::new(); - - if let Some(Ok(timeout)) = - std::env::var("NARGO_FOREIGN_CALL_TIMEOUT").ok().map(|timeout| timeout.parse()) - { - let timeout_duration = std::time::Duration::from_millis(timeout); - client_builder = client_builder.request_timeout(timeout_duration); - }; - - let oracle_resolver = - client_builder.build(resolver_url).expect("Invalid oracle resolver URL"); + let oracle_resolver = build_http_client(resolver_url); // Opcodes are executed in the `ProgramExecutor::execute_circuit` one by one in a loop, // we don't need a concurrent thread pool. @@ -81,22 +73,21 @@ impl RPCForeignCallExecutor { RPCForeignCallExecutor { external_resolver: oracle_resolver, + resolver_url: resolver_url.to_string(), id, root_path, package_name, runtime, } } -} -impl ForeignCallExecutor for RPCForeignCallExecutor -where - F: AcirField + Serialize + for<'a> Deserialize<'a>, -{ - /// Execute an async call blocking the current thread. - /// This method cannot be called from inside a `tokio` runtime, for that to work - /// we need to offload the execution into a different thread; see the tests. - fn execute(&mut self, foreign_call: &ForeignCallWaitInfo) -> ResolveForeignCallResult { + fn send_foreign_call( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, jsonrpsee::core::ClientError> + where + F: AcirField + Serialize + for<'a> Deserialize<'a>, + { let params = ResolveForeignCallRequest { session_id: self.id, function_call: foreign_call.clone(), @@ -108,11 +99,46 @@ where package_name: self.package_name.clone().or(Some(String::new())), }; let encoded_params = rpc_params!(params); - let parsed_response = self.runtime.block_on(async { + self.runtime.block_on(async { self.external_resolver.request("resolve_foreign_call", encoded_params).await - })?; + }) + } +} - Ok(parsed_response) +fn build_http_client(resolver_url: &str) -> HttpClient { + let mut client_builder = HttpClientBuilder::new(); + + if let Some(Ok(timeout)) = + std::env::var("NARGO_FOREIGN_CALL_TIMEOUT").ok().map(|timeout| timeout.parse()) + { + let timeout_duration = std::time::Duration::from_millis(timeout); + client_builder = client_builder.request_timeout(timeout_duration); + }; + + client_builder.build(resolver_url).expect("Invalid oracle resolver URL") +} + +impl ForeignCallExecutor for RPCForeignCallExecutor +where + F: AcirField + Serialize + for<'a> Deserialize<'a>, +{ + /// Execute an async call blocking the current thread. + /// This method cannot be called from inside a `tokio` runtime, for that to work + /// we need to offload the execution into a different thread; see the tests. + fn execute(&mut self, foreign_call: &ForeignCallWaitInfo) -> ResolveForeignCallResult { + let result = self.send_foreign_call(foreign_call); + + match result { + Ok(parsed_response) => Ok(parsed_response), + Err(jsonrpsee::core::ClientError::Transport(err)) => { + println!("We got a transport error: {err:?}"); + println!("Restarting http client..."); + self.external_resolver = build_http_client(&self.resolver_url); + let parsed_response = self.send_foreign_call(foreign_call)?; + Ok(parsed_response) + } + Err(other) => Err(ForeignCallError::from(other)), + } } } From ab1dbccdc2960ed08dc0aade18fdb89e65e0bdb6 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 24 Feb 2025 15:40:01 -0300 Subject: [PATCH 13/60] fix context.restart function - clean brillig_solver, witness_stack and acvm_stack fields as well --- tooling/debugger/src/context.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 6c5a93c6d6c..7fe98ddc8e6 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -490,7 +490,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { circuits, unconstrained_functions, acir_opcode_addresses, - initial_witness: initial_witness.clone(), // keeping it to be able to restart + initial_witness: initial_witness.clone(), // keeping it to be able to restart the context by itself acvm: initialize_acvm( &blackbox_solver, circuits, @@ -1085,24 +1085,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } fn restart(&mut self) { - // let breakpoints: Vec = self.iterate_breakpoints().copied().collect(); - // let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( - // PrintOutput::Stdout, - // self.context.get_foreign_call_resolver_url(), - // self.debug_artifact, - // )); - // self.context = DebugContext::new( - // self.blackbox_solver, - // self.circuits, - // self.debug_artifact, - // self.initial_witness.clone(), - // foreign_call_executor, - // self.unconstrained_functions, - // ); - // for debug_location in breakpoints { - // self.context.add_breakpoint(debug_location); - // } + // restart everything that's progress related + // by assigning the initial values self.current_circuit_id = 0; + self.brillig_solver = None; + self.witness_stack = WitnessStack::default(); + self.acvm_stack = vec![]; self.foreign_call_executor.restart(&self.debug_artifact); self.acvm = initialize_acvm( self.backend, From f66c650e142495702c83829b11c21d6bc9be00b8 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 24 Feb 2025 15:45:01 -0300 Subject: [PATCH 14/60] run cargo fmt --- tooling/debugger/src/repl.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index fd5f6f12742..904f53a69c6 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -112,7 +112,11 @@ impl<'a> ReplDebugger<'a> { panic!("Unwanted result") }; - print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); + print_source_code_location( + self.debug_artifact, + &locations, + self.raw_source_printing, + ); } } } From c44e4406f4754d2e9509ccf301fdd1607250f54c Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 24 Feb 2025 17:34:15 -0300 Subject: [PATCH 15/60] A bit of code cleanup and enhancements inspired in Gus' old comments --- tooling/debugger/src/context.rs | 29 +++++++------- tooling/nargo/src/errors.rs | 44 ++++++++++----------- tooling/nargo/src/ops/execute.rs | 4 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 53 +++++++++++--------------- 4 files changed, 58 insertions(+), 72 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 7fe98ddc8e6..89da06fb88a 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -13,7 +13,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::errors::{map_execution_error, ExecutionError, Location}; +use nargo::errors::{execution_error_from, ExecutionError, Location}; use nargo::NargoError; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; @@ -192,6 +192,15 @@ impl std::fmt::Display for DebugLocation { } } +impl Into for DebugLocation { + fn into(self) -> ResolvedOpcodeLocation { + ResolvedOpcodeLocation { + acir_function_index: usize::try_from(self.circuit_id).unwrap(), + opcode_location: self.opcode_location, + } + } +} + #[derive(Error, Debug)] pub enum DebugLocationFromStrError { #[error("Invalid debug location string: {0}")] @@ -751,21 +760,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.handle_foreign_call(foreign_call) } Err(err) => { - let resolved_call_stack: Vec = self - .get_call_stack() - .iter() - .map(|debug_loc| { - ResolvedOpcodeLocation { - acir_function_index: usize::try_from(debug_loc.circuit_id).unwrap(), // FIXME: is this ok? why circuit_id is u32? - opcode_location: debug_loc.opcode_location, - } - }) - .collect(); - - DebugCommandResult::Error(NargoError::ExecutionError(map_execution_error( + let error = execution_error_from( err, - &resolved_call_stack, - ))) + &self.get_call_stack().into_iter().map(|op| op.into()).collect(), + ); + DebugCommandResult::Error(NargoError::ExecutionError(error)) } } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 5d76fb12ba6..93a26345952 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -243,35 +243,31 @@ pub fn try_to_diagnose_runtime_error( Some(error.with_call_stack(source_locations)) } -pub fn map_execution_error( +pub fn execution_error_from( error: OpcodeResolutionError, call_stack: &Vec, ) -> ExecutionError { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { .. } - | OpcodeResolutionError::IndexOutOfBounds { .. } - | OpcodeResolutionError::InvalidInputBitSize { .. } - | OpcodeResolutionError::BrilligFunctionFailed { .. } => Some(call_stack.clone()), - _ => None, - }; - - let assertion_payload: Option> = match &error { - OpcodeResolutionError::BrilligFunctionFailed { payload, .. } - | OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => payload.clone(), - _ => None, - }; - - let brillig_function_id = match &error { - OpcodeResolutionError::BrilligFunctionFailed { function_id, .. } => Some(*function_id), - _ => None, + let (assertion_payload, brillig_function_id) = match &error { + OpcodeResolutionError::BrilligFunctionFailed { payload, function_id, .. } => { + (payload.clone(), Some(*function_id)) + } + OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => (payload.clone(), None), + _ => (None, None), }; return match assertion_payload { - Some(payload) => ExecutionError::AssertionFailed( - payload, - call_stack.expect("Should have call stack for an assertion failure"), - brillig_function_id, - ), - None => ExecutionError::SolvingError(error, call_stack), + Some(payload) => { + ExecutionError::AssertionFailed(payload, call_stack.clone(), brillig_function_id) + } + None => { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { .. } + | OpcodeResolutionError::IndexOutOfBounds { .. } + | OpcodeResolutionError::InvalidInputBitSize { .. } + | OpcodeResolutionError::BrilligFunctionFailed { .. } => Some(call_stack.clone()), + _ => None, + }; + ExecutionError::SolvingError(error, call_stack) + } }; } diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 49cd7c1209f..c54f2b84df5 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -8,7 +8,7 @@ use acvm::{AcirField, BlackBoxFunctionSolver}; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::NargoError; -use crate::errors::{map_execution_error, ExecutionError}; +use crate::errors::{execution_error_from, ExecutionError}; use crate::foreign_calls::ForeignCallExecutor; struct ProgramExecutor<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { @@ -117,7 +117,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> _ => (), }; - return Err(NargoError::ExecutionError(map_execution_error( + return Err(NargoError::ExecutionError(execution_error_from( error, &self.call_stack, ))); diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 79058254b40..0ff2e1ca4ad 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -81,7 +81,7 @@ pub(crate) struct DebugCommand { oracle_resolver: Option, } -struct ExecutionParams<'a>{ +struct RunParams<'a> { prover_name: String, witness_name: Option, target_dir: &'a Path, @@ -112,7 +112,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr let acir_mode = args.acir_mode; let skip_instrumentation = args.skip_instrumentation.unwrap_or(acir_mode); - let execution_params = ExecutionParams { + let run_params = RunParams { prover_name: args.prover_name, witness_name: args.witness_name, target_dir: &workspace.target_directory_path(), @@ -133,20 +133,10 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr let compile_options = compile_options_for_debugging(acir_mode, skip_instrumentation, args.compile_options); - let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); - if let Some(test_name) = args.test_name { - debug_test( - test_name, - package, - workspace, - compile_options, - execution_params, - expression_width, - ) + debug_test(test_name, package, workspace, compile_options, run_params) } else { - debug_main(package, workspace, compile_options, execution_params, expression_width) + debug_main(package, workspace, compile_options, run_params) } } @@ -240,13 +230,15 @@ fn debug_main( package: &Package, workspace: Workspace, compile_options: CompileOptions, - execution_params: ExecutionParams, - expression_width: ExpressionWidth, + run_params: RunParams, ) -> Result<(), CliError> { + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); + let compiled_program = compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; - run_async(package, compiled_program, &workspace, execution_params).map(|_| ()) + run_async(package, compiled_program, &workspace, run_params).map(|_| ()) } fn debug_test( @@ -254,8 +246,7 @@ fn debug_test( package: &Package, workspace: Workspace, compile_options: CompileOptions, - execution_params: ExecutionParams, - expression_width: ExpressionWidth, + run_params: RunParams, ) -> Result<(), CliError> { let package_name = package.name.to_string(); // let workspace_file_manager = build_workspace_file_manager(&workspace.root_dir, &workspace); @@ -278,6 +269,8 @@ fn debug_test( Ok(compiled_program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); @@ -285,7 +278,7 @@ fn debug_test( let debug = compiled_program.debug.clone(); // Debug test - let debug_result = run_async(package, compiled_program, &workspace, execution_params); + let debug_result = run_async(package, compiled_program, &workspace, run_params); match debug_result { Ok(result) => { @@ -383,15 +376,14 @@ fn run_async( package: &Package, program: CompiledProgram, workspace: &Workspace, - execution_params: ExecutionParams, + run_params: RunParams, ) -> Result { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let debug_result = - debug_program_and_decode(program, package, workspace, &execution_params)?; + let debug_result = debug_program_and_decode(program, package, workspace, &run_params)?; match debug_result { Ok((return_value, witness_stack)) => { @@ -402,11 +394,11 @@ fn run_async( println!("[{}] Circuit output: {return_value:?}", package.name); } - if let Some(witness_name) = execution_params.witness_name { + if let Some(witness_name) = run_params.witness_name { let witness_path = match save_witness_to_dir( &witness_stack, &witness_name, - &execution_params.target_dir, + run_params.target_dir, ) { Ok(path) => path, Err(err) => return Err(CliError::from(err)), @@ -427,17 +419,16 @@ fn debug_program_and_decode( program: CompiledProgram, package: &Package, workspace: &Workspace, - execution_params: &ExecutionParams, + run_params: &RunParams, ) -> Result { let program_abi = program.abi.clone(); - let initial_witness = - parse_initial_witness(package, &execution_params.prover_name, &program.abi)?; + let initial_witness = parse_initial_witness(package, &run_params.prover_name, &program.abi)?; let debug_result = debug_program( program, initial_witness, - execution_params.pedantic_solving, - execution_params.raw_source_printing, - execution_params.oracle_resolver_url.clone(), + run_params.pedantic_solving, + run_params.raw_source_printing, + run_params.oracle_resolver_url.clone(), Some(workspace.root_dir.clone()), package.name.to_string(), ); From 1dbb6668ddf954acf7cb5c5a70b94326d639e851 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 7 Jun 2024 10:14:10 -0600 Subject: [PATCH 16/60] Adapt breakpoint by sourcecode line to debugger in separate thread impl --- tooling/debugger/src/context.rs | 14 +++++++++++++- tooling/debugger/src/repl.rs | 13 ++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 89da06fb88a..49e924a2da1 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -298,6 +298,7 @@ pub(super) enum DebugCommandAPI { IsSolved, Restart, Finalize, + FindOpcodeAtCurrentFileLine(i64), // execution control StepAcirOpcode, StepIntoOpcode, @@ -429,6 +430,11 @@ pub(super) fn start_debugger<'a>( DebugCommandAPIResult::DebugCommandResult(context.next_out()) } DebugCommandAPI::Cont => DebugCommandAPIResult::DebugCommandResult(context.cont()), + DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { + DebugCommandAPIResult::DebugLocation( + context.find_opcode_at_current_file_line(line), + ) + } DebugCommandAPI::Finalize => { let witness_stack = context.finalize(); let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); @@ -635,6 +641,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { Some(found_location) } + pub(super) fn find_opcode_at_current_file_line(&self, line: i64) -> Option { + let file = self.get_current_file()?; + + self.find_opcode_for_source_location(&file, line) + } + /// Returns the callstack in source code locations for the currently /// executing opcode. This can be `None` if the execution finished (and /// `get_current_opcode_location()` returns `None`) or if the opcode is not @@ -650,7 +662,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } /// Returns the `FileId` of the file associated with the innermost function on the call stack. - pub(super) fn get_current_file(&mut self) -> Option { + fn get_current_file(&self) -> Option { self.get_current_source_location() .and_then(|locations| locations.last().map(|location| location.file)) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 904f53a69c6..c7044ca26f1 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -202,6 +202,13 @@ impl<'a> ReplDebugger<'a> { }; witness_map } + fn find_opcode_at_current_file_line(&self, line_number: i64) -> Option { + let result = self.call_debugger(DebugCommandAPI::FindOpcodeAtCurrentFileLine(line_number)); + let DebugCommandAPIResult::DebugLocation(location) = result else { + panic!("Unwanted result") + }; + location + } fn finalize(self) -> WitnessStack { let result = self.call_debugger(DebugCommandAPI::Finalize); let DebugCommandAPIResult::WitnessStack(stack) = result else { panic!("Unwanted result") }; @@ -350,13 +357,9 @@ impl<'a> ReplDebugger<'a> { } fn add_breakpoint_at_line(&mut self, line_number: i64) { - let Some(current_file) = self.context.get_current_file() else { - println!("No current file."); - return; - }; let best_location = - self.context.find_opcode_for_source_location(¤t_file, line_number); + self.find_opcode_at_current_file_line(line_number); match best_location { Some(location) => { From 86ae83afd763728bdd153496b09e5406bb11f55e Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 25 Feb 2025 15:31:12 -0300 Subject: [PATCH 17/60] refactor: encapsulate debug-test logic into smaller functions --- tooling/debugger/src/repl.rs | 4 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 139 +++++++++++++++---------- 2 files changed, 83 insertions(+), 60 deletions(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index c7044ca26f1..12bb8f7b178 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -357,9 +357,7 @@ impl<'a> ReplDebugger<'a> { } fn add_breakpoint_at_line(&mut self, line_number: i64) { - - let best_location = - self.find_opcode_at_current_file_line(line_number); + let best_location = self.find_opcode_at_current_file_line(line_number); match best_location { Some(location) => { diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 0ff2e1ca4ad..8c9d2029da9 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -5,8 +5,6 @@ use std::time::Duration; use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::{WitnessMap, WitnessStack}; -use bn254_blackbox_solver::Bn254BlackBoxSolver; -use acvm::FieldElement; use clap::Args; use fm::FileManager; use nargo::constants::PROVER_INPUT_FILE; @@ -36,7 +34,6 @@ use noirc_frontend::hir::{Context, FunctionNameMatch, ParsedFiles}; use super::check_cmd::check_crate_and_report_errors; use super::compile_cmd::get_target_width; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::test_cmd::formatters::Formatter; use super::test_cmd::TestResult; use super::{LockType, WorkspaceCommand}; @@ -152,6 +149,68 @@ pub(crate) fn compile_options_for_debugging( } } +fn print_test_result(test_result: TestResult, file_manager: &FileManager) -> () { + let formatter: Box = Box::new(PrettyFormatter); + formatter + .test_end_sync(&test_result, 1, 1, file_manager, true, false, false) + .expect("Could not display test result"); +} + +fn debug_test_fn( + test: &TestDefinition, + context: &mut Context, + workspace: &Workspace, + package: &Package, + compile_options: CompileOptions, + run_params: RunParams, +) -> TestResult { + let compiled_program = compile_test_fn_for_debugging(&test, context, package, compile_options); + + let test_status = match compiled_program { + Ok(compiled_program) => { + let abi = compiled_program.abi.clone(); + let debug = compiled_program.debug.clone(); + + // Run debugger + let debug_result = run_async(package, compiled_program, workspace, run_params); + + match debug_result { + Ok(result) => { + test_status_program_compile_pass(&test.function, &abi, &debug, &result) + } + // Debugger failed + Err(error) => TestStatus::Fail { + message: format!("Debugger failed: {:?}", error), + error_diagnostic: None, + }, + } + } + Err(err) => test_status_program_compile_fail(err, &test.function), + }; + + TestResult::new( + test.name.clone(), + package.name.to_string(), + test_status, + String::new(), + Duration::from_secs(1), // FIXME: hardcoded value + ) +} + +fn compile_test_fn_for_debugging( + test_def: &TestDefinition, + context: &mut Context, + package: &Package, + compile_options: CompileOptions, +) -> Result { + let compiled_program = + compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + Ok(compiled_program) +} + pub(crate) fn compile_bin_package_for_debugging( workspace: &Workspace, package: &Package, @@ -248,73 +307,32 @@ fn debug_test( compile_options: CompileOptions, run_params: RunParams, ) -> Result<(), CliError> { - let package_name = package.name.to_string(); - // let workspace_file_manager = build_workspace_file_manager(&workspace.root_dir, &workspace); - // TODO: extract fileManager creation + insert files into single function build_workspace_file_manager - let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + let (file_manager, mut parsed_files) = load_workspace_files(&workspace); - let mut parsed_files = parse_all(&workspace_file_manager); let (mut context, crate_id) = - prepare_package_for_debug(&workspace_file_manager, &mut parsed_files, package, &workspace); + prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); + let test = get_test_function(crate_id, &context, &test_name)?; check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; - let (test_name, test_function) = get_test_function(crate_id, &context, &test_name)?; - - // TODO: see if we can replace with compile_bin_for_debugging - let compiled_program = - compile_no_check(&mut context, &compile_options, test_function.get_id(), None, false); - - let test_status = match compiled_program { - Ok(compiled_program) => { - // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, - // otherwise constraints involving these expressions will not error. - let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); - let compiled_program = - nargo::ops::transform_program(compiled_program, expression_width); - let abi = compiled_program.abi.clone(); - let debug = compiled_program.debug.clone(); - - // Debug test - let debug_result = run_async(package, compiled_program, &workspace, run_params); - - match debug_result { - Ok(result) => { - test_status_program_compile_pass(&test_function, &abi, &debug, &result) - } - // Debugger failed - Err(error) => TestStatus::Fail { - message: format!("Debugger failed: {:?}", error), - error_diagnostic: None, - }, - } - } - Err(err) => test_status_program_compile_fail(err, &test_function), - }; - let test_result = TestResult::new( - test_name, - package_name, - test_status, - String::new(), - Duration::from_secs(1), // FIXME: hardcoded value - ); - - let formatter: Box = Box::new(PrettyFormatter); - formatter - .test_end_sync(&test_result, 1, 1, &workspace_file_manager, true, false, false) - .expect("Could not display test result"); + let test_result = + debug_test_fn(&test, &mut context, &workspace, package, compile_options, run_params); + print_test_result(test_result, &file_manager); Ok(()) } +struct TestDefinition { + name: String, + function: TestFunction, +} + // TODO: move to nargo::ops and reuse in test_cmd? fn get_test_function( crate_id: CrateId, context: &Context, test_name: &str, -) -> Result<(String, TestFunction), CliError> { +) -> Result { // TODO: review Contains signature and check if its ok to send test_name as single element let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); @@ -349,9 +367,16 @@ fn get_test_function( if test_function_has_arguments { return Err(CliError::Generic(String::from("Cannot debug tests with arguments"))); } - Ok((test_name, test_function)) + Ok(TestDefinition { name: test_name, function: test_function }) } +fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { + let mut file_manager = file_manager_with_stdlib(Path::new("")); + insert_all_files_for_workspace_into_file_manager(&workspace, &mut file_manager); + + let parsed_files = parse_all(&file_manager); + (file_manager, parsed_files) +} pub(crate) fn prepare_package_for_debug<'a>( file_manager: &'a FileManager, parsed_files: &'a mut ParsedFiles, From caa6d15f1776dd12b9ba7b15d8920ba0689adea4 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 25 Feb 2025 16:20:22 -0300 Subject: [PATCH 18/60] extract runtime debugger and structs to separate file --- tooling/debugger/src/context.rs | 177 +---------------------------- tooling/debugger/src/debug.rs | 193 ++++++++++++++++++++++++++++++++ tooling/debugger/src/lib.rs | 1 + tooling/debugger/src/repl.rs | 3 +- 4 files changed, 197 insertions(+), 177 deletions(-) create mode 100644 tooling/debugger/src/debug.rs diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 49e924a2da1..3ef3a830692 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -10,7 +10,6 @@ use acvm::pwg::{ }; use acvm::{BlackBoxFunctionSolver, FieldElement}; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; use nargo::errors::{execution_error_from, ExecutionError, Location}; @@ -23,7 +22,6 @@ use thiserror::Error; use std::collections::BTreeMap; use std::collections::HashSet; -use std::sync::mpsc::{Receiver, Sender}; /// A Noir program is composed by /// `n` ACIR circuits @@ -262,52 +260,6 @@ impl From<&StackFrame<'_, F>> for DebugStackFrame { } } -#[derive(Debug)] -pub(super) enum DebugCommandAPIResult { - DebugCommandResult(DebugCommandResult), - DebugLocation(Option), - Opcodes(Vec>), - Locations(Vec), - DebugLocations(Vec), - Bool(bool), - WitnessMap(WitnessMap), - MemoryValue(Option>>), - Unit, - Variables(Vec>), - WitnessStack(WitnessStack), - Field(Option), -} - -#[derive(Debug)] -pub(super) enum DebugCommandAPI { - GetCurrentDebugLocation, - GetOpcodes, - GetOpcodesOfCircuit(u32), - GetSourceLocationForDebugLocation(DebugLocation), - GetCallStack, - IsBreakpointSet(DebugLocation), - IsValidDebugLocation(DebugLocation), - AddBreakpoint(DebugLocation), - DeleteBreakpoint(DebugLocation), - GetWitnessMap, - IsExecutingBrillig, - GetBrilligMemory, - WriteBrilligMemory(usize, FieldElement, BitSize), - OverwriteWitness(Witness, FieldElement), - GetVariables, - IsSolved, - Restart, - Finalize, - FindOpcodeAtCurrentFileLine(i64), - // execution control - StepAcirOpcode, - StepIntoOpcode, - NextInto, - NextOver, - NextOut, - Cont, -} - pub struct ExecutionFrame<'a, B: BlackBoxFunctionSolver> { circuit_id: u32, acvm: ACVM<'a, FieldElement, B>, @@ -335,133 +287,6 @@ pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { initial_witness: WitnessMap, } -pub(super) fn start_debugger<'a>( - command_rx: Receiver, - result_tx: Sender, - circuits: Vec>, - debug_artifact: &'a DebugArtifact, - initial_witness: WitnessMap, - foreign_call_executor: Box, - unconstrained_functions: Vec>, - pedantic_solving: bool, -) { - let blackbox_solver = Bn254BlackBoxSolver(pedantic_solving); - let mut context = DebugContext::new( - &blackbox_solver, - &circuits, - debug_artifact, - initial_witness.clone(), - foreign_call_executor, - &unconstrained_functions, - ); - - println!("Debugger ready for receiving messages.."); - loop { - // recv blocks until it receives message - if let Ok(received) = command_rx.recv() { - let result = match received { - DebugCommandAPI::GetCurrentDebugLocation => { - DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) - } - DebugCommandAPI::GetOpcodes => { - DebugCommandAPIResult::Opcodes(to_vec(context.get_opcodes())) - } - DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => DebugCommandAPIResult::Opcodes( - to_vec(context.get_opcodes_of_circuit(circuit_id)), - ), - DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { - DebugCommandAPIResult::Locations( - context.get_source_location_for_debug_location(&debug_location), - ) - } - DebugCommandAPI::GetCallStack => { - DebugCommandAPIResult::DebugLocations(context.get_call_stack()) - } - DebugCommandAPI::IsBreakpointSet(debug_location) => { - DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) - } - DebugCommandAPI::IsValidDebugLocation(debug_location) => { - DebugCommandAPIResult::Bool(context.is_valid_debug_location(&debug_location)) - } - DebugCommandAPI::AddBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) - } - DebugCommandAPI::DeleteBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) - } - DebugCommandAPI::Restart => { - context.restart(); - DebugCommandAPIResult::Unit - } - DebugCommandAPI::GetWitnessMap => { - DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) - } - DebugCommandAPI::IsExecutingBrillig => { - DebugCommandAPIResult::Bool(context.is_executing_brillig()) - } - DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( - context.get_brillig_memory().map(|values| values.to_vec()), - ), - DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { - context.write_brillig_memory(ptr, value, bit_size); - DebugCommandAPIResult::Unit - } - DebugCommandAPI::OverwriteWitness(witness, value) => { - DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) - } - - DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( - context.get_variables().iter().map(|var| DebugStackFrame::from(var)).collect(), - ), - DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), - DebugCommandAPI::StepAcirOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) - } - DebugCommandAPI::StepIntoOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) - } - DebugCommandAPI::NextInto => { - DebugCommandAPIResult::DebugCommandResult(context.next_into()) - } - DebugCommandAPI::NextOver => { - DebugCommandAPIResult::DebugCommandResult(context.next_over()) - } - DebugCommandAPI::NextOut => { - DebugCommandAPIResult::DebugCommandResult(context.next_out()) - } - DebugCommandAPI::Cont => DebugCommandAPIResult::DebugCommandResult(context.cont()), - DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { - DebugCommandAPIResult::DebugLocation( - context.find_opcode_at_current_file_line(line), - ) - } - DebugCommandAPI::Finalize => { - let witness_stack = context.finalize(); - let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); - // We need to stop the 'event loop' since `finalize` consumes the context - drop(result_tx); - drop(command_rx); - break; - } - }; - let Ok(()) = result_tx.send(result) else { - drop(result_tx); - drop(command_rx); - break; - }; - } else { - println!("Upstream channel closed. Terminating debugger"); - drop(result_tx); - drop(command_rx); - break; - } - } -} - -fn to_vec(opcodes: &[Opcode]) -> Vec> { - opcodes.iter().map(|op| op.clone()).collect() -} - fn initialize_acvm<'a, B: BlackBoxFunctionSolver>( backend: &'a B, circuits: &'a [Circuit], @@ -1095,7 +920,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.witness_stack } - fn restart(&mut self) { + pub(super) fn restart(&mut self) { // restart everything that's progress related // by assigning the initial values self.current_circuit_id = 0; diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs new file mode 100644 index 00000000000..5e59bcb06c5 --- /dev/null +++ b/tooling/debugger/src/debug.rs @@ -0,0 +1,193 @@ +use acvm::{ + acir::{ + brillig::BitSize, + circuit::{brillig::BrilligBytecode, Circuit, Opcode}, + native_types::{Witness, WitnessMap, WitnessStack}, + }, + brillig_vm::MemoryValue, + FieldElement, +}; +use bn254_blackbox_solver::Bn254BlackBoxSolver; +use nargo::errors::Location; +use noirc_artifacts::debug::DebugArtifact; +use std::sync::mpsc::{Receiver, Sender}; + +use crate::{ + context::{DebugCommandResult, DebugContext, DebugLocation, DebugStackFrame}, + foreign_calls::DebugForeignCallExecutor, +}; + +// TODO: revisit this mod name + +#[derive(Debug)] +pub(super) enum DebugCommandAPIResult { + DebugCommandResult(DebugCommandResult), + DebugLocation(Option), + Opcodes(Vec>), + Locations(Vec), + DebugLocations(Vec), + Bool(bool), + WitnessMap(WitnessMap), + MemoryValue(Option>>), + Unit, + Variables(Vec>), + WitnessStack(WitnessStack), + Field(Option), +} + +#[derive(Debug)] +pub(super) enum DebugCommandAPI { + GetCurrentDebugLocation, + GetOpcodes, + GetOpcodesOfCircuit(u32), + GetSourceLocationForDebugLocation(DebugLocation), + GetCallStack, + IsBreakpointSet(DebugLocation), + IsValidDebugLocation(DebugLocation), + AddBreakpoint(DebugLocation), + DeleteBreakpoint(DebugLocation), + GetWitnessMap, + IsExecutingBrillig, + GetBrilligMemory, + WriteBrilligMemory(usize, FieldElement, BitSize), + OverwriteWitness(Witness, FieldElement), + GetVariables, + IsSolved, + Restart, + Finalize, + FindOpcodeAtCurrentFileLine(i64), + // execution control + StepAcirOpcode, + StepIntoOpcode, + NextInto, + NextOver, + NextOut, + Cont, +} + +pub(super) fn start_debugger<'a>( + command_rx: Receiver, + result_tx: Sender, + circuits: Vec>, + debug_artifact: &'a DebugArtifact, + initial_witness: WitnessMap, + foreign_call_executor: Box, + unconstrained_functions: Vec>, + pedantic_solving: bool, +) { + let blackbox_solver = Bn254BlackBoxSolver(pedantic_solving); + let mut context = DebugContext::new( + &blackbox_solver, + &circuits, + debug_artifact, + initial_witness.clone(), + foreign_call_executor, + &unconstrained_functions, + ); + + println!("Debugger ready for receiving messages.."); + loop { + // recv blocks until it receives message + if let Ok(received) = command_rx.recv() { + let result = match received { + DebugCommandAPI::GetCurrentDebugLocation => { + DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) + } + DebugCommandAPI::GetOpcodes => { + DebugCommandAPIResult::Opcodes(to_vec(context.get_opcodes())) + } + DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => DebugCommandAPIResult::Opcodes( + to_vec(context.get_opcodes_of_circuit(circuit_id)), + ), + DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { + DebugCommandAPIResult::Locations( + context.get_source_location_for_debug_location(&debug_location), + ) + } + DebugCommandAPI::GetCallStack => { + DebugCommandAPIResult::DebugLocations(context.get_call_stack()) + } + DebugCommandAPI::IsBreakpointSet(debug_location) => { + DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) + } + DebugCommandAPI::IsValidDebugLocation(debug_location) => { + DebugCommandAPIResult::Bool(context.is_valid_debug_location(&debug_location)) + } + DebugCommandAPI::AddBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) + } + DebugCommandAPI::DeleteBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) + } + DebugCommandAPI::Restart => { + context.restart(); + DebugCommandAPIResult::Unit + } + DebugCommandAPI::GetWitnessMap => { + DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) + } + DebugCommandAPI::IsExecutingBrillig => { + DebugCommandAPIResult::Bool(context.is_executing_brillig()) + } + DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( + context.get_brillig_memory().map(|values| values.to_vec()), + ), + DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { + context.write_brillig_memory(ptr, value, bit_size); + DebugCommandAPIResult::Unit + } + DebugCommandAPI::OverwriteWitness(witness, value) => { + DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) + } + + DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( + context.get_variables().iter().map(|var| DebugStackFrame::from(var)).collect(), + ), + DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), + DebugCommandAPI::StepAcirOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) + } + DebugCommandAPI::StepIntoOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) + } + DebugCommandAPI::NextInto => { + DebugCommandAPIResult::DebugCommandResult(context.next_into()) + } + DebugCommandAPI::NextOver => { + DebugCommandAPIResult::DebugCommandResult(context.next_over()) + } + DebugCommandAPI::NextOut => { + DebugCommandAPIResult::DebugCommandResult(context.next_out()) + } + DebugCommandAPI::Cont => DebugCommandAPIResult::DebugCommandResult(context.cont()), + DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { + DebugCommandAPIResult::DebugLocation( + context.find_opcode_at_current_file_line(line), + ) + } + DebugCommandAPI::Finalize => { + let witness_stack = context.finalize(); + let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); + // We need to stop the 'event loop' since `finalize` consumes the context + drop(result_tx); + drop(command_rx); + break; + } + }; + let Ok(()) = result_tx.send(result) else { + drop(result_tx); + drop(command_rx); + break; + }; + } else { + println!("Upstream channel closed. Terminating debugger"); + drop(result_tx); + drop(command_rx); + break; + } + } +} + +fn to_vec(opcodes: &[Opcode]) -> Vec> { + opcodes.iter().map(|op| op.clone()).collect() +} diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 33679e234ef..09cbdb25353 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -1,5 +1,6 @@ mod context; mod dap; +mod debug; pub mod errors; mod foreign_calls; mod repl; diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 12bb8f7b178..fdf9287b965 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,7 +1,8 @@ use crate::context::{ - start_debugger, DebugCommandAPI, DebugCommandAPIResult, DebugCommandResult, DebugLocation, + DebugCommandResult, DebugLocation, DebugStackFrame, }; +use crate::debug::{start_debugger, DebugCommandAPI, DebugCommandAPIResult}; use acvm::AcirField; use acvm::acir::brillig::BitSize; From d1655dfec675721b2f5d4feba934ba9b480ef750 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 25 Feb 2025 16:45:47 -0300 Subject: [PATCH 19/60] fix debugger panic --- tooling/nargo_cli/src/cli/debug_cmd.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 8c9d2029da9..6d66724deed 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -311,10 +311,11 @@ fn debug_test( let (mut context, crate_id) = prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); - let test = get_test_function(crate_id, &context, &test_name)?; - + check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; + let test= get_test_function(crate_id, &context, &test_name)?; + let test_result = debug_test_fn(&test, &mut context, &workspace, package, compile_options, run_params); print_test_result(test_result, &file_manager); From e7c9e30f90d7bbcaf4819eb4762c79d78fb48b64 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 25 Feb 2025 17:43:48 -0300 Subject: [PATCH 20/60] get rid of nested Results --- tooling/debugger/src/repl.rs | 5 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 68 +++++++++++--------------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index fdf9287b965..bb1f57add82 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,7 +1,4 @@ -use crate::context::{ - DebugCommandResult, DebugLocation, - DebugStackFrame, -}; +use crate::context::{DebugCommandResult, DebugLocation, DebugStackFrame}; use crate::debug::{start_debugger, DebugCommandAPI, DebugCommandAPIResult}; use acvm::AcirField; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 6d66724deed..b3becbbcf96 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -176,11 +176,15 @@ fn debug_test_fn( match debug_result { Ok(result) => { - test_status_program_compile_pass(&test.function, &abi, &debug, &result) + test_status_program_compile_pass(&test.function, &abi, &debug, &Ok(result)) } - // Debugger failed + // Test execution failed + Err(CliError::NargoError(error)) => { + test_status_program_compile_pass(&test.function, &abi, &debug, &Err(error)) + } + // Other errors Err(error) => TestStatus::Fail { - message: format!("Debugger failed: {:?}", error), + message: format!("Debugger failed: {error}"), error_diagnostic: None, }, } @@ -311,10 +315,10 @@ fn debug_test( let (mut context, crate_id) = prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); - + check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; - let test= get_test_function(crate_id, &context, &test_name)?; + let test = get_test_function(crate_id, &context, &test_name)?; let test_result = debug_test_fn(&test, &mut context, &workspace, package, compile_options, run_params); @@ -394,16 +398,15 @@ pub(crate) fn prepare_package_for_debug<'a>( (context, crate_id) } -type DebugResult = Result, NargoError>; -// FIXME: You shouldn't need this. CliError already has a variant which transparently can carry a NargoError. -type ExecutionResult = - Result<(Option, WitnessStack), NargoError>; +// FIXME: find a better name +type ExecutionResult = (Option, WitnessStack); + fn run_async( package: &Package, program: CompiledProgram, workspace: &Workspace, run_params: RunParams, -) -> Result { +) -> Result, CliError> { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); @@ -412,7 +415,7 @@ fn run_async( let debug_result = debug_program_and_decode(program, package, workspace, &run_params)?; match debug_result { - Ok((return_value, witness_stack)) => { + (return_value, witness_stack) => { let witness_stack_result = witness_stack.clone(); println!("[{}] Circuit witness successfully solved", package.name); @@ -421,26 +424,16 @@ fn run_async( } if let Some(witness_name) = run_params.witness_name { - let witness_path = match save_witness_to_dir( - &witness_stack, - &witness_name, - run_params.target_dir, - ) { - Ok(path) => path, - Err(err) => return Err(CliError::from(err)), - }; - + let witness_path = + save_witness_to_dir(&witness_stack, &witness_name, run_params.target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } - Ok(Ok(witness_stack_result)) + Ok(witness_stack_result) } - Err(error) => Ok(Err(error)), } }) } -// FIXME: We have nested results to differentiate between the execution result (the inner one - Nargo) -// and setting up the debugger errors (outer one - CliErrors) fn debug_program_and_decode( program: CompiledProgram, package: &Package, @@ -449,7 +442,7 @@ fn debug_program_and_decode( ) -> Result { let program_abi = program.abi.clone(); let initial_witness = parse_initial_witness(package, &run_params.prover_name, &program.abi)?; - let debug_result = debug_program( + let witness_stack = debug_program( program, initial_witness, run_params.pedantic_solving, @@ -457,20 +450,17 @@ fn debug_program_and_decode( run_params.oracle_resolver_url.clone(), Some(workspace.root_dir.clone()), package.name.to_string(), - ); - match debug_result { - Ok(witness_stack) => match witness_stack { - Some(witness_stack) => { - let main_witness = &witness_stack - .peek() - .expect("Should have at least one witness on the stack") - .witness; - let (_, return_value) = program_abi.decode(main_witness)?; - Ok(Ok((return_value, witness_stack))) - } - None => Err(CliError::ExecutionHalted), - }, - Err(error) => Ok(Err(error)), + )?; + match witness_stack { + Some(witness_stack) => { + let main_witness = &witness_stack + .peek() + .expect("Should have at least one witness on the stack") + .witness; + let (_, return_value) = program_abi.decode(main_witness)?; + Ok((return_value, witness_stack)) + } + None => Err(CliError::ExecutionHalted), } } From 00b83b616ea75301eb74ec1657e97b196ece9f6d Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 25 Feb 2025 19:32:12 -0300 Subject: [PATCH 21/60] abstract extracting value from enum with extract! macro --- tooling/debugger/src/debug.rs | 7 +- tooling/debugger/src/repl.rs | 122 +++++++++++++++++++--------------- 2 files changed, 73 insertions(+), 56 deletions(-) diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs index 5e59bcb06c5..3a1cba8d7a1 100644 --- a/tooling/debugger/src/debug.rs +++ b/tooling/debugger/src/debug.rs @@ -29,12 +29,13 @@ pub(super) enum DebugCommandAPIResult { Bool(bool), WitnessMap(WitnessMap), MemoryValue(Option>>), - Unit, + Unit(()), Variables(Vec>), WitnessStack(WitnessStack), Field(Option), } + #[derive(Debug)] pub(super) enum DebugCommandAPI { GetCurrentDebugLocation, @@ -121,7 +122,7 @@ pub(super) fn start_debugger<'a>( } DebugCommandAPI::Restart => { context.restart(); - DebugCommandAPIResult::Unit + DebugCommandAPIResult::Unit(()) } DebugCommandAPI::GetWitnessMap => { DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) @@ -134,7 +135,7 @@ pub(super) fn start_debugger<'a>( ), DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { context.write_brillig_memory(ptr, value, bit_size); - DebugCommandAPIResult::Unit + DebugCommandAPIResult::Unit(()) } DebugCommandAPI::OverwriteWitness(witness, value) => { DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index bb1f57add82..b504e264ab4 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -44,6 +44,15 @@ pub struct ReplDebugger<'a> { raw_source_printing: bool, } +macro_rules! extract { + ($e:expr, $p:path) => { + match $e { + $p(value) => Some(value), + _ => None, + } + }; +} + impl<'a> ReplDebugger<'a> { pub fn new( circuits: &'a [Circuit], @@ -120,32 +129,28 @@ impl<'a> ReplDebugger<'a> { } fn send_execution_control_command(&self, command: DebugCommandAPI) -> DebugCommandResult { - let result = self.call_debugger(command); - let DebugCommandAPIResult::DebugCommandResult(command_result) = result else { - panic!("Unwanted result") - }; - command_result + extract!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult).unwrap() } // TODO: find a better name fn send_bool_command(&self, command: DebugCommandAPI) -> bool { - let result = self.call_debugger(command); - let DebugCommandAPIResult::Bool(status) = result else { panic!("Unwanted result") }; - status + extract!(self.call_debugger(command), DebugCommandAPIResult::Bool).unwrap() } fn get_opcodes_of_circuit(&self, circuit_id: u32) -> Vec> { - let result = self.call_debugger(DebugCommandAPI::GetOpcodesOfCircuit(circuit_id)); - let DebugCommandAPIResult::Opcodes(opcodes) = result else { panic!("Unwanted result") }; - opcodes + extract!( + self.call_debugger(DebugCommandAPI::GetOpcodesOfCircuit(circuit_id)), + DebugCommandAPIResult::Opcodes + ) + .unwrap() } fn get_current_debug_location(&self) -> Option { - let result = self.call_debugger(DebugCommandAPI::GetCurrentDebugLocation); - let DebugCommandAPIResult::DebugLocation(location) = result else { - panic!("Unwanted result") - }; - location + extract!( + self.call_debugger(DebugCommandAPI::GetCurrentDebugLocation), + DebugCommandAPIResult::DebugLocation + ) + .unwrap() } fn is_breakpoint_set(&self, debug_location: DebugLocation) -> bool { @@ -168,20 +173,26 @@ impl<'a> ReplDebugger<'a> { self.send_bool_command(DebugCommandAPI::IsExecutingBrillig) } fn get_brillig_memory(&self) -> Option>> { - let result = self.call_debugger(DebugCommandAPI::GetBrilligMemory); - let DebugCommandAPIResult::MemoryValue(mem) = result else { panic!("Unwanted result") }; - mem + extract!( + self.call_debugger(DebugCommandAPI::GetBrilligMemory), + DebugCommandAPIResult::MemoryValue + ) + .unwrap() } fn get_variables(&self) -> Vec> { - let result = self.call_debugger(DebugCommandAPI::GetVariables); - let DebugCommandAPIResult::Variables(vars) = result else { panic!("Unwanted result") }; - vars + extract!( + self.call_debugger(DebugCommandAPI::GetVariables), + DebugCommandAPIResult::Variables + ) + .unwrap() } fn overwrite_witness(&self, witness: Witness, value: FieldElement) -> Option { - let result = self.call_debugger(DebugCommandAPI::OverwriteWitness(witness, value)); - let DebugCommandAPIResult::Field(field) = result else { panic!("Unwanted result") }; - field + extract!( + self.call_debugger(DebugCommandAPI::OverwriteWitness(witness, value)), + DebugCommandAPIResult::Field + ) + .unwrap() } fn is_solved(&self) -> bool { @@ -189,33 +200,33 @@ impl<'a> ReplDebugger<'a> { } fn restart_debugger(&self) { - let result = self.call_debugger(DebugCommandAPI::Restart); - let DebugCommandAPIResult::Unit = result else { panic!("Unwanted result") }; + extract!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit).unwrap() } fn get_witness_map(&self) -> WitnessMap { - let result = self.call_debugger(DebugCommandAPI::GetWitnessMap); - let DebugCommandAPIResult::WitnessMap(witness_map) = result else { - panic!("Unwanted result") - }; - witness_map + extract!( + self.call_debugger(DebugCommandAPI::GetWitnessMap), + DebugCommandAPIResult::WitnessMap + ) + .unwrap() } fn find_opcode_at_current_file_line(&self, line_number: i64) -> Option { - let result = self.call_debugger(DebugCommandAPI::FindOpcodeAtCurrentFileLine(line_number)); - let DebugCommandAPIResult::DebugLocation(location) = result else { - panic!("Unwanted result") - }; - location + extract!( + self.call_debugger(DebugCommandAPI::FindOpcodeAtCurrentFileLine(line_number)), + DebugCommandAPIResult::DebugLocation + ) + .unwrap() } fn finalize(self) -> WitnessStack { - let result = self.call_debugger(DebugCommandAPI::Finalize); - let DebugCommandAPIResult::WitnessStack(stack) = result else { panic!("Unwanted result") }; - stack + extract!(self.call_debugger(DebugCommandAPI::Finalize), DebugCommandAPIResult::WitnessStack) + .unwrap() } - fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { - let result = self.call_debugger(DebugCommandAPI::GetOpcodes); - let DebugCommandAPIResult::Opcodes(opcodes) = result else { panic!("Unwanted result") }; + let opcodes = extract!( + self.call_debugger(DebugCommandAPI::GetOpcodes), + DebugCommandAPIResult::Opcodes + ) + .unwrap(); match &debug_location.opcode_location { OpcodeLocation::Acir(instruction_pointer) => { println!( @@ -237,19 +248,22 @@ impl<'a> ReplDebugger<'a> { } } // todo: should we clone the debug_location so it can be sent? - let result = - self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(*debug_location)); - let DebugCommandAPIResult::Locations(locations) = result else { panic!("Unwanted result") }; + let locations = extract!( + self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(*debug_location)), + DebugCommandAPIResult::Locations + ) + .unwrap(); print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } pub fn show_current_call_stack(&self) { // let call_stack = self.context.get_ca - let result = self.call_debugger(DebugCommandAPI::GetCallStack); - let DebugCommandAPIResult::DebugLocations(call_stack) = result else { - panic!("Unwanted result") - }; + let call_stack = extract!( + self.call_debugger(DebugCommandAPI::GetCallStack), + DebugCommandAPIResult::DebugLocations + ) + .unwrap(); if call_stack.is_empty() { println!("Finished execution. Call stack empty."); @@ -518,9 +532,11 @@ impl<'a> ReplDebugger<'a> { println!("Not executing a Brillig block"); return; } - let result = - self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, field_value, bit_size)); - let DebugCommandAPIResult::Unit = result else { panic!("Unwanted result") }; + extract!( + self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, field_value, bit_size)), + DebugCommandAPIResult::Unit + ) + .unwrap(); } pub fn show_vars(&self) { From 14977fd0258c03141b06eb5f796e66ba7957c4ed Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 26 Feb 2025 14:32:24 -0300 Subject: [PATCH 22/60] some more code cleanup --- tooling/debugger/src/repl.rs | 98 +++++++++++--------------- tooling/nargo/src/foreign_calls/rpc.rs | 15 ++-- tooling/nargo_cli/src/cli/debug_cmd.rs | 33 ++++----- 3 files changed, 62 insertions(+), 84 deletions(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index b504e264ab4..cce7d7c0516 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -25,12 +25,9 @@ use std::thread; use crate::source_code_printer::print_source_code_location; pub struct ReplDebugger<'a> { - // context: DebugContext<'a, B>, command_sender: Sender, result_receiver: Receiver, - // blackbox_solver: &'a B, debug_artifact: &'a DebugArtifact, - // initial_witness: WitnessMap, last_result: DebugCommandResult, // ACIR functions to debug @@ -44,7 +41,7 @@ pub struct ReplDebugger<'a> { raw_source_printing: bool, } -macro_rules! extract { +macro_rules! extract_value { ($e:expr, $p:path) => { match $e { $p(value) => Some(value), @@ -75,15 +72,14 @@ impl<'a> ReplDebugger<'a> { } } - // FIXME: this probably reads better with match expression fn call_debugger(&self, command: DebugCommandAPI) -> DebugCommandAPIResult { - if let Ok(()) = self.command_sender.send(command) { - let Ok(result) = self.result_receiver.recv() else { - panic!("Debugger closed connection unexpectedly"); - }; - result - } else { - panic!("Could not communicate with debugger") + // using match here instead of unwrap() to give more useful panic messages + match self.command_sender.send(command) { + Ok(_) => match self.result_receiver.recv() { + Ok(result) => result, + Err(_) => panic!("Debugger closed connection unexpectedly"), + }, + Err(_) => panic!("Could not communicate with debugger"), } } @@ -113,11 +109,7 @@ impl<'a> ReplDebugger<'a> { ); } } - let result = self - .call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(location)); - let DebugCommandAPIResult::Locations(locations) = result else { - panic!("Unwanted result") - }; + let locations = extract_value!(self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(location)), DebugCommandAPIResult::Locations).unwrap(); print_source_code_location( self.debug_artifact, @@ -129,16 +121,16 @@ impl<'a> ReplDebugger<'a> { } fn send_execution_control_command(&self, command: DebugCommandAPI) -> DebugCommandResult { - extract!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult).unwrap() + extract_value!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult).unwrap() } // TODO: find a better name fn send_bool_command(&self, command: DebugCommandAPI) -> bool { - extract!(self.call_debugger(command), DebugCommandAPIResult::Bool).unwrap() + extract_value!(self.call_debugger(command), DebugCommandAPIResult::Bool).unwrap() } fn get_opcodes_of_circuit(&self, circuit_id: u32) -> Vec> { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::GetOpcodesOfCircuit(circuit_id)), DebugCommandAPIResult::Opcodes ) @@ -146,7 +138,7 @@ impl<'a> ReplDebugger<'a> { } fn get_current_debug_location(&self) -> Option { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::GetCurrentDebugLocation), DebugCommandAPIResult::DebugLocation ) @@ -173,14 +165,14 @@ impl<'a> ReplDebugger<'a> { self.send_bool_command(DebugCommandAPI::IsExecutingBrillig) } fn get_brillig_memory(&self) -> Option>> { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::GetBrilligMemory), DebugCommandAPIResult::MemoryValue ) .unwrap() } fn get_variables(&self) -> Vec> { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::GetVariables), DebugCommandAPIResult::Variables ) @@ -188,7 +180,7 @@ impl<'a> ReplDebugger<'a> { } fn overwrite_witness(&self, witness: Witness, value: FieldElement) -> Option { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::OverwriteWitness(witness, value)), DebugCommandAPIResult::Field ) @@ -200,29 +192,29 @@ impl<'a> ReplDebugger<'a> { } fn restart_debugger(&self) { - extract!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit).unwrap() + extract_value!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit).unwrap() } fn get_witness_map(&self) -> WitnessMap { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::GetWitnessMap), DebugCommandAPIResult::WitnessMap ) .unwrap() } fn find_opcode_at_current_file_line(&self, line_number: i64) -> Option { - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::FindOpcodeAtCurrentFileLine(line_number)), DebugCommandAPIResult::DebugLocation ) .unwrap() } fn finalize(self) -> WitnessStack { - extract!(self.call_debugger(DebugCommandAPI::Finalize), DebugCommandAPIResult::WitnessStack) + extract_value!(self.call_debugger(DebugCommandAPI::Finalize), DebugCommandAPIResult::WitnessStack) .unwrap() } fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { - let opcodes = extract!( + let opcodes = extract_value!( self.call_debugger(DebugCommandAPI::GetOpcodes), DebugCommandAPIResult::Opcodes ) @@ -247,8 +239,7 @@ impl<'a> ReplDebugger<'a> { ); } } - // todo: should we clone the debug_location so it can be sent? - let locations = extract!( + let locations = extract_value!( self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(*debug_location)), DebugCommandAPIResult::Locations ) @@ -258,8 +249,7 @@ impl<'a> ReplDebugger<'a> { } pub fn show_current_call_stack(&self) { - // let call_stack = self.context.get_ca - let call_stack = extract!( + let call_stack = extract_value!( self.call_debugger(DebugCommandAPI::GetCallStack), DebugCommandAPIResult::DebugLocations ) @@ -291,7 +281,7 @@ impl<'a> ReplDebugger<'a> { } }); let opcodes = self.get_opcodes_of_circuit(circuit_id); - let current_acir_index: Option = match current_opcode_location { + let current_acir_index = match current_opcode_location { Some(OpcodeLocation::Acir(ip)) => Some(ip), Some(OpcodeLocation::Brillig { acir_index, .. }) => Some(acir_index), None => None, @@ -417,47 +407,39 @@ impl<'a> ReplDebugger<'a> { self.show_current_vm_status(); } - fn step_acir_opcode(&mut self) { + + fn execution_control_command(&mut self, command: DebugCommandAPI) { if self.validate_in_progress() { - let result = self.send_execution_control_command(DebugCommandAPI::StepAcirOpcode); + if let DebugCommandAPI::Cont = command { + println!("(Continuing execution...)"); + } + let result = self.send_execution_control_command(command); self.handle_debug_command_result(result); } } + fn step_acir_opcode(&mut self) { + self.execution_control_command(DebugCommandAPI::StepAcirOpcode); + } + fn step_into_opcode(&mut self) { - if self.validate_in_progress() { - let result = self.send_execution_control_command(DebugCommandAPI::StepIntoOpcode); - self.handle_debug_command_result(result); - } + self.execution_control_command(DebugCommandAPI::StepIntoOpcode); } fn next_into(&mut self) { - if self.validate_in_progress() { - let result = self.send_execution_control_command(DebugCommandAPI::NextInto); - self.handle_debug_command_result(result); - } + self.execution_control_command(DebugCommandAPI::NextInto); } fn next_over(&mut self) { - if self.validate_in_progress() { - let result = self.send_execution_control_command(DebugCommandAPI::NextOver); - self.handle_debug_command_result(result); - } + self.execution_control_command(DebugCommandAPI::NextOver); } fn next_out(&mut self) { - if self.validate_in_progress() { - let result = self.send_execution_control_command(DebugCommandAPI::NextOut); - self.handle_debug_command_result(result); - } + self.execution_control_command(DebugCommandAPI::NextOut); } fn cont(&mut self) { - if self.validate_in_progress() { - println!("(Continuing execution...)"); - let result = self.send_execution_control_command(DebugCommandAPI::Cont); - self.handle_debug_command_result(result); - } + self.execution_control_command(DebugCommandAPI::Cont); } fn restart_session(&mut self) { @@ -532,7 +514,7 @@ impl<'a> ReplDebugger<'a> { println!("Not executing a Brillig block"); return; } - extract!( + extract_value!( self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, field_value, bit_size)), DebugCommandAPIResult::Unit ) diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index 2a92f2a32c7..c6b3d50bb20 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -19,8 +19,8 @@ pub struct RPCForeignCallExecutor { id: u64, /// JSON RPC client to resolve foreign calls external_resolver: HttpClient, - - resolver_url: String, + /// External resolver target. We are keeping it to be able to restart httpClient if necessary + target_url: String, /// Root path to the program or workspace in execution. root_path: Option, /// Name of the package in execution @@ -73,7 +73,7 @@ impl RPCForeignCallExecutor { RPCForeignCallExecutor { external_resolver: oracle_resolver, - resolver_url: resolver_url.to_string(), + target_url: resolver_url.to_string(), id, root_path, package_name, @@ -105,7 +105,7 @@ impl RPCForeignCallExecutor { } } -fn build_http_client(resolver_url: &str) -> HttpClient { +fn build_http_client(target: &str) -> HttpClient { let mut client_builder = HttpClientBuilder::new(); if let Some(Ok(timeout)) = @@ -115,7 +115,7 @@ fn build_http_client(resolver_url: &str) -> HttpClient { client_builder = client_builder.request_timeout(timeout_duration); }; - client_builder.build(resolver_url).expect("Invalid oracle resolver URL") + client_builder.build(target).expect("Invalid oracle resolver URL") } impl ForeignCallExecutor for RPCForeignCallExecutor @@ -130,10 +130,13 @@ where match result { Ok(parsed_response) => Ok(parsed_response), + // TODO: This is a workaround + // The client is losing connection with the server and it's not being able to manage it + // so we are re-creating the HttpClient when it happens Err(jsonrpsee::core::ClientError::Transport(err)) => { println!("We got a transport error: {err:?}"); println!("Restarting http client..."); - self.external_resolver = build_http_client(&self.resolver_url); + self.external_resolver = build_http_client(&self.target_url); let parsed_response = self.send_foreign_call(foreign_call)?; Ok(parsed_response) } diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index b3becbbcf96..6b6af359a10 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -221,11 +221,7 @@ pub(crate) fn compile_bin_package_for_debugging( compile_options: &CompileOptions, expression_width: ExpressionWidth, ) -> Result { - // TODO: extract fileManager creation + insert files into single function build_workspace_file_manager - let mut workspace_file_manager: FileManager = file_manager_with_stdlib(Path::new("")); - insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); - - let mut parsed_files = parse_all(&workspace_file_manager); + let (workspace_file_manager, mut parsed_files) = load_workspace_files(workspace); let compilation_result = if compile_options.instrument_debug { let debug_state = @@ -412,25 +408,22 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let debug_result = debug_program_and_decode(program, package, workspace, &run_params)?; + let (return_value, witness_stack) = + debug_program_and_decode(program, package, workspace, &run_params)?; - match debug_result { - (return_value, witness_stack) => { - let witness_stack_result = witness_stack.clone(); - println!("[{}] Circuit witness successfully solved", package.name); + let witness_stack_result = witness_stack.clone(); + println!("[{}] Circuit witness successfully solved", package.name); - if let Some(return_value) = return_value { - println!("[{}] Circuit output: {return_value:?}", package.name); - } + if let Some(return_value) = return_value { + println!("[{}] Circuit output: {return_value:?}", package.name); + } - if let Some(witness_name) = run_params.witness_name { - let witness_path = - save_witness_to_dir(&witness_stack, &witness_name, run_params.target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - } - Ok(witness_stack_result) - } + if let Some(witness_name) = run_params.witness_name { + let witness_path = + save_witness_to_dir(&witness_stack, &witness_name, run_params.target_dir)?; + println!("[{}] Witness saved to {}", package.name, witness_path.display()); } + Ok(witness_stack_result) }) } From 0591747f158322049b190ef2f6307240cc1ecff5 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 26 Feb 2025 15:32:54 -0300 Subject: [PATCH 23/60] cargo fmt & clippy --- compiler/noirc_frontend/src/debug/mod.rs | 2 +- tooling/debugger/src/context.rs | 18 +++++++++------- tooling/debugger/src/debug.rs | 11 +++------- tooling/debugger/src/foreign_calls.rs | 12 +++++------ tooling/debugger/src/repl.rs | 26 ++++++++++++++++-------- tooling/nargo/src/errors.rs | 12 ++++++----- tooling/nargo_cli/src/cli/debug_cmd.rs | 10 ++++----- 7 files changed, 51 insertions(+), 40 deletions(-) diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 2635cc1f87d..b00ad36bdf1 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -1,7 +1,7 @@ use crate::ast::PathSegment; use crate::parse_program; -use crate::signed_field::SignedField; use crate::parser::{ParsedModule, ParsedSubModule}; +use crate::signed_field::SignedField; use crate::{ ast, ast::Path, diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 3ef3a830692..28fa6332bab 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -190,11 +190,11 @@ impl std::fmt::Display for DebugLocation { } } -impl Into for DebugLocation { - fn into(self) -> ResolvedOpcodeLocation { +impl From for ResolvedOpcodeLocation { + fn from(debug_loc: DebugLocation) -> Self { ResolvedOpcodeLocation { - acir_function_index: usize::try_from(self.circuit_id).unwrap(), - opcode_location: self.opcode_location, + acir_function_index: usize::try_from(debug_loc.circuit_id).unwrap(), + opcode_location: debug_loc.opcode_location, } } } @@ -332,7 +332,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_opcode_addresses, initial_witness: initial_witness.clone(), // keeping it to be able to restart the context by itself acvm: initialize_acvm( - &blackbox_solver, + blackbox_solver, circuits, initial_witness, unconstrained_functions, @@ -599,7 +599,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { Err(err) => { let error = execution_error_from( err, - &self.get_call_stack().into_iter().map(|op| op.into()).collect(), + &self + .get_call_stack() + .into_iter() + .map(|op| op.into()) + .collect::>(), ); DebugCommandResult::Error(NargoError::ExecutionError(error)) } @@ -927,7 +931,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.brillig_solver = None; self.witness_stack = WitnessStack::default(); self.acvm_stack = vec![]; - self.foreign_call_executor.restart(&self.debug_artifact); + self.foreign_call_executor.restart(self.debug_artifact); self.acvm = initialize_acvm( self.backend, self.circuits, diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs index 3a1cba8d7a1..8464ea52647 100644 --- a/tooling/debugger/src/debug.rs +++ b/tooling/debugger/src/debug.rs @@ -35,7 +35,6 @@ pub(super) enum DebugCommandAPIResult { Field(Option), } - #[derive(Debug)] pub(super) enum DebugCommandAPI { GetCurrentDebugLocation, @@ -95,10 +94,10 @@ pub(super) fn start_debugger<'a>( DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) } DebugCommandAPI::GetOpcodes => { - DebugCommandAPIResult::Opcodes(to_vec(context.get_opcodes())) + DebugCommandAPIResult::Opcodes(context.get_opcodes().to_owned()) } DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => DebugCommandAPIResult::Opcodes( - to_vec(context.get_opcodes_of_circuit(circuit_id)), + context.get_opcodes_of_circuit(circuit_id).to_owned(), ), DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { DebugCommandAPIResult::Locations( @@ -142,7 +141,7 @@ pub(super) fn start_debugger<'a>( } DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( - context.get_variables().iter().map(|var| DebugStackFrame::from(var)).collect(), + context.get_variables().iter().map(DebugStackFrame::from).collect(), ), DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), DebugCommandAPI::StepAcirOpcode => { @@ -188,7 +187,3 @@ pub(super) fn start_debugger<'a>( } } } - -fn to_vec(opcodes: &[Opcode]) -> Vec> { - opcodes.iter().map(|op| op.clone()).collect() -} diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index dd27174ab53..79d143c03f1 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -46,7 +46,7 @@ pub trait DebugForeignCallExecutor: ForeignCallExecutor { fn get_variables(&self) -> Vec>; fn current_stack_frame(&self) -> Option>; fn get_foreign_call_resolver_url(&self) -> Option; - fn restart(&mut self, artifact: &DebugArtifact) -> (); + fn restart(&mut self, artifact: &DebugArtifact); } #[derive(Default)] @@ -64,9 +64,9 @@ impl DefaultDebugForeignCallExecutor { package_name: String, ) -> impl DebugForeignCallExecutor + '_ { DefaultForeignCallBuilder { - output: output, + output, enable_mocks: true, - resolver_url: resolver_url, + resolver_url, root_path: root_path.clone(), package_name: Some(package_name), } @@ -131,7 +131,7 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { self.foreign_call_resolver_url.clone() } - fn restart(&mut self, artifact: &DebugArtifact) -> () { + fn restart(&mut self, artifact: &DebugArtifact) { self.debug_vars = DebugVars::default(); self.load_artifact(artifact); } @@ -240,7 +240,7 @@ where fn get_foreign_call_resolver_url(&self) -> Option { self.handler().get_foreign_call_resolver_url() } - fn restart(&mut self, artifact: &DebugArtifact) -> () { - (&mut self.handler).restart(artifact); + fn restart(&mut self, artifact: &DebugArtifact) { + self.handler.restart(artifact); } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index cce7d7c0516..67d6a353f4e 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -109,7 +109,13 @@ impl<'a> ReplDebugger<'a> { ); } } - let locations = extract_value!(self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(location)), DebugCommandAPIResult::Locations).unwrap(); + let locations = extract_value!( + self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation( + location + )), + DebugCommandAPIResult::Locations + ) + .unwrap(); print_source_code_location( self.debug_artifact, @@ -121,7 +127,8 @@ impl<'a> ReplDebugger<'a> { } fn send_execution_control_command(&self, command: DebugCommandAPI) -> DebugCommandResult { - extract_value!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult).unwrap() + extract_value!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult) + .unwrap() } // TODO: find a better name @@ -192,7 +199,8 @@ impl<'a> ReplDebugger<'a> { } fn restart_debugger(&self) { - extract_value!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit).unwrap() + extract_value!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit) + .unwrap() } fn get_witness_map(&self) -> WitnessMap { @@ -210,8 +218,11 @@ impl<'a> ReplDebugger<'a> { .unwrap() } fn finalize(self) -> WitnessStack { - extract_value!(self.call_debugger(DebugCommandAPI::Finalize), DebugCommandAPIResult::WitnessStack) - .unwrap() + extract_value!( + self.call_debugger(DebugCommandAPI::Finalize), + DebugCommandAPIResult::WitnessStack + ) + .unwrap() } fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { let opcodes = extract_value!( @@ -407,7 +418,6 @@ impl<'a> ReplDebugger<'a> { self.show_current_vm_status(); } - fn execution_control_command(&mut self, command: DebugCommandAPI) { if self.validate_in_progress() { if let DebugCommandAPI::Cont = command { @@ -418,8 +428,8 @@ impl<'a> ReplDebugger<'a> { } } - fn step_acir_opcode(&mut self) { - self.execution_control_command(DebugCommandAPI::StepAcirOpcode); + fn step_acir_opcode(&mut self) { + self.execution_control_command(DebugCommandAPI::StepAcirOpcode); } fn step_into_opcode(&mut self) { diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 93a26345952..59646198872 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -245,7 +245,7 @@ pub fn try_to_diagnose_runtime_error( pub fn execution_error_from( error: OpcodeResolutionError, - call_stack: &Vec, + call_stack: &[ResolvedOpcodeLocation], ) -> ExecutionError { let (assertion_payload, brillig_function_id) = match &error { OpcodeResolutionError::BrilligFunctionFailed { payload, function_id, .. } => { @@ -255,19 +255,21 @@ pub fn execution_error_from( _ => (None, None), }; - return match assertion_payload { + match assertion_payload { Some(payload) => { - ExecutionError::AssertionFailed(payload, call_stack.clone(), brillig_function_id) + ExecutionError::AssertionFailed(payload, call_stack.to_owned(), brillig_function_id) } None => { let call_stack = match &error { OpcodeResolutionError::UnsatisfiedConstrain { .. } | OpcodeResolutionError::IndexOutOfBounds { .. } | OpcodeResolutionError::InvalidInputBitSize { .. } - | OpcodeResolutionError::BrilligFunctionFailed { .. } => Some(call_stack.clone()), + | OpcodeResolutionError::BrilligFunctionFailed { .. } => { + Some(call_stack.to_owned()) + } _ => None, }; ExecutionError::SolvingError(error, call_stack) } - }; + } } diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 6b6af359a10..7be56b15a5d 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -149,7 +149,7 @@ pub(crate) fn compile_options_for_debugging( } } -fn print_test_result(test_result: TestResult, file_manager: &FileManager) -> () { +fn print_test_result(test_result: TestResult, file_manager: &FileManager) { let formatter: Box = Box::new(PrettyFormatter); formatter .test_end_sync(&test_result, 1, 1, file_manager, true, false, false) @@ -164,7 +164,7 @@ fn debug_test_fn( compile_options: CompileOptions, run_params: RunParams, ) -> TestResult { - let compiled_program = compile_test_fn_for_debugging(&test, context, package, compile_options); + let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options); let test_status = match compiled_program { Ok(compiled_program) => { @@ -232,7 +232,7 @@ pub(crate) fn compile_bin_package_for_debugging( &parsed_files, workspace, package, - &compile_options, + compile_options, None, debug_state, ) @@ -242,7 +242,7 @@ pub(crate) fn compile_bin_package_for_debugging( &parsed_files, workspace, package, - &compile_options, + compile_options, None, ) }; @@ -373,7 +373,7 @@ fn get_test_function( fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { let mut file_manager = file_manager_with_stdlib(Path::new("")); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut file_manager); + insert_all_files_for_workspace_into_file_manager(workspace, &mut file_manager); let parsed_files = parse_all(&file_manager); (file_manager, parsed_files) From ba139bdc35e78192fac50d06878265fb474419bc Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 26 Feb 2025 16:02:03 -0300 Subject: [PATCH 24/60] refactor to avoid exceeding # of function args --- tooling/debugger/src/dap.rs | 15 +-- tooling/debugger/src/debug.rs | 234 ++++++++++++++++++---------------- tooling/debugger/src/repl.rs | 16 +-- 3 files changed, 136 insertions(+), 129 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 709eb943338..e2a2156809d 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -2,8 +2,6 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; use std::path::PathBuf; -use acvm::acir::circuit::Circuit; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::PrintOutput; @@ -63,16 +61,15 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn new( server: Server, solver: &'a B, - circuits: &'a [Circuit], + program: &'a CompiledProgram, debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, - unconstrained_functions: &'a [BrilligBytecode], root_path: Option, package_name: String, ) -> Self { let context = DebugContext::new( solver, - circuits, + &program.program.functions, debug_artifact, initial_witness, Box::new(DefaultDebugForeignCallExecutor::from_artifact( @@ -82,7 +79,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< root_path, package_name, )), - unconstrained_functions, + &program.program.unconstrained_functions, ); Self { server, @@ -620,14 +617,14 @@ pub fn run_session>( root_path: Option, package_name: String, ) -> Result<(), ServerError> { - let debug_artifact = DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; + let debug_artifact = + DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; let mut session = DapSession::new( server, solver, - &program.program.functions, + &program, &debug_artifact, initial_witness, - &program.program.unconstrained_functions, root_path, package_name, ); diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs index 8464ea52647..273fe2aeb39 100644 --- a/tooling/debugger/src/debug.rs +++ b/tooling/debugger/src/debug.rs @@ -65,125 +65,137 @@ pub(super) enum DebugCommandAPI { Cont, } -pub(super) fn start_debugger<'a>( - command_rx: Receiver, - result_tx: Sender, - circuits: Vec>, - debug_artifact: &'a DebugArtifact, - initial_witness: WitnessMap, - foreign_call_executor: Box, - unconstrained_functions: Vec>, - pedantic_solving: bool, -) { - let blackbox_solver = Bn254BlackBoxSolver(pedantic_solving); - let mut context = DebugContext::new( - &blackbox_solver, - &circuits, - debug_artifact, - initial_witness.clone(), - foreign_call_executor, - &unconstrained_functions, - ); +pub struct Debugger<'a> { + pub circuits: Vec>, + pub debug_artifact: &'a DebugArtifact, + pub initial_witness: WitnessMap, + pub unconstrained_functions: Vec>, + pub pedantic_solving: bool, +} + +impl<'a> Debugger<'a> { + pub(super) fn start_debugging( + &self, + command_rx: Receiver, + result_tx: Sender, + foreign_call_executor: Box, + ) { + let blackbox_solver = Bn254BlackBoxSolver(self.pedantic_solving); + let mut context = DebugContext::new( + &blackbox_solver, + &self.circuits, + self.debug_artifact, + self.initial_witness.clone(), + foreign_call_executor, + &self.unconstrained_functions, + ); - println!("Debugger ready for receiving messages.."); - loop { - // recv blocks until it receives message - if let Ok(received) = command_rx.recv() { - let result = match received { - DebugCommandAPI::GetCurrentDebugLocation => { - DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) - } - DebugCommandAPI::GetOpcodes => { - DebugCommandAPIResult::Opcodes(context.get_opcodes().to_owned()) - } - DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => DebugCommandAPIResult::Opcodes( - context.get_opcodes_of_circuit(circuit_id).to_owned(), - ), - DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { - DebugCommandAPIResult::Locations( - context.get_source_location_for_debug_location(&debug_location), - ) - } - DebugCommandAPI::GetCallStack => { - DebugCommandAPIResult::DebugLocations(context.get_call_stack()) - } - DebugCommandAPI::IsBreakpointSet(debug_location) => { - DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) - } - DebugCommandAPI::IsValidDebugLocation(debug_location) => { - DebugCommandAPIResult::Bool(context.is_valid_debug_location(&debug_location)) - } - DebugCommandAPI::AddBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) - } - DebugCommandAPI::DeleteBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) - } - DebugCommandAPI::Restart => { - context.restart(); - DebugCommandAPIResult::Unit(()) - } - DebugCommandAPI::GetWitnessMap => { - DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) - } - DebugCommandAPI::IsExecutingBrillig => { - DebugCommandAPIResult::Bool(context.is_executing_brillig()) - } - DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( - context.get_brillig_memory().map(|values| values.to_vec()), - ), - DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { - context.write_brillig_memory(ptr, value, bit_size); - DebugCommandAPIResult::Unit(()) - } - DebugCommandAPI::OverwriteWitness(witness, value) => { - DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) - } + println!("Debugger ready for receiving messages.."); + loop { + // recv blocks until it receives message + if let Ok(received) = command_rx.recv() { + let result = match received { + DebugCommandAPI::GetCurrentDebugLocation => { + DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) + } + DebugCommandAPI::GetOpcodes => { + DebugCommandAPIResult::Opcodes(context.get_opcodes().to_owned()) + } + DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => { + DebugCommandAPIResult::Opcodes( + context.get_opcodes_of_circuit(circuit_id).to_owned(), + ) + } + DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { + DebugCommandAPIResult::Locations( + context.get_source_location_for_debug_location(&debug_location), + ) + } + DebugCommandAPI::GetCallStack => { + DebugCommandAPIResult::DebugLocations(context.get_call_stack()) + } + DebugCommandAPI::IsBreakpointSet(debug_location) => { + DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) + } + DebugCommandAPI::IsValidDebugLocation(debug_location) => { + DebugCommandAPIResult::Bool( + context.is_valid_debug_location(&debug_location), + ) + } + DebugCommandAPI::AddBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) + } + DebugCommandAPI::DeleteBreakpoint(debug_location) => { + DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) + } + DebugCommandAPI::Restart => { + context.restart(); + DebugCommandAPIResult::Unit(()) + } + DebugCommandAPI::GetWitnessMap => { + DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) + } + DebugCommandAPI::IsExecutingBrillig => { + DebugCommandAPIResult::Bool(context.is_executing_brillig()) + } + DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( + context.get_brillig_memory().map(|values| values.to_vec()), + ), + DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { + context.write_brillig_memory(ptr, value, bit_size); + DebugCommandAPIResult::Unit(()) + } + DebugCommandAPI::OverwriteWitness(witness, value) => { + DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) + } - DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( - context.get_variables().iter().map(DebugStackFrame::from).collect(), - ), - DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), - DebugCommandAPI::StepAcirOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) - } - DebugCommandAPI::StepIntoOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) - } - DebugCommandAPI::NextInto => { - DebugCommandAPIResult::DebugCommandResult(context.next_into()) - } - DebugCommandAPI::NextOver => { - DebugCommandAPIResult::DebugCommandResult(context.next_over()) - } - DebugCommandAPI::NextOut => { - DebugCommandAPIResult::DebugCommandResult(context.next_out()) - } - DebugCommandAPI::Cont => DebugCommandAPIResult::DebugCommandResult(context.cont()), - DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { - DebugCommandAPIResult::DebugLocation( - context.find_opcode_at_current_file_line(line), - ) - } - DebugCommandAPI::Finalize => { - let witness_stack = context.finalize(); - let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); - // We need to stop the 'event loop' since `finalize` consumes the context + DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( + context.get_variables().iter().map(DebugStackFrame::from).collect(), + ), + DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), + DebugCommandAPI::StepAcirOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) + } + DebugCommandAPI::StepIntoOpcode => { + DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) + } + DebugCommandAPI::NextInto => { + DebugCommandAPIResult::DebugCommandResult(context.next_into()) + } + DebugCommandAPI::NextOver => { + DebugCommandAPIResult::DebugCommandResult(context.next_over()) + } + DebugCommandAPI::NextOut => { + DebugCommandAPIResult::DebugCommandResult(context.next_out()) + } + DebugCommandAPI::Cont => { + DebugCommandAPIResult::DebugCommandResult(context.cont()) + } + DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { + DebugCommandAPIResult::DebugLocation( + context.find_opcode_at_current_file_line(line), + ) + } + DebugCommandAPI::Finalize => { + let witness_stack = context.finalize(); + let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); + // We need to stop the 'event loop' since `finalize` consumes the context + drop(result_tx); + drop(command_rx); + break; + } + }; + let Ok(()) = result_tx.send(result) else { drop(result_tx); drop(command_rx); break; - } - }; - let Ok(()) = result_tx.send(result) else { + }; + } else { + println!("Upstream channel closed. Terminating debugger"); drop(result_tx); drop(command_rx); break; - }; - } else { - println!("Upstream channel closed. Terminating debugger"); - drop(result_tx); - drop(command_rx); - break; + } } } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 67d6a353f4e..054b2dbcf3a 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,5 +1,5 @@ use crate::context::{DebugCommandResult, DebugLocation, DebugStackFrame}; -use crate::debug::{start_debugger, DebugCommandAPI, DebugCommandAPIResult}; +use crate::debug::{DebugCommandAPI, DebugCommandAPIResult, Debugger}; use acvm::AcirField; use acvm::acir::brillig::BitSize; @@ -578,16 +578,14 @@ pub fn run( let (command_tx, command_rx) = mpsc::channel::(); let (result_tx, result_rx) = mpsc::channel::(); thread::spawn(move || { - start_debugger( - command_rx, - result_tx, - debugger_circuits, - &debugger_artifact, + let debugger = Debugger { + circuits: debugger_circuits, + debug_artifact: &debugger_artifact, initial_witness, - foreign_call_executor, - debugger_unconstrained_functions, + unconstrained_functions: debugger_unconstrained_functions, pedantic_solving, - ); + }; + debugger.start_debugging(command_rx, result_tx, foreign_call_executor); }); let context = RefCell::new(ReplDebugger::new( From fccbd1c85339f47a7c88f2fcddced5b5582d0eb6 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 27 Feb 2025 17:19:04 -0300 Subject: [PATCH 25/60] adapt dap iterface to debug test functions --- tooling/debugger/src/dap.rs | 14 +++-- tooling/debugger/src/lib.rs | 9 +-- tooling/lsp/src/requests/code_lens_request.rs | 21 ++++++- tooling/nargo_cli/src/cli/dap_cmd.rs | 63 ++++++++++++++++--- tooling/nargo_cli/src/cli/debug_cmd.rs | 16 ++--- 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index e2a2156809d..274918f7171 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; +use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::PrintOutput; use crate::context::DebugContext; @@ -66,6 +67,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< initial_witness: WitnessMap, root_path: Option, package_name: String, + foreign_call_resolver_url: Option, ) -> Self { let context = DebugContext::new( solver, @@ -74,7 +76,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< initial_witness, Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, - None, // TODO: set oracle_resolver url + foreign_call_resolver_url, debug_artifact, root_path, package_name, @@ -609,24 +611,28 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } } -pub fn run_session>( +pub fn run_session( server: Server, - solver: &B, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, + pedantic_solver: bool, + foreign_call_resolver_url: Option, ) -> Result<(), ServerError> { let debug_artifact = DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; + + let solver = Bn254BlackBoxSolver(pedantic_solver); let mut session = DapSession::new( server, - solver, + &solver, &program, &debug_artifact, initial_witness, root_path, package_name, + foreign_call_resolver_url, ); session.run_loop() diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 09cbdb25353..b9a403a85d2 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -12,7 +12,7 @@ use std::path::PathBuf; use ::dap::errors::ServerError; use ::dap::server::Server; use acvm::acir::native_types::{WitnessMap, WitnessStack}; -use acvm::{BlackBoxFunctionSolver, FieldElement}; +use acvm::FieldElement; use nargo::NargoError; use noirc_driver::CompiledProgram; @@ -37,13 +37,14 @@ pub fn run_repl_session( ) } -pub fn run_dap_loop>( +pub fn run_dap_loop( server: Server, - solver: &B, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, + pedantic_solving: bool, + foreign_call_resolver_url: Option, ) -> Result<(), ServerError> { - dap::run_session(server, solver, program, initial_witness, root_path, package_name) + dap::run_session(server, program, initial_witness, root_path, package_name, pedantic_solving, foreign_call_resolver_url) } diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index 1870e8e0602..d0002e457c8 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -22,6 +22,8 @@ const EXECUTE_COMMAND: &str = "nargo.execute"; const EXECUTE_CODELENS_TITLE: &str = "Execute"; const DEBUG_COMMAND: &str = "nargo.debug.dap"; const DEBUG_CODELENS_TITLE: &str = "Debug"; +const DEBUG_TEST_COMMAND: &str = "nargo.debug.test"; +const DEBUG_TEST_CODELENS_TITLE: &str = "Debug test"; fn with_arrow(title: &str) -> String { format!("{ARROW} {title}") @@ -116,7 +118,7 @@ pub(crate) fn collect_lenses_for_package( arguments: Some( [ package_selection_args(workspace, package), - vec!["--exact".into(), "--show-output".into(), func_name.into()], + vec!["--exact".into(), "--show-output".into(), func_name.clone().into()], ] .concat(), ), @@ -125,6 +127,23 @@ pub(crate) fn collect_lenses_for_package( let test_lens = CodeLens { range, command: Some(test_command), data: None }; lenses.push(test_lens); + + let debug_test_command = Command { + title: DEBUG_TEST_CODELENS_TITLE.to_string(), + command: DEBUG_TEST_COMMAND.into(), + arguments: Some( + [ + package_selection_args(workspace, package), + vec!["--exact".into(), func_name.into()], + ] + .concat(), + ), + }; + + let debug_test_lens = CodeLens { range, command: Some(debug_test_command), data: None }; + + lenses.push(debug_test_lens); + } if package.is_binary() { diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 291e1099af7..fdd2848d5fc 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,9 +1,9 @@ use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; +use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; @@ -19,7 +19,8 @@ use dap::server::Server; use dap::types::Capabilities; use serde_json::Value; -use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging}; +use super::check_cmd::check_crate_and_report_errors; +use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging, compile_test_fn_for_debugging, get_test_function, load_workspace_files, prepare_package_for_debug}; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -48,6 +49,9 @@ pub(crate) struct DapCommand { #[clap(long)] preflight_skip_instrumentation: bool, + #[clap(long)] + preflight_test_name: Option, + /// Use pedantic ACVM solving, i.e. double-check some black-box function /// assumptions when solving. /// This is disabled by default. @@ -98,6 +102,36 @@ fn workspace_not_found_error_msg(project_folder: &str, package: Option<&str>) -> } } +fn compile_main( + workspace: &Workspace, + package: &Package, + expression_width: ExpressionWidth, + compile_options: &CompileOptions, +) -> Result { + compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) + .map_err(|_| LoadError::Generic("Failed to compile project".into())) +} + +fn compile_test( + workspace: &Workspace, + package: &Package, + expression_width: ExpressionWidth, + compile_options: CompileOptions, + test_name: String, +) -> Result { + let (file_manager, mut parsed_files) = load_workspace_files(&workspace); + + let (mut context, crate_id) = + prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); + + check_crate_and_report_errors(&mut context, crate_id, &compile_options).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + let test = get_test_function(crate_id, &context, &test_name).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + compile_test_fn_for_debugging(&test, &mut context, package, compile_options, Some(expression_width)).map_err(|_| LoadError::Generic("Failed to compile project".into())) + +} + fn load_and_compile_project( project_folder: &str, package: Option<&str>, @@ -105,19 +139,22 @@ fn load_and_compile_project( expression_width: ExpressionWidth, acir_mode: bool, skip_instrumentation: bool, + test_name: Option, ) -> Result<(CompiledProgram, WitnessMap, PathBuf, String), LoadError> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace .into_iter() - .find(|p| p.is_binary()) - .ok_or(LoadError::Generic("No matching binary packages found in workspace".into()))?; + .find(|p| p.is_binary() || p.is_contract()) + .ok_or(LoadError::Generic("No matching binary or contract packages found in workspace. Only these packages can be debugged.".into()))?; let compile_options = compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); - let compiled_program = - compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) - .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + let compiled_program = match test_name { + None => compile_main(&workspace, &package, expression_width, &compile_options), + Some(test_name) => compile_test(&workspace, &package, expression_width, compile_options, test_name), + }?; let (inputs_map, _) = read_inputs_from_file( @@ -176,6 +213,12 @@ fn loop_uninitialized_dap( .get("skipInstrumentation") .and_then(|v| v.as_bool()) .unwrap_or(generate_acir); + let test_name = + additional_data.get("testName").and_then(|v| v.as_str()).map(String::from); + let oracle_resolver_url = additional_data + .get("oracleResolver") + .and_then(|v| v.as_str()) + .map(String::from); eprintln!("Project folder: {}", project_folder); eprintln!("Package: {}", package.unwrap_or("(default)")); @@ -188,17 +231,19 @@ fn loop_uninitialized_dap( expression_width, generate_acir, skip_instrumentation, + test_name, ) { Ok((compiled_program, initial_witness, root_path, package_name)) => { server.respond(req.ack()?)?; noir_debugger::run_dap_loop( server, - &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, Some(root_path), package_name, + pedantic_solving, + oracle_resolver_url, )?; break; } @@ -233,6 +278,7 @@ fn run_preflight_check( }; let package = args.preflight_package.as_deref(); + let test_name = args.preflight_test_name; let prover_name = args.preflight_prover_name.as_deref().unwrap_or(PROVER_INPUT_FILE); let _ = load_and_compile_project( @@ -242,6 +288,7 @@ fn run_preflight_check( expression_width, args.preflight_generate_acir, args.preflight_skip_instrumentation, + test_name, )?; Ok(()) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 7be56b15a5d..c5495d101d0 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -164,7 +164,7 @@ fn debug_test_fn( compile_options: CompileOptions, run_params: RunParams, ) -> TestResult { - let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options); + let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options, None); let test_status = match compiled_program { Ok(compiled_program) => { @@ -201,16 +201,17 @@ fn debug_test_fn( ) } -fn compile_test_fn_for_debugging( +pub(super) fn compile_test_fn_for_debugging( test_def: &TestDefinition, context: &mut Context, package: &Package, compile_options: CompileOptions, + expression_with: Option, ) -> Result { let compiled_program = compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); + expression_with.unwrap_or(get_target_width(package.expression_width, compile_options.expression_width)); let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); Ok(compiled_program) } @@ -323,18 +324,17 @@ fn debug_test( Ok(()) } -struct TestDefinition { +pub(super) struct TestDefinition { name: String, function: TestFunction, } // TODO: move to nargo::ops and reuse in test_cmd? -fn get_test_function( +pub(super) fn get_test_function( crate_id: CrateId, context: &Context, test_name: &str, ) -> Result { - // TODO: review Contains signature and check if its ok to send test_name as single element let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); @@ -371,14 +371,14 @@ fn get_test_function( Ok(TestDefinition { name: test_name, function: test_function }) } -fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { +pub(super) fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { let mut file_manager = file_manager_with_stdlib(Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut file_manager); let parsed_files = parse_all(&file_manager); (file_manager, parsed_files) } -pub(crate) fn prepare_package_for_debug<'a>( +pub(super) fn prepare_package_for_debug<'a>( file_manager: &'a FileManager, parsed_files: &'a mut ParsedFiles, package: &'a Package, From e7f1d1ead69e1e6e2f7f3bd71438cc29bf98e765 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 28 Feb 2025 16:19:55 -0300 Subject: [PATCH 26/60] Analyze test result when running DAP debugger interface --- tooling/debugger/src/dap.rs | 78 ++++++++--- tooling/debugger/src/lib.rs | 15 ++- tooling/lsp/src/requests/code_lens_request.rs | 1 - tooling/nargo_cli/src/cli/dap_cmd.rs | 122 +++++++++++++++--- tooling/nargo_cli/src/cli/debug_cmd.rs | 11 +- 5 files changed, 177 insertions(+), 50 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 274918f7171..a2b691cd4e1 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -2,10 +2,10 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; use std::path::PathBuf; -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; -use nargo::PrintOutput; +use nargo::{NargoError, PrintOutput}; use crate::context::DebugContext; use crate::context::{DebugCommandResult, DebugLocation}; @@ -33,13 +33,14 @@ use noirc_driver::CompiledProgram; type BreakpointId = i64; pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> { - server: Server, + server: &'a mut Server, context: DebugContext<'a, B>, debug_artifact: &'a DebugArtifact, running: bool, next_breakpoint_id: BreakpointId, instruction_breakpoints: Vec<(DebugLocation, BreakpointId)>, source_breakpoints: BTreeMap>, + last_result: DebugCommandResult, } enum ScopeReferences { @@ -60,7 +61,7 @@ impl From for ScopeReferences { impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { pub fn new( - server: Server, + server: &'a mut Server, solver: &'a B, program: &'a CompiledProgram, debug_artifact: &'a DebugArtifact, @@ -91,6 +92,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< next_breakpoint_id: 1, instruction_breakpoints: vec![], source_breakpoints: BTreeMap::new(), + last_result: DebugCommandResult::Ok, // TODO: handle circuits with no opcodes ? } } @@ -130,7 +132,12 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< match req.command { Command::Disconnect(_) => { eprintln!("INFO: ending debugging session"); - self.server.respond(req.ack()?)?; + self.running = false; + // TODO: review deleting this line + // responding ack here makes the server to close connection + // so the dap_cmd then can't use it to send the test_result to the user + // + // self.server.respond(req.ack()?)?; break; } Command::SetBreakpoints(_) => { @@ -347,7 +354,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } fn handle_execution_result(&mut self, result: DebugCommandResult) -> Result<(), ServerError> { - match result { + self.last_result = result; + match &self.last_result { DebugCommandResult::Done => { self.running = false; } @@ -363,7 +371,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< }))?; } DebugCommandResult::BreakpointReached(location) => { - let breakpoint_ids = self.find_breakpoints_at_location(&location); + let breakpoint_ids = self.find_breakpoints_at_location(location); self.server.send_event(Event::Stopped(StoppedEventBody { reason: StoppedEventReason::Breakpoint, description: Some(String::from("Paused at breakpoint")), @@ -374,16 +382,21 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< hit_breakpoint_ids: Some(breakpoint_ids), }))?; } - DebugCommandResult::Error(err) => { - self.server.send_event(Event::Stopped(StoppedEventBody { - reason: StoppedEventReason::Exception, - description: Some(format!("{err:?}")), - thread_id: Some(0), - preserve_focus_hint: Some(false), - text: None, - all_threads_stopped: Some(false), - hit_breakpoint_ids: None, - }))?; + DebugCommandResult::Error(_err) => { + // TODO: review: + // is it better for the user to have the possibility to restart the debug session and get out of the error + // or is it better to finish the session automatically? so the user does not have to manually stop the execution? + + // self.server.send_event(Event::Stopped(StoppedEventBody { + // reason: StoppedEventReason::Exception, + // description: Some("Stopped on exception".into()), + // thread_id: Some(0), + // preserve_focus_hint: Some(false), + // text: Some(format!("{err:#?}")), + // all_threads_stopped: Some(false), + // hit_breakpoint_ids: None, + // }))?; + self.server.send_event(Event::Terminated(None))?; } } Ok(()) @@ -609,20 +622,27 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< .respond(req.success(ResponseBody::Variables(VariablesResponse { variables })))?; Ok(()) } + + pub fn last_error(self) -> Option> { + match self.last_result { + DebugCommandResult::Error(error) => Some(error), + _ => None, + } + } } pub fn run_session( - server: Server, + server: &mut Server, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, pedantic_solver: bool, foreign_call_resolver_url: Option, -) -> Result<(), ServerError> { +) -> Result { let debug_artifact = DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; - + let solver = Bn254BlackBoxSolver(pedantic_solver); let mut session = DapSession::new( server, @@ -635,5 +655,21 @@ pub fn run_session( foreign_call_resolver_url, ); - session.run_loop() + session.run_loop()?; + if session.context.is_solved() { + let solved_witness_stack = session.context.finalize(); + Ok(ExecutionResult::Solved(solved_witness_stack)) + } else { + match session.last_error() { + // Expose the last known error + Some(error) => Ok(ExecutionResult::Error(error)), + None => Ok(ExecutionResult::Incomplete), + } + } +} + +pub enum ExecutionResult { + Solved(WitnessStack), + Incomplete, + Error(NargoError), } diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index b9a403a85d2..66d2d043b46 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -14,6 +14,7 @@ use ::dap::server::Server; use acvm::acir::native_types::{WitnessMap, WitnessStack}; use acvm::FieldElement; +pub use dap::ExecutionResult; use nargo::NargoError; use noirc_driver::CompiledProgram; @@ -38,13 +39,21 @@ pub fn run_repl_session( } pub fn run_dap_loop( - server: Server, + server: &mut Server, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, pedantic_solving: bool, foreign_call_resolver_url: Option, -) -> Result<(), ServerError> { - dap::run_session(server, program, initial_witness, root_path, package_name, pedantic_solving, foreign_call_resolver_url) +) -> Result { + dap::run_session( + server, + program, + initial_witness, + root_path, + package_name, + pedantic_solving, + foreign_call_resolver_url, + ) } diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index d0002e457c8..6cc67a1a957 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -143,7 +143,6 @@ pub(crate) fn collect_lenses_for_package( let debug_test_lens = CodeLens { range, command: Some(debug_test_command), data: None }; lenses.push(debug_test_lens); - } if package.is_binary() { diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index fdd2848d5fc..93e547eb8cc 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -2,12 +2,17 @@ use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; use clap::Args; +use dap::errors::ServerError; +use dap::events::OutputEventBody; use nargo::constants::PROVER_INPUT_FILE; +use nargo::ops::{test_status_program_compile_pass, TestStatus}; use nargo::package::Package; use nargo::workspace::Workspace; -use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; +use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; use std::io::{BufReader, BufWriter, Read, Write}; @@ -16,14 +21,19 @@ use std::path::{Path, PathBuf}; use dap::requests::Command; use dap::responses::ResponseBody; use dap::server::Server; -use dap::types::Capabilities; +use dap::types::{Capabilities, OutputEventCategory}; use serde_json::Value; use super::check_cmd::check_crate_and_report_errors; -use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging, compile_test_fn_for_debugging, get_test_function, load_workspace_files, prepare_package_for_debug}; +use super::debug_cmd::{ + compile_bin_package_for_debugging, compile_options_for_debugging, + compile_test_fn_for_debugging, get_test_function, load_workspace_files, + prepare_package_for_debug, TestDefinition, +}; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; +use noir_debugger::ExecutionResult; #[derive(Debug, Clone, Args)] pub(crate) struct DapCommand { @@ -108,7 +118,7 @@ fn compile_main( expression_width: ExpressionWidth, compile_options: &CompileOptions, ) -> Result { - compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) + compile_bin_package_for_debugging(workspace, package, compile_options, expression_width) .map_err(|_| LoadError::Generic("Failed to compile project".into())) } @@ -118,18 +128,27 @@ fn compile_test( expression_width: ExpressionWidth, compile_options: CompileOptions, test_name: String, -) -> Result { - let (file_manager, mut parsed_files) = load_workspace_files(&workspace); +) -> Result<(CompiledProgram, TestDefinition), LoadError> { + let (file_manager, mut parsed_files) = load_workspace_files(workspace); let (mut context, crate_id) = - prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); - - check_crate_and_report_errors(&mut context, crate_id, &compile_options).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + prepare_package_for_debug(&file_manager, &mut parsed_files, package, workspace); - let test = get_test_function(crate_id, &context, &test_name).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + check_crate_and_report_errors(&mut context, crate_id, &compile_options) + .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; - compile_test_fn_for_debugging(&test, &mut context, package, compile_options, Some(expression_width)).map_err(|_| LoadError::Generic("Failed to compile project".into())) + let test = get_test_function(crate_id, &context, &test_name) + .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + let program = compile_test_fn_for_debugging( + &test, + &mut context, + package, + compile_options, + Some(expression_width), + ) + .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + Ok((program, test)) } fn load_and_compile_project( @@ -140,7 +159,10 @@ fn load_and_compile_project( acir_mode: bool, skip_instrumentation: bool, test_name: Option, -) -> Result<(CompiledProgram, WitnessMap, PathBuf, String), LoadError> { +) -> Result< + (CompiledProgram, WitnessMap, PathBuf, String, Option), + LoadError, +> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace @@ -151,9 +173,16 @@ fn load_and_compile_project( let compile_options = compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); - let compiled_program = match test_name { - None => compile_main(&workspace, &package, expression_width, &compile_options), - Some(test_name) => compile_test(&workspace, &package, expression_width, compile_options, test_name), + let (compiled_program, test_def) = match test_name { + None => { + let program = compile_main(&workspace, package, expression_width, &compile_options)?; + Ok((program, None)) + } + Some(test_name) => { + let (program, test_def) = + compile_test(&workspace, package, expression_width, compile_options, test_name)?; + Ok((program, Some(test_def))) + } }?; @@ -169,7 +198,13 @@ fn load_and_compile_project( .encode(&inputs_map, None) .map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?; - Ok((compiled_program, initial_witness, workspace.root_dir.clone(), package.name.to_string())) + Ok(( + compiled_program, + initial_witness, + workspace.root_dir.clone(), + package.name.to_string(), + test_def, + )) } fn loop_uninitialized_dap( @@ -233,18 +268,24 @@ fn loop_uninitialized_dap( skip_instrumentation, test_name, ) { - Ok((compiled_program, initial_witness, root_path, package_name)) => { + Ok((compiled_program, initial_witness, root_path, package_name, test_def)) => { server.respond(req.ack()?)?; + let abi = compiled_program.abi.clone(); + let debug = compiled_program.debug.clone(); - noir_debugger::run_dap_loop( - server, + let result = noir_debugger::run_dap_loop( + &mut server, compiled_program, initial_witness, Some(root_path), - package_name, + package_name.clone(), pedantic_solving, oracle_resolver_url, )?; + + if let Some(test) = test_def { + analyze_test_result(&mut server, result, test, abi, debug)?; + } break; } Err(LoadError::Generic(message)) => { @@ -267,6 +308,47 @@ fn loop_uninitialized_dap( Ok(()) } +fn analyze_test_result( + server: &mut Server, + result: ExecutionResult, + test: TestDefinition, + abi: Abi, + debug: Vec, +) -> Result<(), ServerError> { + let test_status = match result { + ExecutionResult::Solved(result) => { + test_status_program_compile_pass(&test.function, &abi, &debug, &Ok(result)) + } + // Test execution failed + ExecutionResult::Error(error) => { + test_status_program_compile_pass(&test.function, &abi, &debug, &Err(error)) + } + // Execution didn't complete + ExecutionResult::Incomplete => { + TestStatus::Fail { message: "Execution halted".into(), error_diagnostic: None } + } + }; + + let test_result_message = match test_status { + TestStatus::Pass => "✓ Test passed".into(), + TestStatus::Fail { message, error_diagnostic } => { + let basic_message = format!("x Test failed: {message}"); + match error_diagnostic { + Some(diagnostic) => format!("{basic_message}.\n{diagnostic:#?}"), + None => basic_message, + } + } + TestStatus::CompileError(diagnostic) => format!("x Test failed.\n{diagnostic:#?}"), + TestStatus::Skipped => "* Test skipped".into(), + }; + + server.send_event(dap::events::Event::Output(OutputEventBody { + category: Some(OutputEventCategory::Console), + output: test_result_message, + ..OutputEventBody::default() + })) +} + fn run_preflight_check( expression_width: ExpressionWidth, args: DapCommand, diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index c5495d101d0..880e991cce5 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -164,7 +164,8 @@ fn debug_test_fn( compile_options: CompileOptions, run_params: RunParams, ) -> TestResult { - let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options, None); + let compiled_program = + compile_test_fn_for_debugging(test, context, package, compile_options, None); let test_status = match compiled_program { Ok(compiled_program) => { @@ -210,8 +211,8 @@ pub(super) fn compile_test_fn_for_debugging( ) -> Result { let compiled_program = compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; - let expression_width = - expression_with.unwrap_or(get_target_width(package.expression_width, compile_options.expression_width)); + let expression_width = expression_with + .unwrap_or(get_target_width(package.expression_width, compile_options.expression_width)); let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); Ok(compiled_program) } @@ -325,8 +326,8 @@ fn debug_test( } pub(super) struct TestDefinition { - name: String, - function: TestFunction, + pub name: String, + pub function: TestFunction, } // TODO: move to nargo::ops and reuse in test_cmd? From d59b8795273ecc49e13517c1077a7e1516979889 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 5 Mar 2025 15:08:35 -0300 Subject: [PATCH 27/60] Define DebugExecutionResult struct re-accomodate debug_cmd to make use of it --- tooling/debugger/src/context.rs | 7 ++ tooling/debugger/src/dap.rs | 18 ++-- tooling/debugger/src/lib.rs | 9 +- tooling/debugger/src/repl.rs | 10 +-- tooling/nargo_cli/src/cli/dap_cmd.rs | 19 ++-- tooling/nargo_cli/src/cli/debug_cmd.rs | 119 ++++++++++++------------- tooling/nargo_cli/src/errors.rs | 3 - 7 files changed, 89 insertions(+), 96 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 28fa6332bab..7cf1a51e76b 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -265,6 +265,13 @@ pub struct ExecutionFrame<'a, B: BlackBoxFunctionSolver> { acvm: ACVM<'a, FieldElement, B>, } +#[derive(Debug)] +pub enum DebugExecutionResult { + Solved(WitnessStack), + Incomplete, + Error(NargoError), +} + pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { pub(crate) acvm: ACVM<'a, FieldElement, B>, current_circuit_id: u32, diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index a2b691cd4e1..f317d1558f0 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -2,13 +2,13 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; use std::path::PathBuf; -use acvm::acir::native_types::{WitnessMap, WitnessStack}; +use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::{NargoError, PrintOutput}; -use crate::context::DebugContext; use crate::context::{DebugCommandResult, DebugLocation}; +use crate::context::{DebugContext, DebugExecutionResult}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use dap::errors::ServerError; @@ -639,7 +639,7 @@ pub fn run_session( package_name: String, pedantic_solver: bool, foreign_call_resolver_url: Option, -) -> Result { +) -> Result { let debug_artifact = DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; @@ -658,18 +658,12 @@ pub fn run_session( session.run_loop()?; if session.context.is_solved() { let solved_witness_stack = session.context.finalize(); - Ok(ExecutionResult::Solved(solved_witness_stack)) + Ok(DebugExecutionResult::Solved(solved_witness_stack)) } else { match session.last_error() { // Expose the last known error - Some(error) => Ok(ExecutionResult::Error(error)), - None => Ok(ExecutionResult::Incomplete), + Some(error) => Ok(DebugExecutionResult::Error(error)), + None => Ok(DebugExecutionResult::Incomplete), } } } - -pub enum ExecutionResult { - Solved(WitnessStack), - Incomplete, - Error(NargoError), -} diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 66d2d043b46..cefa88db3a9 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -11,11 +11,10 @@ use std::path::PathBuf; use ::dap::errors::ServerError; use ::dap::server::Server; -use acvm::acir::native_types::{WitnessMap, WitnessStack}; +use acvm::acir::native_types::WitnessMap; use acvm::FieldElement; +pub use context::DebugExecutionResult; -pub use dap::ExecutionResult; -use nargo::NargoError; use noirc_driver::CompiledProgram; pub fn run_repl_session( @@ -26,7 +25,7 @@ pub fn run_repl_session( root_path: Option, package_name: String, pedantic_solving: bool, -) -> Result>, NargoError> { +) -> DebugExecutionResult { repl::run( program, initial_witness, @@ -46,7 +45,7 @@ pub fn run_dap_loop( package_name: String, pedantic_solving: bool, foreign_call_resolver_url: Option, -) -> Result { +) -> Result { dap::run_session( server, program, diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 054b2dbcf3a..abf160798a4 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,4 +1,4 @@ -use crate::context::{DebugCommandResult, DebugLocation, DebugStackFrame}; +use crate::context::{DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame}; use crate::debug::{DebugCommandAPI, DebugCommandAPIResult, Debugger}; use acvm::AcirField; @@ -558,7 +558,7 @@ pub fn run( root_path: Option, package_name: String, pedantic_solving: bool, -) -> Result>, NargoError> { +) -> DebugExecutionResult { let debugger_circuits = program.program.functions.clone(); let circuits = &program.program.functions; let debugger_artifact = @@ -791,12 +791,12 @@ pub fn run( if context.borrow().is_solved() { let solved_witness_stack = context.into_inner().finalize(); - Ok(Some(solved_witness_stack)) + DebugExecutionResult::Solved(solved_witness_stack) } else { match context.into_inner().last_error() { // Expose the last known error - Some(error) => Err(error), - None => Ok(None), + Some(error) => DebugExecutionResult::Error(error), + None => DebugExecutionResult::Incomplete, } } } diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 93e547eb8cc..833f0b87a13 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -5,11 +5,12 @@ use clap::Args; use dap::errors::ServerError; use dap::events::OutputEventBody; use nargo::constants::PROVER_INPUT_FILE; -use nargo::ops::{test_status_program_compile_pass, TestStatus}; +use nargo::ops::{TestStatus, test_status_program_compile_pass}; use nargo::package::Package; use nargo::workspace::Workspace; +use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; -use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noir_debugger::DebugExecutionResult; use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::debug_info::DebugInfo; @@ -26,14 +27,13 @@ use serde_json::Value; use super::check_cmd::check_crate_and_report_errors; use super::debug_cmd::{ - compile_bin_package_for_debugging, compile_options_for_debugging, + TestDefinition, compile_bin_package_for_debugging, compile_options_for_debugging, compile_test_fn_for_debugging, get_test_function, load_workspace_files, - prepare_package_for_debug, TestDefinition, + prepare_package_for_debug, }; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; -use noir_debugger::ExecutionResult; #[derive(Debug, Clone, Args)] pub(crate) struct DapCommand { @@ -185,7 +185,6 @@ fn load_and_compile_project( } }?; - let (inputs_map, _) = read_inputs_from_file( &package.root_dir.join(prover_name).with_extension("toml"), &compiled_program.abi, @@ -310,21 +309,21 @@ fn loop_uninitialized_dap( fn analyze_test_result( server: &mut Server, - result: ExecutionResult, + result: DebugExecutionResult, test: TestDefinition, abi: Abi, debug: Vec, ) -> Result<(), ServerError> { let test_status = match result { - ExecutionResult::Solved(result) => { + DebugExecutionResult::Solved(result) => { test_status_program_compile_pass(&test.function, &abi, &debug, &Ok(result)) } // Test execution failed - ExecutionResult::Error(error) => { + DebugExecutionResult::Error(error) => { test_status_program_compile_pass(&test.function, &abi, &debug, &Err(error)) } // Execution didn't complete - ExecutionResult::Incomplete => { + DebugExecutionResult::Incomplete => { TestStatus::Fail { message: "Execution halted".into(), error_diagnostic: None } } }; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 880e991cce5..3f2ac561f0c 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -15,17 +15,15 @@ use nargo::ops::{ }; use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; -use nargo::{ - NargoError, insert_all_files_for_workspace_into_file_manager, parse_all, prepare_package, -}; +use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all, prepare_package}; use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noirc_abi::input_parser::InputValue; +use noir_debugger::DebugExecutionResult; use noirc_abi::Abi; use noirc_driver::{ - compile_no_check, file_manager_with_stdlib, link_to_debug_crate, CompileOptions, - CompiledProgram, + CompileOptions, CompiledProgram, compile_no_check, file_manager_with_stdlib, + link_to_debug_crate, }; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::graph::CrateId; @@ -34,8 +32,8 @@ use noirc_frontend::hir::{Context, FunctionNameMatch, ParsedFiles}; use super::check_cmd::check_crate_and_report_errors; use super::compile_cmd::get_target_width; -use super::test_cmd::formatters::Formatter; use super::test_cmd::TestResult; +use super::test_cmd::formatters::Formatter; use super::{LockType, WorkspaceCommand}; use crate::cli::test_cmd::formatters::PrettyFormatter; use crate::errors::CliError; @@ -176,14 +174,16 @@ fn debug_test_fn( let debug_result = run_async(package, compiled_program, workspace, run_params); match debug_result { - Ok(result) => { + Ok(DebugExecutionResult::Solved(result)) => { test_status_program_compile_pass(&test.function, &abi, &debug, &Ok(result)) } - // Test execution failed - Err(CliError::NargoError(error)) => { + Ok(DebugExecutionResult::Error(error)) => { test_status_program_compile_pass(&test.function, &abi, &debug, &Err(error)) } - // Other errors + Ok(DebugExecutionResult::Incomplete) => TestStatus::Fail { + message: "Debugger halted. Incomplete execution".to_string(), + error_diagnostic: None, + }, Err(error) => TestStatus::Fail { message: format!("Debugger failed: {error}"), error_diagnostic: None, @@ -299,7 +299,9 @@ fn debug_main( let compiled_program = compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; - run_async(package, compiled_program, &workspace, run_params).map(|_| ()) + run_async(package, compiled_program, &workspace, run_params)?; + + Ok(()) } fn debug_test( @@ -395,67 +397,64 @@ pub(super) fn prepare_package_for_debug<'a>( (context, crate_id) } -// FIXME: find a better name -type ExecutionResult = (Option, WitnessStack); - fn run_async( package: &Package, program: CompiledProgram, workspace: &Workspace, run_params: RunParams, -) -> Result, CliError> { +) -> Result { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); + let abi = &program.abi.clone(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, witness_stack) = - debug_program_and_decode(program, package, workspace, &run_params)?; - - let witness_stack_result = witness_stack.clone(); - println!("[{}] Circuit witness successfully solved", package.name); + let initial_witness = parse_initial_witness(package, &run_params.prover_name, abi)?; + + let result = debug_program( + program, + initial_witness, + run_params.pedantic_solving, + run_params.raw_source_printing, + run_params.oracle_resolver_url, + Some(workspace.root_dir.clone()), + package.name.to_string(), + ); - if let Some(return_value) = return_value { - println!("[{}] Circuit output: {return_value:?}", package.name); + if let DebugExecutionResult::Solved(ref witness_stack) = result { + println!("[{}] Circuit witness successfully solved", package.name); + decode_and_save_program_witness( + &package.name, + witness_stack, + abi, + run_params.witness_name, + &run_params.target_dir, + )?; } - if let Some(witness_name) = run_params.witness_name { - let witness_path = - save_witness_to_dir(&witness_stack, &witness_name, run_params.target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - } - Ok(witness_stack_result) + Ok(result) }) } -fn debug_program_and_decode( - program: CompiledProgram, - package: &Package, - workspace: &Workspace, - run_params: &RunParams, -) -> Result { - let program_abi = program.abi.clone(); - let initial_witness = parse_initial_witness(package, &run_params.prover_name, &program.abi)?; - let witness_stack = debug_program( - program, - initial_witness, - run_params.pedantic_solving, - run_params.raw_source_printing, - run_params.oracle_resolver_url.clone(), - Some(workspace.root_dir.clone()), - package.name.to_string(), - )?; - match witness_stack { - Some(witness_stack) => { - let main_witness = &witness_stack - .peek() - .expect("Should have at least one witness on the stack") - .witness; - let (_, return_value) = program_abi.decode(main_witness)?; - Ok((return_value, witness_stack)) - } - None => Err(CliError::ExecutionHalted), +fn decode_and_save_program_witness( + package_name: &CrateName, + witness_stack: &WitnessStack, + abi: &Abi, + target_witness_name: Option, + target_dir: &Path, +) -> Result<(), CliError> { + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + + if let (_, Some(return_value)) = abi.decode(main_witness)? { + println!("[{}] Circuit output: {return_value:?}", package_name); } + + if let Some(witness_name) = target_witness_name { + let witness_path = save_witness_to_dir(witness_stack, &witness_name, target_dir)?; + println!("[{}] Witness saved to {}", package_name, witness_path.display()); + } + Ok(()) } fn parse_initial_witness( @@ -464,10 +463,8 @@ fn parse_initial_witness( abi: &Abi, ) -> Result, CliError> { // Parse the initial witness values from Prover.toml - let (inputs_map, _) = read_inputs_from_file( - &package.root_dir.join(prover_name).with_extension("toml"), - abi, - )?; + let (inputs_map, _) = + read_inputs_from_file(&package.root_dir.join(prover_name).with_extension("toml"), abi)?; let initial_witness = abi.encode(&inputs_map, None)?; Ok(initial_witness) } @@ -480,7 +477,7 @@ pub(crate) fn debug_program( foreign_call_resolver_url: Option, root_path: Option, package_name: String, -) -> Result>, NargoError> { +) -> DebugExecutionResult { noir_debugger::run_repl_session( compiled_program, initial_witness, diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index 8b174cf781f..fb241bcbd29 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -42,7 +42,4 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), - - #[error("Execution halted")] - ExecutionHalted, } From 4cda3e9c43b4a93a99778f2999cfab5a45b4336a Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 5 Mar 2025 16:08:50 -0300 Subject: [PATCH 28/60] simplify dap_cmd functions by using compile_options struct --- tooling/debugger/src/dap.rs | 6 +- tooling/debugger/src/lib.rs | 4 +- tooling/debugger/src/repl.rs | 4 +- tooling/nargo_cli/src/cli/dap_cmd.rs | 89 +++++++++++++------------- tooling/nargo_cli/src/cli/debug_cmd.rs | 16 ++--- 5 files changed, 61 insertions(+), 58 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index f317d1558f0..efcca23f334 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -66,7 +66,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< program: &'a CompiledProgram, debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, - root_path: Option, + root_path: PathBuf, package_name: String, foreign_call_resolver_url: Option, ) -> Self { @@ -79,7 +79,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< PrintOutput::Stdout, foreign_call_resolver_url, debug_artifact, - root_path, + Some(root_path), package_name, )), &program.program.unconstrained_functions, @@ -635,7 +635,7 @@ pub fn run_session( server: &mut Server, program: CompiledProgram, initial_witness: WitnessMap, - root_path: Option, + root_path: PathBuf, package_name: String, pedantic_solver: bool, foreign_call_resolver_url: Option, diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index cefa88db3a9..751c8f3f873 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -22,7 +22,7 @@ pub fn run_repl_session( initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: Option, + root_path: PathBuf, package_name: String, pedantic_solving: bool, ) -> DebugExecutionResult { @@ -41,7 +41,7 @@ pub fn run_dap_loop( server: &mut Server, program: CompiledProgram, initial_witness: WitnessMap, - root_path: Option, + root_path: PathBuf, package_name: String, pedantic_solving: bool, foreign_call_resolver_url: Option, diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index abf160798a4..f816d49cacb 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -555,7 +555,7 @@ pub fn run( initial_witness: WitnessMap, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: Option, + root_path: PathBuf, package_name: String, pedantic_solving: bool, ) -> DebugExecutionResult { @@ -571,7 +571,7 @@ pub fn run( PrintOutput::Stdout, foreign_call_resolver_url, &debugger_artifact, - root_path, + Some(root_path), package_name, )); diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 833f0b87a13..9c0142901b7 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -26,6 +26,7 @@ use dap::types::{Capabilities, OutputEventCategory}; use serde_json::Value; use super::check_cmd::check_crate_and_report_errors; +use super::compile_cmd::get_target_width; use super::debug_cmd::{ TestDefinition, compile_bin_package_for_debugging, compile_options_for_debugging, compile_test_fn_for_debugging, get_test_function, load_workspace_files, @@ -115,9 +116,10 @@ fn workspace_not_found_error_msg(project_folder: &str, package: Option<&str>) -> fn compile_main( workspace: &Workspace, package: &Package, - expression_width: ExpressionWidth, compile_options: &CompileOptions, ) -> Result { + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); compile_bin_package_for_debugging(workspace, package, compile_options, expression_width) .map_err(|_| LoadError::Generic("Failed to compile project".into())) } @@ -125,7 +127,6 @@ fn compile_main( fn compile_test( workspace: &Workspace, package: &Package, - expression_width: ExpressionWidth, compile_options: CompileOptions, test_name: String, ) -> Result<(CompiledProgram, TestDefinition), LoadError> { @@ -140,29 +141,24 @@ fn compile_test( let test = get_test_function(crate_id, &context, &test_name) .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; - let program = compile_test_fn_for_debugging( - &test, - &mut context, - package, - compile_options, - Some(expression_width), - ) - .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + let program = compile_test_fn_for_debugging(&test, &mut context, package, compile_options) + .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; Ok((program, test)) } - +//TODO: find a better name +struct Project { + program: CompiledProgram, + initial_witness: WitnessMap, + root_dir: PathBuf, + package_name: String, +} fn load_and_compile_project( project_folder: &str, package: Option<&str>, prover_name: &str, - expression_width: ExpressionWidth, - acir_mode: bool, - skip_instrumentation: bool, + compile_options: CompileOptions, test_name: Option, -) -> Result< - (CompiledProgram, WitnessMap, PathBuf, String, Option), - LoadError, -> { +) -> Result<(Project, Option), LoadError> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace @@ -170,17 +166,14 @@ fn load_and_compile_project( .find(|p| p.is_binary() || p.is_contract()) .ok_or(LoadError::Generic("No matching binary or contract packages found in workspace. Only these packages can be debugged.".into()))?; - let compile_options = - compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); - let (compiled_program, test_def) = match test_name { None => { - let program = compile_main(&workspace, package, expression_width, &compile_options)?; + let program = compile_main(&workspace, package, &compile_options)?; Ok((program, None)) } Some(test_name) => { let (program, test_def) = - compile_test(&workspace, package, expression_width, compile_options, test_name)?; + compile_test(&workspace, package, compile_options, test_name)?; Ok((program, Some(test_def))) } }?; @@ -197,13 +190,13 @@ fn load_and_compile_project( .encode(&inputs_map, None) .map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?; - Ok(( - compiled_program, + let project = Project { + program: compiled_program, initial_witness, - workspace.root_dir.clone(), - package.name.to_string(), - test_def, - )) + root_dir: workspace.root_dir.clone(), + package_name: package.name.to_string(), + }; + Ok((project, test_def)) } fn loop_uninitialized_dap( @@ -258,31 +251,36 @@ fn loop_uninitialized_dap( eprintln!("Package: {}", package.unwrap_or("(default)")); eprintln!("Prover name: {}", prover_name); + let compile_options = compile_options_for_debugging( + generate_acir, + skip_instrumentation, + Some(expression_width), + CompileOptions::default(), + ); + match load_and_compile_project( project_folder, package, prover_name, - expression_width, - generate_acir, - skip_instrumentation, + compile_options, test_name, ) { - Ok((compiled_program, initial_witness, root_path, package_name, test_def)) => { + Ok((project, test)) => { server.respond(req.ack()?)?; - let abi = compiled_program.abi.clone(); - let debug = compiled_program.debug.clone(); + let abi = project.program.abi.clone(); + let debug = project.program.debug.clone(); let result = noir_debugger::run_dap_loop( &mut server, - compiled_program, - initial_witness, - Some(root_path), - package_name.clone(), + project.program, + project.initial_witness, + project.root_dir, + project.package_name, pedantic_solving, oracle_resolver_url, )?; - if let Some(test) = test_def { + if let Some(test) = test { analyze_test_result(&mut server, result, test, abi, debug)?; } break; @@ -362,13 +360,18 @@ fn run_preflight_check( let test_name = args.preflight_test_name; let prover_name = args.preflight_prover_name.as_deref().unwrap_or(PROVER_INPUT_FILE); + let compile_options: CompileOptions = compile_options_for_debugging( + args.preflight_generate_acir, + args.preflight_skip_instrumentation, + Some(expression_width), + CompileOptions::default(), + ); + let _ = load_and_compile_project( project_folder.as_str(), package, prover_name, - expression_width, - args.preflight_generate_acir, - args.preflight_skip_instrumentation, + compile_options, test_name, )?; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 3f2ac561f0c..2a057b8ae94 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -126,7 +126,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr }; let compile_options = - compile_options_for_debugging(acir_mode, skip_instrumentation, args.compile_options); + compile_options_for_debugging(acir_mode, skip_instrumentation, None, args.compile_options); if let Some(test_name) = args.test_name { debug_test(test_name, package, workspace, compile_options, run_params) @@ -138,11 +138,13 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr pub(crate) fn compile_options_for_debugging( acir_mode: bool, skip_instrumentation: bool, + expression_width: Option, compile_options: CompileOptions, ) -> CompileOptions { CompileOptions { instrument_debug: !skip_instrumentation, force_brillig: !acir_mode, + expression_width, ..compile_options } } @@ -162,8 +164,7 @@ fn debug_test_fn( compile_options: CompileOptions, run_params: RunParams, ) -> TestResult { - let compiled_program = - compile_test_fn_for_debugging(test, context, package, compile_options, None); + let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options); let test_status = match compiled_program { Ok(compiled_program) => { @@ -207,12 +208,11 @@ pub(super) fn compile_test_fn_for_debugging( context: &mut Context, package: &Package, compile_options: CompileOptions, - expression_with: Option, ) -> Result { let compiled_program = compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; - let expression_width = expression_with - .unwrap_or(get_target_width(package.expression_width, compile_options.expression_width)); + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); Ok(compiled_program) } @@ -417,7 +417,7 @@ fn run_async( run_params.pedantic_solving, run_params.raw_source_printing, run_params.oracle_resolver_url, - Some(workspace.root_dir.clone()), + workspace.root_dir.clone(), package.name.to_string(), ); @@ -475,7 +475,7 @@ pub(crate) fn debug_program( pedantic_solving: bool, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: Option, + root_path: PathBuf, package_name: String, ) -> DebugExecutionResult { noir_debugger::run_repl_session( From f0ee01e11ce3db5cfa74c22210703ed50eba6c67 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 5 Mar 2025 16:40:10 -0300 Subject: [PATCH 29/60] Create new Project struct to encapsulate info needed for debugging --- tooling/debugger/src/context.rs | 12 ++++++- tooling/debugger/src/dap.rs | 48 +++++++++----------------- tooling/debugger/src/lib.rs | 36 +++---------------- tooling/debugger/src/repl.rs | 34 +++++++++--------- tooling/nargo_cli/src/cli/dap_cmd.rs | 25 ++++---------- tooling/nargo_cli/src/cli/debug_cmd.rs | 24 ++++++------- 6 files changed, 68 insertions(+), 111 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 7cf1a51e76b..e2e3109ad43 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -15,13 +15,14 @@ use fm::FileId; use nargo::errors::{execution_error_from, ExecutionError, Location}; use nargo::NargoError; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; -use noirc_driver::DebugFile; +use noirc_driver::{CompiledProgram, DebugFile}; use noirc_printable_type::{PrintableType, PrintableValue}; use thiserror::Error; use std::collections::BTreeMap; use std::collections::HashSet; +use std::path::PathBuf; /// A Noir program is composed by /// `n` ACIR circuits @@ -272,6 +273,15 @@ pub enum DebugExecutionResult { Error(NargoError), } +//TODO: find a better name +#[derive(Debug, Clone)] +pub struct Project { + pub compiled_program: CompiledProgram, + pub initial_witness: WitnessMap, + pub root_dir: PathBuf, + pub package_name: String, +} + pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { pub(crate) acvm: ACVM<'a, FieldElement, B>, current_circuit_id: u32, diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index efcca23f334..930d3a6a958 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -1,8 +1,6 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; -use std::path::PathBuf; -use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::{NargoError, PrintOutput}; @@ -10,6 +8,7 @@ use nargo::{NargoError, PrintOutput}; use crate::context::{DebugCommandResult, DebugLocation}; use crate::context::{DebugContext, DebugExecutionResult}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; +use crate::Project; use dap::errors::ServerError; use dap::events::StoppedEventBody; @@ -28,7 +27,6 @@ use dap::types::{ use noirc_artifacts::debug::DebugArtifact; use fm::FileId; -use noirc_driver::CompiledProgram; type BreakpointId = i64; @@ -63,26 +61,23 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn new( server: &'a mut Server, solver: &'a B, - program: &'a CompiledProgram, + project: &'a Project, debug_artifact: &'a DebugArtifact, - initial_witness: WitnessMap, - root_path: PathBuf, - package_name: String, foreign_call_resolver_url: Option, ) -> Self { let context = DebugContext::new( solver, - &program.program.functions, + &project.compiled_program.program.functions, debug_artifact, - initial_witness, + project.initial_witness.clone(), Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, foreign_call_resolver_url, debug_artifact, - Some(root_path), - package_name, + Some(project.root_dir.clone()), + project.package_name.clone(), )), - &program.program.unconstrained_functions, + &project.compiled_program.program.unconstrained_functions, ); Self { server, @@ -633,27 +628,18 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn run_session( server: &mut Server, - program: CompiledProgram, - initial_witness: WitnessMap, - root_path: PathBuf, - package_name: String, - pedantic_solver: bool, + project: Project, + pedantic_solving: bool, foreign_call_resolver_url: Option, ) -> Result { - let debug_artifact = - DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; - - let solver = Bn254BlackBoxSolver(pedantic_solver); - let mut session = DapSession::new( - server, - &solver, - &program, - &debug_artifact, - initial_witness, - root_path, - package_name, - foreign_call_resolver_url, - ); + let debug_artifact = DebugArtifact { + debug_symbols: project.compiled_program.debug.clone(), + file_map: project.compiled_program.file_map.clone(), + }; + + let solver = Bn254BlackBoxSolver(pedantic_solving); + let mut session = + DapSession::new(server, &solver, &project, &debug_artifact, foreign_call_resolver_url); session.run_loop()?; if session.context.is_solved() { diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 751c8f3f873..1ac5b2fb43e 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -7,52 +7,26 @@ mod repl; mod source_code_printer; use std::io::{Read, Write}; -use std::path::PathBuf; use ::dap::errors::ServerError; use ::dap::server::Server; -use acvm::acir::native_types::WitnessMap; -use acvm::FieldElement; pub use context::DebugExecutionResult; - -use noirc_driver::CompiledProgram; +pub use context::Project; pub fn run_repl_session( - program: CompiledProgram, - initial_witness: WitnessMap, + project: Project, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: PathBuf, - package_name: String, pedantic_solving: bool, ) -> DebugExecutionResult { - repl::run( - program, - initial_witness, - raw_source_printing, - foreign_call_resolver_url, - root_path, - package_name, - pedantic_solving, - ) + repl::run(project, raw_source_printing, foreign_call_resolver_url, pedantic_solving) } pub fn run_dap_loop( server: &mut Server, - program: CompiledProgram, - initial_witness: WitnessMap, - root_path: PathBuf, - package_name: String, + project: Project, pedantic_solving: bool, foreign_call_resolver_url: Option, ) -> Result { - dap::run_session( - server, - program, - initial_witness, - root_path, - package_name, - pedantic_solving, - foreign_call_resolver_url, - ) + dap::run_session(server, project, pedantic_solving, foreign_call_resolver_url) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index f816d49cacb..ec441da4702 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,5 +1,6 @@ use crate::context::{DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame}; use crate::debug::{DebugCommandAPI, DebugCommandAPIResult, Debugger}; +use crate::Project; use acvm::AcirField; use acvm::acir::brillig::BitSize; @@ -10,7 +11,6 @@ use acvm::brillig_vm::MemoryValue; use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::FieldElement; use nargo::{NargoError, PrintOutput}; -use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; @@ -18,7 +18,6 @@ use noirc_artifacts::debug::DebugArtifact; use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; -use std::path::PathBuf; use std::sync::mpsc::{self, Receiver, Sender}; use std::thread; @@ -551,28 +550,31 @@ impl<'a> ReplDebugger<'a> { } pub fn run( - program: CompiledProgram, - initial_witness: WitnessMap, + project: Project, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: PathBuf, - package_name: String, pedantic_solving: bool, ) -> DebugExecutionResult { - let debugger_circuits = program.program.functions.clone(); - let circuits = &program.program.functions; - let debugger_artifact = - DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; - let debug_artifact = DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; - let debugger_unconstrained_functions = program.program.unconstrained_functions.clone(); - let unconstrained_functions = &program.program.unconstrained_functions; + let program = project.compiled_program.program; + let debugger_circuits = program.functions.clone(); + let circuits = &program.functions; + let debugger_artifact = DebugArtifact { + debug_symbols: project.compiled_program.debug.clone(), + file_map: project.compiled_program.file_map.clone(), + }; + let debug_artifact = DebugArtifact { + debug_symbols: project.compiled_program.debug, + file_map: project.compiled_program.file_map, + }; + let debugger_unconstrained_functions = program.unconstrained_functions.clone(); + let unconstrained_functions = &program.unconstrained_functions; let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, foreign_call_resolver_url, &debugger_artifact, - Some(root_path), - package_name, + Some(project.root_dir), + project.package_name, )); let (command_tx, command_rx) = mpsc::channel::(); @@ -581,7 +583,7 @@ pub fn run( let debugger = Debugger { circuits: debugger_circuits, debug_artifact: &debugger_artifact, - initial_witness, + initial_witness: project.initial_witness, unconstrained_functions: debugger_unconstrained_functions, pedantic_solving, }; diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 9c0142901b7..52f15c024ed 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,6 +1,4 @@ -use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; -use acvm::acir::native_types::WitnessMap; use clap::Args; use dap::errors::ServerError; use dap::events::OutputEventBody; @@ -10,14 +8,14 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; -use noir_debugger::DebugExecutionResult; +use noir_debugger::{DebugExecutionResult, Project}; use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; use std::io::{BufReader, BufWriter, Read, Write}; -use std::path::{Path, PathBuf}; +use std::path::Path; use dap::requests::Command; use dap::responses::ResponseBody; @@ -145,13 +143,7 @@ fn compile_test( .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; Ok((program, test)) } -//TODO: find a better name -struct Project { - program: CompiledProgram, - initial_witness: WitnessMap, - root_dir: PathBuf, - package_name: String, -} + fn load_and_compile_project( project_folder: &str, package: Option<&str>, @@ -191,7 +183,7 @@ fn load_and_compile_project( .map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?; let project = Project { - program: compiled_program, + compiled_program, initial_witness, root_dir: workspace.root_dir.clone(), package_name: package.name.to_string(), @@ -267,15 +259,12 @@ fn loop_uninitialized_dap( ) { Ok((project, test)) => { server.respond(req.ack()?)?; - let abi = project.program.abi.clone(); - let debug = project.program.debug.clone(); + let abi = project.compiled_program.abi.clone(); + let debug = project.compiled_program.debug.clone(); let result = noir_debugger::run_dap_loop( &mut server, - project.program, - project.initial_witness, - project.root_dir, - project.package_name, + project, pedantic_solving, oracle_resolver_url, )?; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 2a057b8ae94..710292a4438 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,5 +1,4 @@ use std::path::Path; -use std::path::PathBuf; use std::time::Duration; use acvm::FieldElement; @@ -19,7 +18,7 @@ use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all, prepare use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noir_debugger::DebugExecutionResult; +use noir_debugger::{DebugExecutionResult, Project}; use noirc_abi::Abi; use noirc_driver::{ CompileOptions, CompiledProgram, compile_no_check, file_manager_with_stdlib, @@ -411,14 +410,17 @@ fn run_async( println!("[{}] Starting debugger", package.name); let initial_witness = parse_initial_witness(package, &run_params.prover_name, abi)?; - let result = debug_program( - program, + let project = Project { + compiled_program: program, initial_witness, + root_dir: workspace.root_dir.clone(), + package_name: package.name.to_string(), + }; + let result = debug_program( + project, run_params.pedantic_solving, run_params.raw_source_printing, run_params.oracle_resolver_url, - workspace.root_dir.clone(), - package.name.to_string(), ); if let DebugExecutionResult::Solved(ref witness_stack) = result { @@ -470,21 +472,15 @@ fn parse_initial_witness( } pub(crate) fn debug_program( - compiled_program: CompiledProgram, - initial_witness: WitnessMap, + project: Project, pedantic_solving: bool, raw_source_printing: bool, foreign_call_resolver_url: Option, - root_path: PathBuf, - package_name: String, ) -> DebugExecutionResult { noir_debugger::run_repl_session( - compiled_program, - initial_witness, + project, raw_source_printing, foreign_call_resolver_url, - root_path, - package_name, pedantic_solving, ) } From 0d5ce43116e0bc1351e25ba8720306fb44c0a6bd Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 5 Mar 2025 17:08:12 -0300 Subject: [PATCH 30/60] Move RunParams to debugger and use it in repl and dap interfaces --- tooling/debugger/src/context.rs | 8 ++++ tooling/debugger/src/dap.rs | 9 ++-- tooling/debugger/src/lib.rs | 16 +++---- tooling/debugger/src/repl.rs | 17 +++---- tooling/nargo_cli/src/cli/dap_cmd.rs | 9 ++-- tooling/nargo_cli/src/cli/debug_cmd.rs | 66 +++++++++++--------------- 6 files changed, 59 insertions(+), 66 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index e2e3109ad43..d3ac43ad685 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -282,6 +282,14 @@ pub struct Project { pub package_name: String, } +#[derive(Debug, Clone)] + +pub struct RunParams { + pub pedantic_solving: bool, + pub raw_source_printing: bool, + pub oracle_resolver_url: Option, +} + pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { pub(crate) acvm: ACVM<'a, FieldElement, B>, current_circuit_id: u32, diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 930d3a6a958..90b5f970382 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -5,7 +5,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::{NargoError, PrintOutput}; -use crate::context::{DebugCommandResult, DebugLocation}; +use crate::context::{DebugCommandResult, DebugLocation, RunParams}; use crate::context::{DebugContext, DebugExecutionResult}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use crate::Project; @@ -629,17 +629,16 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn run_session( server: &mut Server, project: Project, - pedantic_solving: bool, - foreign_call_resolver_url: Option, + run_params: RunParams, ) -> Result { let debug_artifact = DebugArtifact { debug_symbols: project.compiled_program.debug.clone(), file_map: project.compiled_program.file_map.clone(), }; - let solver = Bn254BlackBoxSolver(pedantic_solving); + let solver = Bn254BlackBoxSolver(run_params.pedantic_solving); let mut session = - DapSession::new(server, &solver, &project, &debug_artifact, foreign_call_resolver_url); + DapSession::new(server, &solver, &project, &debug_artifact, run_params.oracle_resolver_url); session.run_loop()?; if session.context.is_solved() { diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 1ac5b2fb43e..cdfd5d24ab5 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -10,23 +10,19 @@ use std::io::{Read, Write}; use ::dap::errors::ServerError; use ::dap::server::Server; +// TODO: extract these pub structs to its own module pub use context::DebugExecutionResult; pub use context::Project; +pub use context::RunParams; -pub fn run_repl_session( - project: Project, - raw_source_printing: bool, - foreign_call_resolver_url: Option, - pedantic_solving: bool, -) -> DebugExecutionResult { - repl::run(project, raw_source_printing, foreign_call_resolver_url, pedantic_solving) +pub fn run_repl_session(project: Project, run_params: RunParams) -> DebugExecutionResult { + repl::run(project, run_params) } pub fn run_dap_loop( server: &mut Server, project: Project, - pedantic_solving: bool, - foreign_call_resolver_url: Option, + run_params: RunParams, ) -> Result { - dap::run_session(server, project, pedantic_solving, foreign_call_resolver_url) + dap::run_session(server, project, run_params) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index ec441da4702..cf584ed3236 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,4 +1,6 @@ -use crate::context::{DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame}; +use crate::context::{ + DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame, RunParams, +}; use crate::debug::{DebugCommandAPI, DebugCommandAPIResult, Debugger}; use crate::Project; @@ -549,12 +551,7 @@ impl<'a> ReplDebugger<'a> { } } -pub fn run( - project: Project, - raw_source_printing: bool, - foreign_call_resolver_url: Option, - pedantic_solving: bool, -) -> DebugExecutionResult { +pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { let program = project.compiled_program.program; let debugger_circuits = program.functions.clone(); let circuits = &program.functions; @@ -571,7 +568,7 @@ pub fn run( let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, - foreign_call_resolver_url, + run_params.oracle_resolver_url, &debugger_artifact, Some(project.root_dir), project.package_name, @@ -585,7 +582,7 @@ pub fn run( debug_artifact: &debugger_artifact, initial_witness: project.initial_witness, unconstrained_functions: debugger_unconstrained_functions, - pedantic_solving, + pedantic_solving: run_params.pedantic_solving, }; debugger.start_debugging(command_rx, result_tx, foreign_call_executor); }); @@ -594,7 +591,7 @@ pub fn run( circuits, &debug_artifact, unconstrained_functions, - raw_source_printing, + run_params.raw_source_printing, command_tx, result_rx, )); diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 52f15c024ed..c48e4e7ecaf 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -8,7 +8,7 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; -use noir_debugger::{DebugExecutionResult, Project}; +use noir_debugger::{DebugExecutionResult, Project, RunParams}; use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::debug_info::DebugInfo; @@ -265,8 +265,11 @@ fn loop_uninitialized_dap( let result = noir_debugger::run_dap_loop( &mut server, project, - pedantic_solving, - oracle_resolver_url, + RunParams { + oracle_resolver_url, + pedantic_solving, + raw_source_printing: false, // TODO: should we get this from dap args? + }, )?; if let Some(test) = test { diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 710292a4438..f955aa8439b 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -18,7 +18,7 @@ use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all, prepare use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noir_debugger::{DebugExecutionResult, Project}; +use noir_debugger::{DebugExecutionResult, Project, RunParams}; use noirc_abi::Abi; use noirc_driver::{ CompileOptions, CompiledProgram, compile_no_check, file_manager_with_stdlib, @@ -75,16 +75,11 @@ pub(crate) struct DebugCommand { oracle_resolver: Option, } -struct RunParams<'a> { +// TODO: find a better name +struct PackageParams<'a> { prover_name: String, witness_name: Option, target_dir: &'a Path, - // FIXME: perhaps this doesn't belong here - // since it is for configuring the Bn254BlackBoxSolver - // TODO: we should probably add the foreign call config in the same place - pedantic_solving: bool, - raw_source_printing: bool, - oracle_resolver_url: Option, } impl WorkspaceCommand for DebugCommand { @@ -106,10 +101,12 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr let acir_mode = args.acir_mode; let skip_instrumentation = args.skip_instrumentation.unwrap_or(acir_mode); - let run_params = RunParams { + let package_params = PackageParams { prover_name: args.prover_name, witness_name: args.witness_name, target_dir: &workspace.target_directory_path(), + }; + let run_params = RunParams { pedantic_solving: args.compile_options.pedantic_solving, raw_source_printing: args.raw_source_printing.unwrap_or(false), oracle_resolver_url: args.oracle_resolver, @@ -128,9 +125,9 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr compile_options_for_debugging(acir_mode, skip_instrumentation, None, args.compile_options); if let Some(test_name) = args.test_name { - debug_test(test_name, package, workspace, compile_options, run_params) + debug_test(test_name, package, workspace, compile_options, run_params, package_params) } else { - debug_main(package, workspace, compile_options, run_params) + debug_main(package, workspace, compile_options, run_params, package_params) } } @@ -162,6 +159,7 @@ fn debug_test_fn( package: &Package, compile_options: CompileOptions, run_params: RunParams, + package_params: PackageParams, ) -> TestResult { let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options); @@ -171,7 +169,8 @@ fn debug_test_fn( let debug = compiled_program.debug.clone(); // Run debugger - let debug_result = run_async(package, compiled_program, workspace, run_params); + let debug_result = + run_async(package, compiled_program, workspace, run_params, package_params); match debug_result { Ok(DebugExecutionResult::Solved(result)) => { @@ -291,6 +290,7 @@ fn debug_main( workspace: Workspace, compile_options: CompileOptions, run_params: RunParams, + package_params: PackageParams, ) -> Result<(), CliError> { let expression_width = get_target_width(package.expression_width, compile_options.expression_width); @@ -298,7 +298,7 @@ fn debug_main( let compiled_program = compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; - run_async(package, compiled_program, &workspace, run_params)?; + run_async(package, compiled_program, &workspace, run_params, package_params)?; Ok(()) } @@ -309,6 +309,7 @@ fn debug_test( workspace: Workspace, compile_options: CompileOptions, run_params: RunParams, + package_params: PackageParams, ) -> Result<(), CliError> { let (file_manager, mut parsed_files) = load_workspace_files(&workspace); @@ -319,8 +320,15 @@ fn debug_test( let test = get_test_function(crate_id, &context, &test_name)?; - let test_result = - debug_test_fn(&test, &mut context, &workspace, package, compile_options, run_params); + let test_result = debug_test_fn( + &test, + &mut context, + &workspace, + package, + compile_options, + run_params, + package_params, + ); print_test_result(test_result, &file_manager); Ok(()) @@ -401,6 +409,7 @@ fn run_async( program: CompiledProgram, workspace: &Workspace, run_params: RunParams, + package_params: PackageParams, ) -> Result { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); @@ -408,7 +417,7 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let initial_witness = parse_initial_witness(package, &run_params.prover_name, abi)?; + let initial_witness = parse_initial_witness(package, &package_params.prover_name, abi)?; let project = Project { compiled_program: program, @@ -416,12 +425,7 @@ fn run_async( root_dir: workspace.root_dir.clone(), package_name: package.name.to_string(), }; - let result = debug_program( - project, - run_params.pedantic_solving, - run_params.raw_source_printing, - run_params.oracle_resolver_url, - ); + let result = noir_debugger::run_repl_session(project, run_params); if let DebugExecutionResult::Solved(ref witness_stack) = result { println!("[{}] Circuit witness successfully solved", package.name); @@ -429,8 +433,8 @@ fn run_async( &package.name, witness_stack, abi, - run_params.witness_name, - &run_params.target_dir, + package_params.witness_name, + package_params.target_dir, )?; } @@ -470,17 +474,3 @@ fn parse_initial_witness( let initial_witness = abi.encode(&inputs_map, None)?; Ok(initial_witness) } - -pub(crate) fn debug_program( - project: Project, - pedantic_solving: bool, - raw_source_printing: bool, - foreign_call_resolver_url: Option, -) -> DebugExecutionResult { - noir_debugger::run_repl_session( - project, - raw_source_printing, - foreign_call_resolver_url, - pedantic_solving, - ) -} From 0277c3e0f2f905cf0b72d951b34191b7ac7b1f28 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 6 Mar 2025 12:25:29 -0300 Subject: [PATCH 31/60] Remove unnecessary get_foreign_call_resolver_url function --- tooling/debugger/src/foreign_calls.rs | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 79d143c03f1..ff29ca86e4f 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -45,14 +45,12 @@ impl DebugForeignCall { pub trait DebugForeignCallExecutor: ForeignCallExecutor { fn get_variables(&self) -> Vec>; fn current_stack_frame(&self) -> Option>; - fn get_foreign_call_resolver_url(&self) -> Option; fn restart(&mut self, artifact: &DebugArtifact); } #[derive(Default)] pub struct DefaultDebugForeignCallExecutor { pub debug_vars: DebugVars, - pub foreign_call_resolver_url: Option, } impl DefaultDebugForeignCallExecutor { @@ -81,16 +79,7 @@ impl DefaultDebugForeignCallExecutor { root_path: Option, package_name: String, ) -> impl DebugForeignCallExecutor + '_ { - Self::make( - output, - resolver_url.clone(), - DefaultDebugForeignCallExecutor { - foreign_call_resolver_url: resolver_url, - ..Self::default() - }, - root_path, - package_name, - ) + Self::make(output, resolver_url, Self::default(), root_path, package_name) } pub fn from_artifact<'a>( @@ -99,11 +88,8 @@ impl DefaultDebugForeignCallExecutor { artifact: &DebugArtifact, root_path: Option, package_name: String, - ) -> impl DebugForeignCallExecutor + use<'a> { - let mut ex = DefaultDebugForeignCallExecutor { - foreign_call_resolver_url: resolver_url.clone(), - ..Self::default() - }; + ) -> impl DebugForeignCallExecutor + use<'a> { + let mut ex = Self::default(); ex.load_artifact(artifact); Self::make(output, resolver_url, ex, root_path, package_name) } @@ -127,10 +113,6 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { self.debug_vars.current_stack_frame() } - fn get_foreign_call_resolver_url(&self) -> Option { - self.foreign_call_resolver_url.clone() - } - fn restart(&mut self, artifact: &DebugArtifact) { self.debug_vars = DebugVars::default(); self.load_artifact(artifact); @@ -237,9 +219,6 @@ where fn current_stack_frame(&self) -> Option> { self.handler().current_stack_frame() } - fn get_foreign_call_resolver_url(&self) -> Option { - self.handler().get_foreign_call_resolver_url() - } fn restart(&mut self, artifact: &DebugArtifact) { self.handler.restart(artifact); } From 9351e1738548a9d9a45c2233822d94731defae85 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 6 Mar 2025 12:58:11 -0300 Subject: [PATCH 32/60] Some code enhancements --- tooling/debugger/src/debug.rs | 23 +++++++++-------------- tooling/debugger/src/repl.rs | 18 ++++++++++++++---- tooling/nargo_cli/src/cli/debug_cmd.rs | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs index 273fe2aeb39..7e1cbf47203 100644 --- a/tooling/debugger/src/debug.rs +++ b/tooling/debugger/src/debug.rs @@ -65,19 +65,19 @@ pub(super) enum DebugCommandAPI { Cont, } -pub struct Debugger<'a> { +pub struct AsyncDebugger<'a> { pub circuits: Vec>, pub debug_artifact: &'a DebugArtifact, pub initial_witness: WitnessMap, pub unconstrained_functions: Vec>, pub pedantic_solving: bool, + pub command_rx: Receiver, + pub result_tx: Sender, } -impl<'a> Debugger<'a> { +impl<'a> AsyncDebugger<'a> { pub(super) fn start_debugging( &self, - command_rx: Receiver, - result_tx: Sender, foreign_call_executor: Box, ) { let blackbox_solver = Bn254BlackBoxSolver(self.pedantic_solving); @@ -93,7 +93,7 @@ impl<'a> Debugger<'a> { println!("Debugger ready for receiving messages.."); loop { // recv blocks until it receives message - if let Ok(received) = command_rx.recv() { + if let Ok(received) = self.command_rx.recv() { let result = match received { DebugCommandAPI::GetCurrentDebugLocation => { DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) @@ -178,22 +178,17 @@ impl<'a> Debugger<'a> { } DebugCommandAPI::Finalize => { let witness_stack = context.finalize(); - let _ = result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); - // We need to stop the 'event loop' since `finalize` consumes the context - drop(result_tx); - drop(command_rx); + let _ = + self.result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); break; } }; - let Ok(()) = result_tx.send(result) else { - drop(result_tx); - drop(command_rx); + let Ok(()) = self.result_tx.send(result) else { + println!("Downstream channel closed. Terminating debugger"); break; }; } else { println!("Upstream channel closed. Terminating debugger"); - drop(result_tx); - drop(command_rx); break; } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index cf584ed3236..5bb40985746 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,7 +1,7 @@ use crate::context::{ DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame, RunParams, }; -use crate::debug::{DebugCommandAPI, DebugCommandAPIResult, Debugger}; +use crate::debug::{AsyncDebugger, DebugCommandAPI, DebugCommandAPIResult}; use crate::Project; use acvm::AcirField; @@ -60,7 +60,7 @@ impl<'a> ReplDebugger<'a> { command_sender: Sender, result_receiver: Receiver, ) -> Self { - let last_result = DebugCommandResult::Ok; // TODO: handle circuit with no opcodes + let last_result = DebugCommandResult::Ok; Self { command_sender, @@ -73,6 +73,13 @@ impl<'a> ReplDebugger<'a> { } } + fn initialize(&mut self) { + // handle circuit with no opcodes + if self.get_current_debug_location().is_none() { + self.last_result = DebugCommandResult::Done + } + } + fn call_debugger(&self, command: DebugCommandAPI) -> DebugCommandAPIResult { // using match here instead of unwrap() to give more useful panic messages match self.command_sender.send(command) { @@ -577,14 +584,16 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { let (command_tx, command_rx) = mpsc::channel::(); let (result_tx, result_rx) = mpsc::channel::(); thread::spawn(move || { - let debugger = Debugger { + let debugger = AsyncDebugger { circuits: debugger_circuits, debug_artifact: &debugger_artifact, initial_witness: project.initial_witness, unconstrained_functions: debugger_unconstrained_functions, pedantic_solving: run_params.pedantic_solving, + command_rx, + result_tx, }; - debugger.start_debugging(command_rx, result_tx, foreign_call_executor); + debugger.start_debugging(foreign_call_executor); }); let context = RefCell::new(ReplDebugger::new( @@ -597,6 +606,7 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { )); let ref_context = &context; + ref_context.borrow_mut().initialize(); ref_context.borrow().show_current_vm_status(); let mut repl = Repl::builder() diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index f955aa8439b..ae1c9faab5e 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -180,7 +180,7 @@ fn debug_test_fn( test_status_program_compile_pass(&test.function, &abi, &debug, &Err(error)) } Ok(DebugExecutionResult::Incomplete) => TestStatus::Fail { - message: "Debugger halted. Incomplete execution".to_string(), + message: "Incomplete execution. Debugger halted".to_string(), error_diagnostic: None, }, Err(error) => TestStatus::Fail { From 6a9d509f0909faed2eb11f11b0a174d8bf7a0adc Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 6 Mar 2025 15:34:10 -0300 Subject: [PATCH 33/60] cargo fmt --- tooling/debugger/src/context.rs | 2 +- tooling/debugger/src/dap.rs | 2 +- tooling/debugger/src/debug.rs | 4 ++-- tooling/debugger/src/repl.rs | 4 ++-- tooling/nargo/src/ops/execute.rs | 2 +- tooling/nargo/src/ops/mod.rs | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index d3ac43ad685..a0b30021f3a 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -12,8 +12,8 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::errors::{execution_error_from, ExecutionError, Location}; use nargo::NargoError; +use nargo::errors::{ExecutionError, Location, execution_error_from}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::{CompiledProgram, DebugFile}; diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 90b5f970382..345affca42a 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -5,10 +5,10 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::{NargoError, PrintOutput}; +use crate::Project; use crate::context::{DebugCommandResult, DebugLocation, RunParams}; use crate::context::{DebugContext, DebugExecutionResult}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; -use crate::Project; use dap::errors::ServerError; use dap::events::StoppedEventBody; diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs index 7e1cbf47203..9bde7b4b233 100644 --- a/tooling/debugger/src/debug.rs +++ b/tooling/debugger/src/debug.rs @@ -1,11 +1,11 @@ use acvm::{ + FieldElement, acir::{ brillig::BitSize, - circuit::{brillig::BrilligBytecode, Circuit, Opcode}, + circuit::{Circuit, Opcode, brillig::BrilligBytecode}, native_types::{Witness, WitnessMap, WitnessStack}, }, brillig_vm::MemoryValue, - FieldElement, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::errors::Location; diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 5bb40985746..7e8969d4f8c 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,17 +1,17 @@ +use crate::Project; use crate::context::{ DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame, RunParams, }; use crate::debug::{AsyncDebugger, DebugCommandAPI, DebugCommandAPIResult}; -use crate::Project; use acvm::AcirField; +use acvm::FieldElement; use acvm::acir::brillig::BitSize; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; -use acvm::FieldElement; use nargo::{NargoError, PrintOutput}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index c54f2b84df5..3e25b67fa94 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -8,7 +8,7 @@ use acvm::{AcirField, BlackBoxFunctionSolver}; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::NargoError; -use crate::errors::{execution_error_from, ExecutionError}; +use crate::errors::{ExecutionError, execution_error_from}; use crate::foreign_calls::ForeignCallExecutor; struct ProgramExecutor<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 0b523b5c033..32bef27fbee 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -8,8 +8,8 @@ pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; pub use self::test::{ - check_expected_failure_message, run_test, test_status_program_compile_fail, - test_status_program_compile_pass, TestStatus, + TestStatus, check_expected_failure_message, run_test, test_status_program_compile_fail, + test_status_program_compile_pass, }; mod check; From 06cff995f8982632f8f8caa18a24ef6a7f3333df Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 7 Mar 2025 19:32:12 -0300 Subject: [PATCH 34/60] unify AsyncDebugger and ReplDebugger --- tooling/debugger/src/debug.rs | 196 ---------- tooling/debugger/src/lib.rs | 1 - tooling/debugger/src/repl.rs | 705 +++++++++++++++++++--------------- 3 files changed, 394 insertions(+), 508 deletions(-) delete mode 100644 tooling/debugger/src/debug.rs diff --git a/tooling/debugger/src/debug.rs b/tooling/debugger/src/debug.rs deleted file mode 100644 index 9bde7b4b233..00000000000 --- a/tooling/debugger/src/debug.rs +++ /dev/null @@ -1,196 +0,0 @@ -use acvm::{ - FieldElement, - acir::{ - brillig::BitSize, - circuit::{Circuit, Opcode, brillig::BrilligBytecode}, - native_types::{Witness, WitnessMap, WitnessStack}, - }, - brillig_vm::MemoryValue, -}; -use bn254_blackbox_solver::Bn254BlackBoxSolver; -use nargo::errors::Location; -use noirc_artifacts::debug::DebugArtifact; -use std::sync::mpsc::{Receiver, Sender}; - -use crate::{ - context::{DebugCommandResult, DebugContext, DebugLocation, DebugStackFrame}, - foreign_calls::DebugForeignCallExecutor, -}; - -// TODO: revisit this mod name - -#[derive(Debug)] -pub(super) enum DebugCommandAPIResult { - DebugCommandResult(DebugCommandResult), - DebugLocation(Option), - Opcodes(Vec>), - Locations(Vec), - DebugLocations(Vec), - Bool(bool), - WitnessMap(WitnessMap), - MemoryValue(Option>>), - Unit(()), - Variables(Vec>), - WitnessStack(WitnessStack), - Field(Option), -} - -#[derive(Debug)] -pub(super) enum DebugCommandAPI { - GetCurrentDebugLocation, - GetOpcodes, - GetOpcodesOfCircuit(u32), - GetSourceLocationForDebugLocation(DebugLocation), - GetCallStack, - IsBreakpointSet(DebugLocation), - IsValidDebugLocation(DebugLocation), - AddBreakpoint(DebugLocation), - DeleteBreakpoint(DebugLocation), - GetWitnessMap, - IsExecutingBrillig, - GetBrilligMemory, - WriteBrilligMemory(usize, FieldElement, BitSize), - OverwriteWitness(Witness, FieldElement), - GetVariables, - IsSolved, - Restart, - Finalize, - FindOpcodeAtCurrentFileLine(i64), - // execution control - StepAcirOpcode, - StepIntoOpcode, - NextInto, - NextOver, - NextOut, - Cont, -} - -pub struct AsyncDebugger<'a> { - pub circuits: Vec>, - pub debug_artifact: &'a DebugArtifact, - pub initial_witness: WitnessMap, - pub unconstrained_functions: Vec>, - pub pedantic_solving: bool, - pub command_rx: Receiver, - pub result_tx: Sender, -} - -impl<'a> AsyncDebugger<'a> { - pub(super) fn start_debugging( - &self, - foreign_call_executor: Box, - ) { - let blackbox_solver = Bn254BlackBoxSolver(self.pedantic_solving); - let mut context = DebugContext::new( - &blackbox_solver, - &self.circuits, - self.debug_artifact, - self.initial_witness.clone(), - foreign_call_executor, - &self.unconstrained_functions, - ); - - println!("Debugger ready for receiving messages.."); - loop { - // recv blocks until it receives message - if let Ok(received) = self.command_rx.recv() { - let result = match received { - DebugCommandAPI::GetCurrentDebugLocation => { - DebugCommandAPIResult::DebugLocation(context.get_current_debug_location()) - } - DebugCommandAPI::GetOpcodes => { - DebugCommandAPIResult::Opcodes(context.get_opcodes().to_owned()) - } - DebugCommandAPI::GetOpcodesOfCircuit(circuit_id) => { - DebugCommandAPIResult::Opcodes( - context.get_opcodes_of_circuit(circuit_id).to_owned(), - ) - } - DebugCommandAPI::GetSourceLocationForDebugLocation(debug_location) => { - DebugCommandAPIResult::Locations( - context.get_source_location_for_debug_location(&debug_location), - ) - } - DebugCommandAPI::GetCallStack => { - DebugCommandAPIResult::DebugLocations(context.get_call_stack()) - } - DebugCommandAPI::IsBreakpointSet(debug_location) => { - DebugCommandAPIResult::Bool(context.is_breakpoint_set(&debug_location)) - } - DebugCommandAPI::IsValidDebugLocation(debug_location) => { - DebugCommandAPIResult::Bool( - context.is_valid_debug_location(&debug_location), - ) - } - DebugCommandAPI::AddBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.add_breakpoint(debug_location)) - } - DebugCommandAPI::DeleteBreakpoint(debug_location) => { - DebugCommandAPIResult::Bool(context.delete_breakpoint(&debug_location)) - } - DebugCommandAPI::Restart => { - context.restart(); - DebugCommandAPIResult::Unit(()) - } - DebugCommandAPI::GetWitnessMap => { - DebugCommandAPIResult::WitnessMap(context.get_witness_map().clone()) - } - DebugCommandAPI::IsExecutingBrillig => { - DebugCommandAPIResult::Bool(context.is_executing_brillig()) - } - DebugCommandAPI::GetBrilligMemory => DebugCommandAPIResult::MemoryValue( - context.get_brillig_memory().map(|values| values.to_vec()), - ), - DebugCommandAPI::WriteBrilligMemory(ptr, value, bit_size) => { - context.write_brillig_memory(ptr, value, bit_size); - DebugCommandAPIResult::Unit(()) - } - DebugCommandAPI::OverwriteWitness(witness, value) => { - DebugCommandAPIResult::Field(context.overwrite_witness(witness, value)) - } - - DebugCommandAPI::GetVariables => DebugCommandAPIResult::Variables( - context.get_variables().iter().map(DebugStackFrame::from).collect(), - ), - DebugCommandAPI::IsSolved => DebugCommandAPIResult::Bool(context.is_solved()), - DebugCommandAPI::StepAcirOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_acir_opcode()) - } - DebugCommandAPI::StepIntoOpcode => { - DebugCommandAPIResult::DebugCommandResult(context.step_into_opcode()) - } - DebugCommandAPI::NextInto => { - DebugCommandAPIResult::DebugCommandResult(context.next_into()) - } - DebugCommandAPI::NextOver => { - DebugCommandAPIResult::DebugCommandResult(context.next_over()) - } - DebugCommandAPI::NextOut => { - DebugCommandAPIResult::DebugCommandResult(context.next_out()) - } - DebugCommandAPI::Cont => { - DebugCommandAPIResult::DebugCommandResult(context.cont()) - } - DebugCommandAPI::FindOpcodeAtCurrentFileLine(line) => { - DebugCommandAPIResult::DebugLocation( - context.find_opcode_at_current_file_line(line), - ) - } - DebugCommandAPI::Finalize => { - let witness_stack = context.finalize(); - let _ = - self.result_tx.send(DebugCommandAPIResult::WitnessStack(witness_stack)); - break; - } - }; - let Ok(()) = self.result_tx.send(result) else { - println!("Downstream channel closed. Terminating debugger"); - break; - }; - } else { - println!("Upstream channel closed. Terminating debugger"); - break; - } - } - } -} diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index cdfd5d24ab5..3f1393806f1 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -1,6 +1,5 @@ mod context; mod dap; -mod debug; pub mod errors; mod foreign_calls; mod repl; diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 7e8969d4f8c..a1aff0f210b 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,104 +1,242 @@ use crate::Project; use crate::context::{ - DebugCommandResult, DebugExecutionResult, DebugLocation, DebugStackFrame, RunParams, + DebugCommandResult, DebugContext, DebugExecutionResult, DebugLocation, DebugStackFrame, + RunParams, }; -use crate::debug::{AsyncDebugger, DebugCommandAPI, DebugCommandAPIResult}; - -use acvm::AcirField; -use acvm::FieldElement; -use acvm::acir::brillig::BitSize; -use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; -use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; -use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; -use acvm::brillig_vm::MemoryValue; -use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; -use nargo::{NargoError, PrintOutput}; +use nargo::PrintOutput; +use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; use easy_repl::{CommandStatus, Repl, command}; -use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; use std::sync::mpsc::{self, Receiver, Sender}; use std::thread; -use crate::source_code_printer::print_source_code_location; - -pub struct ReplDebugger<'a> { - command_sender: Sender, - result_receiver: Receiver, - debug_artifact: &'a DebugArtifact, - last_result: DebugCommandResult, +use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; +use acvm::{ + AcirField, FieldElement, + acir::{ + brillig::BitSize, + circuit::{ + Circuit, Opcode, OpcodeLocation, + brillig::{BrilligBytecode, BrilligFunctionId}, + }, + native_types::{Witness, WitnessMap}, + }, + brillig_vm::MemoryValue, +}; +use bn254_blackbox_solver::Bn254BlackBoxSolver; +use noirc_printable_type::PrintableValueDisplay; - // ACIR functions to debug - circuits: &'a [Circuit], +use crate::{ + foreign_calls::DebugForeignCallExecutor, source_code_printer::print_source_code_location, +}; - // Brillig functions referenced from the ACIR circuits above - unconstrained_functions: &'a [BrilligBytecode], +type Context<'a> = DebugContext<'a, Bn254BlackBoxSolver>; + +#[derive(Debug, Clone)] +pub(super) enum DebugCommandAPI { + AddBreakpoint(DebugLocation), + AddBreakpointAtLine(i64), + DeleteBreakpoint(DebugLocation), + Restart, + StepAcirOpcode, + StepIntoOpcode, + NextInto, + NextOver, + NextOut, + Cont, + UpdateWitness(u32, String), + WriteBrilligMemory(usize, String, u32), + ShowVariables, + ShowWitnessMap, + ShowWitness(u32), + ShowBrilligMemory, + ShowCurrentCallStack, + ShowCurrentVmStatus, + ShowOpcodes, + Terminate, +} - // whether to print the source without highlighting, pretty-printing, - // or line numbers - raw_source_printing: bool, +#[derive(Debug)] +pub(super) enum DebuggerStatus { + Idle, + Busy, + Error, + Final(DebugExecutionResult), } -macro_rules! extract_value { - ($e:expr, $p:path) => { - match $e { - $p(value) => Some(value), - _ => None, - } - }; +pub struct AsyncDebugger<'a> { + circuits: Vec>, + debug_artifact: &'a DebugArtifact, + initial_witness: WitnessMap, + unconstrained_functions: Vec>, + command_receiver: Receiver, + status_sender: Sender, + last_result: DebugCommandResult, + pedantic_solving: bool, + raw_source_printing: bool, } -impl<'a> ReplDebugger<'a> { +impl<'a> AsyncDebugger<'a> { pub fn new( - circuits: &'a [Circuit], + compiled_program: &CompiledProgram, debug_artifact: &'a DebugArtifact, - unconstrained_functions: &'a [BrilligBytecode], + initial_witness: WitnessMap, + status_sender: Sender, + command_receiver: Receiver, raw_source_printing: bool, - command_sender: Sender, - result_receiver: Receiver, + pedantic_solving: bool, ) -> Self { let last_result = DebugCommandResult::Ok; Self { - command_sender, - result_receiver, - circuits, + command_receiver, + status_sender, + circuits: compiled_program.program.functions.clone(), debug_artifact, last_result, - unconstrained_functions, + unconstrained_functions: compiled_program.program.unconstrained_functions.clone(), raw_source_printing, + initial_witness, + pedantic_solving, } } - fn initialize(&mut self) { - // handle circuit with no opcodes - if self.get_current_debug_location().is_none() { + fn send_status(&mut self, status: DebuggerStatus) { + self.status_sender.send(status).expect("Downstream channel closed") + } + fn mark_busy(&mut self) { + self.send_status(DebuggerStatus::Busy) + } + fn mark_idle(&mut self) { + self.send_status(DebuggerStatus::Idle) + } + fn mark_error(&mut self) { + self.send_status(DebuggerStatus::Error) + } + + pub(super) fn start_debugging( + mut self, + foreign_call_executor: Box, + ) { + let blackbox_solver = &Bn254BlackBoxSolver(self.pedantic_solving); + let circuits = &self.circuits.clone(); + let unconstrained_functions = &self.unconstrained_functions.clone(); + let mut context = DebugContext::new( + blackbox_solver, + circuits, + self.debug_artifact, + self.initial_witness.clone(), + foreign_call_executor, + unconstrained_functions, + ); + + if context.get_current_debug_location().is_none() { + // handle circuit with no opcodes self.last_result = DebugCommandResult::Done } - } - fn call_debugger(&self, command: DebugCommandAPI) -> DebugCommandAPIResult { - // using match here instead of unwrap() to give more useful panic messages - match self.command_sender.send(command) { - Ok(_) => match self.result_receiver.recv() { - Ok(result) => result, - Err(_) => panic!("Debugger closed connection unexpectedly"), - }, - Err(_) => panic!("Could not communicate with debugger"), + println!("Debugger ready for receiving messages.."); + self.mark_idle(); + loop { + // recv blocks until it receives message + if let Ok(received) = self.command_receiver.recv() { + self.mark_busy(); + match received { + DebugCommandAPI::AddBreakpoint(debug_location) => { + Self::add_breakpoint_at(&mut context, debug_location); + self.mark_idle(); + } + DebugCommandAPI::DeleteBreakpoint(debug_location) => { + Self::delete_breakpoint_at(&mut context, debug_location); + self.mark_idle(); + } + DebugCommandAPI::Restart => { + self.restart_session(&mut context); + self.mark_idle(); + } + DebugCommandAPI::WriteBrilligMemory(index, value, bit_size) => { + Self::write_brillig_memory(&mut context, index, value, bit_size); + self.mark_idle(); + } + DebugCommandAPI::UpdateWitness(index, value) => { + Self::update_witness(&mut context, index, value); + self.mark_idle(); + } + DebugCommandAPI::StepAcirOpcode => { + self.handle_step(&mut context, |context| context.step_acir_opcode()); + } + DebugCommandAPI::StepIntoOpcode => { + self.handle_step(&mut context, |context| context.step_into_opcode()) + } + DebugCommandAPI::NextInto => { + self.handle_step(&mut context, |context| context.next_into()) + } + DebugCommandAPI::NextOver => { + self.handle_step(&mut context, |context| context.next_over()) + } + DebugCommandAPI::NextOut => { + self.handle_step(&mut context, |context| context.next_out()) + } + DebugCommandAPI::Cont => self.handle_step(&mut context, |context| { + println!("(Continuing execution...)"); + context.cont() + }), + DebugCommandAPI::AddBreakpointAtLine(line_number) => { + Self::add_breakpoint_at_line(&mut context, line_number); + self.mark_idle(); + } + DebugCommandAPI::ShowVariables => { + Self::show_variables(&mut context); + self.mark_idle(); + } + DebugCommandAPI::ShowWitnessMap => { + Self::show_witness_map(&mut context); + self.mark_idle(); + } + DebugCommandAPI::ShowWitness(index) => { + Self::show_witness(&mut context, index); + self.mark_idle(); + } + DebugCommandAPI::ShowBrilligMemory => { + Self::show_brillig_memory(&mut context); + self.mark_idle(); + } + DebugCommandAPI::ShowCurrentCallStack => { + self.show_current_call_stack(&mut context); + self.mark_idle(); + } + DebugCommandAPI::ShowOpcodes => { + self.show_opcodes(&mut context); + self.mark_idle(); + } + DebugCommandAPI::ShowCurrentVmStatus => { + self.show_current_vm_status(&mut context); + self.mark_idle(); + } + DebugCommandAPI::Terminate => { + self.terminate(context); + break; + } + }; + } else { + println!("Upstream channel closed. Terminating debugger"); + break; + } } } - pub fn show_current_vm_status(&self) { - let location = self.get_current_debug_location(); + fn show_current_vm_status(&self, context: &mut Context<'_>) { + let location = context.get_current_debug_location(); match location { None => println!("Finished execution"), Some(location) => { let circuit_id = location.circuit_id; - let opcodes = self.get_opcodes_of_circuit(circuit_id); + let opcodes = context.get_opcodes_of_circuit(circuit_id); match &location.opcode_location { OpcodeLocation::Acir(ip) => { @@ -117,13 +255,7 @@ impl<'a> ReplDebugger<'a> { ); } } - let locations = extract_value!( - self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation( - location - )), - DebugCommandAPIResult::Locations - ) - .unwrap(); + let locations = context.get_source_location_for_debug_location(&location); print_source_code_location( self.debug_artifact, @@ -134,110 +266,13 @@ impl<'a> ReplDebugger<'a> { } } - fn send_execution_control_command(&self, command: DebugCommandAPI) -> DebugCommandResult { - extract_value!(self.call_debugger(command), DebugCommandAPIResult::DebugCommandResult) - .unwrap() - } - - // TODO: find a better name - fn send_bool_command(&self, command: DebugCommandAPI) -> bool { - extract_value!(self.call_debugger(command), DebugCommandAPIResult::Bool).unwrap() - } - - fn get_opcodes_of_circuit(&self, circuit_id: u32) -> Vec> { - extract_value!( - self.call_debugger(DebugCommandAPI::GetOpcodesOfCircuit(circuit_id)), - DebugCommandAPIResult::Opcodes - ) - .unwrap() - } - - fn get_current_debug_location(&self) -> Option { - extract_value!( - self.call_debugger(DebugCommandAPI::GetCurrentDebugLocation), - DebugCommandAPIResult::DebugLocation - ) - .unwrap() - } - - fn is_breakpoint_set(&self, debug_location: DebugLocation) -> bool { - self.send_bool_command(DebugCommandAPI::IsBreakpointSet(debug_location)) - } - - fn is_valid_debug_location(&self, debug_location: DebugLocation) -> bool { - self.send_bool_command(DebugCommandAPI::IsValidDebugLocation(debug_location)) - } - - fn add_breakpoint(&self, debug_location: DebugLocation) -> bool { - self.send_bool_command(DebugCommandAPI::AddBreakpoint(debug_location)) - } - - fn delete_breakpoint(&self, debug_location: DebugLocation) -> bool { - self.send_bool_command(DebugCommandAPI::DeleteBreakpoint(debug_location)) - } - - fn is_executing_brillig(&self) -> bool { - self.send_bool_command(DebugCommandAPI::IsExecutingBrillig) - } - fn get_brillig_memory(&self) -> Option>> { - extract_value!( - self.call_debugger(DebugCommandAPI::GetBrilligMemory), - DebugCommandAPIResult::MemoryValue - ) - .unwrap() - } - fn get_variables(&self) -> Vec> { - extract_value!( - self.call_debugger(DebugCommandAPI::GetVariables), - DebugCommandAPIResult::Variables - ) - .unwrap() - } - - fn overwrite_witness(&self, witness: Witness, value: FieldElement) -> Option { - extract_value!( - self.call_debugger(DebugCommandAPI::OverwriteWitness(witness, value)), - DebugCommandAPIResult::Field - ) - .unwrap() - } - - fn is_solved(&self) -> bool { - self.send_bool_command(DebugCommandAPI::IsSolved) - } - - fn restart_debugger(&self) { - extract_value!(self.call_debugger(DebugCommandAPI::Restart), DebugCommandAPIResult::Unit) - .unwrap() - } - - fn get_witness_map(&self) -> WitnessMap { - extract_value!( - self.call_debugger(DebugCommandAPI::GetWitnessMap), - DebugCommandAPIResult::WitnessMap - ) - .unwrap() - } - fn find_opcode_at_current_file_line(&self, line_number: i64) -> Option { - extract_value!( - self.call_debugger(DebugCommandAPI::FindOpcodeAtCurrentFileLine(line_number)), - DebugCommandAPIResult::DebugLocation - ) - .unwrap() - } - fn finalize(self) -> WitnessStack { - extract_value!( - self.call_debugger(DebugCommandAPI::Finalize), - DebugCommandAPIResult::WitnessStack - ) - .unwrap() - } - fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { - let opcodes = extract_value!( - self.call_debugger(DebugCommandAPI::GetOpcodes), - DebugCommandAPIResult::Opcodes - ) - .unwrap(); + fn show_stack_frame( + &self, + context: &mut Context<'_>, + index: usize, + debug_location: &DebugLocation, + ) { + let opcodes = context.get_opcodes(); match &debug_location.opcode_location { OpcodeLocation::Acir(instruction_pointer) => { println!( @@ -258,21 +293,12 @@ impl<'a> ReplDebugger<'a> { ); } } - let locations = extract_value!( - self.call_debugger(DebugCommandAPI::GetSourceLocationForDebugLocation(*debug_location)), - DebugCommandAPIResult::Locations - ) - .unwrap(); - + let locations = context.get_source_location_for_debug_location(debug_location); print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } - pub fn show_current_call_stack(&self) { - let call_stack = extract_value!( - self.call_debugger(DebugCommandAPI::GetCallStack), - DebugCommandAPIResult::DebugLocations - ) - .unwrap(); + fn show_current_call_stack(&mut self, context: &mut Context<'_>) { + let call_stack = context.get_call_stack(); if call_stack.is_empty() { println!("Finished execution. Call stack empty."); @@ -280,26 +306,26 @@ impl<'a> ReplDebugger<'a> { } for (i, frame_location) in call_stack.iter().enumerate() { - self.show_stack_frame(i, frame_location); + self.show_stack_frame(context, i, frame_location); } } - fn display_opcodes(&self) { + fn show_opcodes(&mut self, context: &mut Context<'_>) { for i in 0..self.circuits.len() { - self.display_opcodes_of_circuit(i as u32); + self.show_opcodes_of_circuit(context, i as u32); } } - fn display_opcodes_of_circuit(&self, circuit_id: u32) { + fn show_opcodes_of_circuit(&mut self, context: &mut Context<'_>, circuit_id: u32) { let current_opcode_location = - self.get_current_debug_location().and_then(|debug_location| { + context.get_current_debug_location().and_then(|debug_location| { if debug_location.circuit_id == circuit_id { Some(debug_location.opcode_location) } else { None } }); - let opcodes = self.get_opcodes_of_circuit(circuit_id); + let opcodes = context.get_opcodes_of_circuit(circuit_id); let current_acir_index = match current_opcode_location { Some(OpcodeLocation::Acir(ip)) => Some(ip), Some(OpcodeLocation::Brillig { acir_index, .. }) => Some(acir_index), @@ -312,7 +338,7 @@ impl<'a> ReplDebugger<'a> { let outer_marker = |acir_index| { if current_acir_index == Some(acir_index) { "->" - } else if self.is_breakpoint_set(DebugLocation { + } else if context.is_breakpoint_set(&DebugLocation { circuit_id, opcode_location: OpcodeLocation::Acir(acir_index), brillig_function_id: None, @@ -325,7 +351,7 @@ impl<'a> ReplDebugger<'a> { let brillig_marker = |acir_index, brillig_index, brillig_function_id| { if current_acir_index == Some(acir_index) && brillig_index == current_brillig_index { "->" - } else if self.is_breakpoint_set(DebugLocation { + } else if context.is_breakpoint_set(&DebugLocation { circuit_id, opcode_location: OpcodeLocation::Brillig { acir_index, brillig_index }, brillig_function_id: Some(brillig_function_id), @@ -367,138 +393,118 @@ impl<'a> ReplDebugger<'a> { } } - fn add_breakpoint_at(&mut self, location: DebugLocation) { - if !self.is_valid_debug_location(location) { + fn add_breakpoint_at(context: &mut Context<'_>, location: DebugLocation) { + if !context.is_valid_debug_location(&location) { println!("Invalid location {location}"); - } else if self.add_breakpoint(location) { + } else if context.add_breakpoint(location) { println!("Added breakpoint at {location}"); } else { println!("Breakpoint at {location} already set"); } } - fn add_breakpoint_at_line(&mut self, line_number: i64) { - let best_location = self.find_opcode_at_current_file_line(line_number); - + fn add_breakpoint_at_line(context: &mut Context<'_>, line_number: i64) { + let best_location = context.find_opcode_at_current_file_line(line_number); match best_location { Some(location) => { println!("Added breakpoint at line {}", line_number); - self.add_breakpoint_at(location) + Self::add_breakpoint_at(context, location); } None => println!("No opcode at line {}", line_number), } } - fn delete_breakpoint_at(&mut self, location: DebugLocation) { - if self.delete_breakpoint(location) { + fn delete_breakpoint_at(context: &mut Context<'_>, location: DebugLocation) { + if context.delete_breakpoint(&location) { println!("Breakpoint at {location} deleted"); } else { println!("Breakpoint at {location} not set"); } } - fn validate_in_progress(&self) -> bool { - match self.last_result { - DebugCommandResult::Ok | DebugCommandResult::BreakpointReached(..) => true, + fn handle_result(&mut self, result: DebugCommandResult) { + self.last_result = result; + match &self.last_result { DebugCommandResult::Done => { println!("Execution finished"); - false - } - DebugCommandResult::Error(ref error) => { - println!("ERROR: {}", error); - self.show_current_vm_status(); - false + self.mark_idle(); } - } - } - - fn handle_debug_command_result(&mut self, result: DebugCommandResult) { - match &result { + DebugCommandResult::Ok => self.mark_idle(), DebugCommandResult::BreakpointReached(location) => { println!("Stopped at breakpoint in opcode {}", location); + self.mark_idle(); } DebugCommandResult::Error(error) => { println!("ERROR: {}", error); + self.mark_error(); } - _ => (), } - self.last_result = result; - self.show_current_vm_status(); } - fn execution_control_command(&mut self, command: DebugCommandAPI) { - if self.validate_in_progress() { - if let DebugCommandAPI::Cont = command { - println!("(Continuing execution...)"); + fn handle_step(&mut self, context: &mut Context<'_>, step: F) + where + F: Fn(&mut Context) -> DebugCommandResult, + { + let should_execute = match self.last_result { + DebugCommandResult::Ok | DebugCommandResult::BreakpointReached(..) => true, + DebugCommandResult::Done => { + println!("Execution finished"); + false + } + DebugCommandResult::Error(ref error) => { + println!("ERROR: {}", error); + self.show_current_vm_status(context); + false } - let result = self.send_execution_control_command(command); - self.handle_debug_command_result(result); + }; + if should_execute { + let result = step(context); + self.show_current_vm_status(context); + self.handle_result(result); + } else { + self.mark_idle(); } } - fn step_acir_opcode(&mut self) { - self.execution_control_command(DebugCommandAPI::StepAcirOpcode); - } - - fn step_into_opcode(&mut self) { - self.execution_control_command(DebugCommandAPI::StepIntoOpcode); - } - - fn next_into(&mut self) { - self.execution_control_command(DebugCommandAPI::NextInto); - } - - fn next_over(&mut self) { - self.execution_control_command(DebugCommandAPI::NextOver); - } - - fn next_out(&mut self) { - self.execution_control_command(DebugCommandAPI::NextOut); - } - - fn cont(&mut self) { - self.execution_control_command(DebugCommandAPI::Cont); - } - - fn restart_session(&mut self) { - self.restart_debugger(); + fn restart_session(&mut self, context: &mut Context<'_>) { + context.restart(); self.last_result = DebugCommandResult::Ok; println!("Restarted debugging session."); - self.show_current_vm_status(); + self.show_current_vm_status(context); } - pub fn show_witness_map(&self) { - let witness_map = self.get_witness_map(); + fn show_witness_map(context: &mut Context<'_>) { + let witness_map = context.get_witness_map(); // NOTE: we need to clone() here to get the iterator for (witness, value) in witness_map.clone().into_iter() { println!("_{} = {value}", witness.witness_index()); } } - pub fn show_witness(&self, index: u32) { - if let Some(value) = self.get_witness_map().get_index(index) { + fn show_witness(context: &mut Context<'_>, index: u32) { + if let Some(value) = context.get_witness_map().get_index(index) { println!("_{} = {value}", index); } } - pub fn update_witness(&mut self, index: u32, value: String) { + fn update_witness(context: &mut Context<'_>, index: u32, value: String) { let Some(field_value) = FieldElement::try_from_str(&value) else { println!("Invalid witness value: {value}"); return; }; let witness = Witness::from(index); - _ = self.overwrite_witness(witness, field_value); + _ = context.overwrite_witness(witness, field_value); println!("_{} = {value}", index); } - pub fn show_brillig_memory(&self) { - if !self.is_executing_brillig() { + fn show_brillig_memory(context: &mut Context<'_>) { + if !context.is_executing_brillig() { println!("Not executing a Brillig block"); return; } - let Some(memory) = self.get_brillig_memory() else { + let Some(memory) = context.get_brillig_memory() else { // this can happen when just entering the Brillig block since ACVM // would have not initialized the Brillig VM yet; in fact, the // Brillig code may be skipped altogether @@ -516,8 +522,7 @@ impl<'a> ReplDebugger<'a> { println!("{index} = {}", value); } } - - pub fn write_brillig_memory(&mut self, index: usize, value: String, bit_size: u32) { + fn write_brillig_memory(context: &mut Context<'_>, index: usize, value: String, bit_size: u32) { let Some(field_value) = FieldElement::try_from_str(&value) else { println!("Invalid value: {value}"); return; @@ -528,19 +533,17 @@ impl<'a> ReplDebugger<'a> { return; }; - if !self.is_executing_brillig() { + if !context.is_executing_brillig() { println!("Not executing a Brillig block"); return; } - extract_value!( - self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, field_value, bit_size)), - DebugCommandAPIResult::Unit - ) - .unwrap(); + context.write_brillig_memory(index, field_value, bit_size); } - pub fn show_vars(&self) { - for frame in self.get_variables() { + fn show_variables(context: &mut Context<'_>) { + let variables: Vec> = + context.get_variables().iter().map(DebugStackFrame::from).collect(); + for frame in variables { println!("{}({})", frame.function_name, frame.function_params.join(", ")); for (var_name, value, var_type) in frame.variables.iter() { let printable_value = @@ -550,63 +553,151 @@ impl<'a> ReplDebugger<'a> { } } - fn last_error(self) -> Option> { - match self.last_result { - DebugCommandResult::Error(error) => Some(error), - _ => None, + fn terminate(self, context: Context<'_>) { + let result = if context.is_solved() { + let solved_witness_stack = context.finalize(); + DebugExecutionResult::Solved(solved_witness_stack) + } else { + match self.last_result { + // Expose the last known error + DebugCommandResult::Error(error) => DebugExecutionResult::Error(error), + _ => DebugExecutionResult::Incomplete, + } + }; + self.status_sender.send(DebuggerStatus::Final(result)).expect("Downstream channel closed") + } +} + +struct DebugCommander { + command_sender: Sender, + status_receiver: Receiver, +} +impl DebugCommander { + fn debugger_status(&self) -> DebuggerStatus { + self.status_receiver.recv().expect("Debugger closed connection unexpectedly") + } + + fn call_debugger(&self, command: DebugCommandAPI) { + self.when_not_busy( + || self.command_sender.send(command).expect("Could not communicate with debugger"), //{ + ) + } + + fn get_final_result(&self) -> DebugExecutionResult { + loop { + let status = self.debugger_status(); + if let DebuggerStatus::Final(result) = status { + return result; + } + } + } + + fn when_not_busy(&self, callback: F) + where + F: FnOnce(), + { + loop { + let status = self.debugger_status(); + let DebuggerStatus::Busy = status else { break }; } + callback(); + } + + pub fn step_acir_opcode(&self) { + self.call_debugger(DebugCommandAPI::StepAcirOpcode); + } + pub fn cont(&self) { + self.call_debugger(DebugCommandAPI::Cont); + } + pub fn step_into_opcode(&self) { + self.call_debugger(DebugCommandAPI::StepIntoOpcode); + } + pub fn next_into(&self) { + self.call_debugger(DebugCommandAPI::NextInto); + } + pub fn next_over(&self) { + self.call_debugger(DebugCommandAPI::NextOver); + } + pub fn next_out(&self) { + self.call_debugger(DebugCommandAPI::NextOut); + } + pub fn restart_session(&self) { + self.call_debugger(DebugCommandAPI::Restart); + } + pub fn add_breakpoint_at_line(&self, line_number: i64) { + self.call_debugger(DebugCommandAPI::AddBreakpointAtLine(line_number)); + } + pub fn add_breakpoint_at(&self, location: DebugLocation) { + self.call_debugger(DebugCommandAPI::AddBreakpoint(location)); + } + pub fn delete_breakpoint_at(&self, location: DebugLocation) { + self.call_debugger(DebugCommandAPI::DeleteBreakpoint(location)); + } + pub fn update_witness(&self, index: u32, value: String) { + self.call_debugger(DebugCommandAPI::UpdateWitness(index, value)); + } + pub fn write_brillig_memory(&self, index: usize, value: String, bit_size: u32) { + self.call_debugger(DebugCommandAPI::WriteBrilligMemory(index, value, bit_size)); + } + pub fn show_vars(&self) { + self.call_debugger(DebugCommandAPI::ShowVariables); + } + pub fn show_opcodes(&self) { + self.call_debugger(DebugCommandAPI::ShowOpcodes); + } + pub fn show_witness_map(&self) { + self.call_debugger(DebugCommandAPI::ShowWitnessMap); + } + pub fn show_witness(&self, index: u32) { + self.call_debugger(DebugCommandAPI::ShowWitness(index)); + } + pub fn show_brillig_memory(&self) { + self.call_debugger(DebugCommandAPI::ShowBrilligMemory); + } + pub fn show_current_call_stack(&self) { + self.call_debugger(DebugCommandAPI::ShowCurrentCallStack); + } + pub fn show_current_vm_status(&self) { + self.call_debugger(DebugCommandAPI::ShowCurrentVmStatus); + } + pub fn terminate(&self) { + self.call_debugger(DebugCommandAPI::Terminate); } } pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { - let program = project.compiled_program.program; - let debugger_circuits = program.functions.clone(); - let circuits = &program.functions; - let debugger_artifact = DebugArtifact { + let debug_artifact = DebugArtifact { debug_symbols: project.compiled_program.debug.clone(), file_map: project.compiled_program.file_map.clone(), }; - let debug_artifact = DebugArtifact { - debug_symbols: project.compiled_program.debug, - file_map: project.compiled_program.file_map, - }; - let debugger_unconstrained_functions = program.unconstrained_functions.clone(); - let unconstrained_functions = &program.unconstrained_functions; let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, run_params.oracle_resolver_url, - &debugger_artifact, + &debug_artifact, Some(project.root_dir), project.package_name, )); let (command_tx, command_rx) = mpsc::channel::(); - let (result_tx, result_rx) = mpsc::channel::(); + let (status_tx, status_rx) = mpsc::channel::(); thread::spawn(move || { - let debugger = AsyncDebugger { - circuits: debugger_circuits, - debug_artifact: &debugger_artifact, - initial_witness: project.initial_witness, - unconstrained_functions: debugger_unconstrained_functions, - pedantic_solving: run_params.pedantic_solving, + let debugger = AsyncDebugger::new( + &project.compiled_program, + &debug_artifact, + project.initial_witness, + status_tx, command_rx, - result_tx, - }; + run_params.raw_source_printing, + run_params.pedantic_solving, + ); debugger.start_debugging(foreign_call_executor); }); - let context = RefCell::new(ReplDebugger::new( - circuits, - &debug_artifact, - unconstrained_functions, - run_params.raw_source_printing, - command_tx, - result_rx, - )); + let context = + RefCell::new(DebugCommander { command_sender: command_tx, status_receiver: status_rx }); let ref_context = &context; - ref_context.borrow_mut().initialize(); ref_context.borrow().show_current_vm_status(); let mut repl = Repl::builder() @@ -685,7 +776,7 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { command! { "display ACIR opcodes", () => || { - ref_context.borrow().display_opcodes(); + ref_context.borrow().show_opcodes(); Ok(CommandStatus::Done) } }, @@ -798,14 +889,6 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { // Drop it so that we can move fields out from `context` again. drop(repl); - if context.borrow().is_solved() { - let solved_witness_stack = context.into_inner().finalize(); - DebugExecutionResult::Solved(solved_witness_stack) - } else { - match context.into_inner().last_error() { - // Expose the last known error - Some(error) => DebugExecutionResult::Error(error), - None => DebugExecutionResult::Incomplete, - } - } + context.borrow().terminate(); + context.borrow().get_final_result() } From 301fe723bd505ec1d06434c938c7780ec54856c2 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 10 Mar 2025 13:35:04 -0300 Subject: [PATCH 35/60] Remove unused DebuggerStatus::Error variant --- tooling/debugger/src/dap.rs | 2 +- tooling/debugger/src/repl.rs | 46 +++++++----------------------------- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 345affca42a..099070cf488 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -87,7 +87,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< next_breakpoint_id: 1, instruction_breakpoints: vec![], source_breakpoints: BTreeMap::new(), - last_result: DebugCommandResult::Ok, // TODO: handle circuits with no opcodes ? + last_result: DebugCommandResult::Ok, } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index a1aff0f210b..e196492c190 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -64,11 +64,10 @@ pub(super) enum DebugCommandAPI { pub(super) enum DebuggerStatus { Idle, Busy, - Error, Final(DebugExecutionResult), } -pub struct AsyncDebugger<'a> { +pub struct AsyncReplDebugger<'a> { circuits: Vec>, debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, @@ -80,7 +79,7 @@ pub struct AsyncDebugger<'a> { raw_source_printing: bool, } -impl<'a> AsyncDebugger<'a> { +impl<'a> AsyncReplDebugger<'a> { pub fn new( compiled_program: &CompiledProgram, debug_artifact: &'a DebugArtifact, @@ -108,15 +107,6 @@ impl<'a> AsyncDebugger<'a> { fn send_status(&mut self, status: DebuggerStatus) { self.status_sender.send(status).expect("Downstream channel closed") } - fn mark_busy(&mut self) { - self.send_status(DebuggerStatus::Busy) - } - fn mark_idle(&mut self) { - self.send_status(DebuggerStatus::Idle) - } - fn mark_error(&mut self) { - self.send_status(DebuggerStatus::Error) - } pub(super) fn start_debugging( mut self, @@ -140,31 +130,26 @@ impl<'a> AsyncDebugger<'a> { } println!("Debugger ready for receiving messages.."); - self.mark_idle(); loop { + self.send_status(DebuggerStatus::Idle); // recv blocks until it receives message if let Ok(received) = self.command_receiver.recv() { - self.mark_busy(); + self.send_status(DebuggerStatus::Busy); match received { DebugCommandAPI::AddBreakpoint(debug_location) => { Self::add_breakpoint_at(&mut context, debug_location); - self.mark_idle(); } DebugCommandAPI::DeleteBreakpoint(debug_location) => { Self::delete_breakpoint_at(&mut context, debug_location); - self.mark_idle(); } DebugCommandAPI::Restart => { self.restart_session(&mut context); - self.mark_idle(); } DebugCommandAPI::WriteBrilligMemory(index, value, bit_size) => { Self::write_brillig_memory(&mut context, index, value, bit_size); - self.mark_idle(); } DebugCommandAPI::UpdateWitness(index, value) => { Self::update_witness(&mut context, index, value); - self.mark_idle(); } DebugCommandAPI::StepAcirOpcode => { self.handle_step(&mut context, |context| context.step_acir_opcode()); @@ -187,35 +172,27 @@ impl<'a> AsyncDebugger<'a> { }), DebugCommandAPI::AddBreakpointAtLine(line_number) => { Self::add_breakpoint_at_line(&mut context, line_number); - self.mark_idle(); } DebugCommandAPI::ShowVariables => { Self::show_variables(&mut context); - self.mark_idle(); } DebugCommandAPI::ShowWitnessMap => { Self::show_witness_map(&mut context); - self.mark_idle(); } DebugCommandAPI::ShowWitness(index) => { Self::show_witness(&mut context, index); - self.mark_idle(); } DebugCommandAPI::ShowBrilligMemory => { Self::show_brillig_memory(&mut context); - self.mark_idle(); } DebugCommandAPI::ShowCurrentCallStack => { self.show_current_call_stack(&mut context); - self.mark_idle(); } DebugCommandAPI::ShowOpcodes => { self.show_opcodes(&mut context); - self.mark_idle(); } DebugCommandAPI::ShowCurrentVmStatus => { self.show_current_vm_status(&mut context); - self.mark_idle(); } DebugCommandAPI::Terminate => { self.terminate(context); @@ -427,16 +404,13 @@ impl<'a> AsyncDebugger<'a> { match &self.last_result { DebugCommandResult::Done => { println!("Execution finished"); - self.mark_idle(); } - DebugCommandResult::Ok => self.mark_idle(), + DebugCommandResult::Ok => (), DebugCommandResult::BreakpointReached(location) => { println!("Stopped at breakpoint in opcode {}", location); - self.mark_idle(); } DebugCommandResult::Error(error) => { println!("ERROR: {}", error); - self.mark_error(); } } } @@ -461,8 +435,6 @@ impl<'a> AsyncDebugger<'a> { let result = step(context); self.show_current_vm_status(context); self.handle_result(result); - } else { - self.mark_idle(); } } @@ -578,9 +550,9 @@ impl DebugCommander { } fn call_debugger(&self, command: DebugCommandAPI) { - self.when_not_busy( - || self.command_sender.send(command).expect("Could not communicate with debugger"), //{ - ) + self.when_not_busy(|| { + self.command_sender.send(command).expect("Could not communicate with debugger") + }) } fn get_final_result(&self) -> DebugExecutionResult { @@ -682,7 +654,7 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { let (command_tx, command_rx) = mpsc::channel::(); let (status_tx, status_rx) = mpsc::channel::(); thread::spawn(move || { - let debugger = AsyncDebugger::new( + let debugger = AsyncReplDebugger::new( &project.compiled_program, &debug_artifact, project.initial_witness, From 3c801453ee3ea5feac36161c5f92795ce107fc41 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 10 Mar 2025 19:31:32 -0300 Subject: [PATCH 36/60] Create new nargo::ops::debug mod and organize functions --- tooling/nargo/src/ops/compile.rs | 17 +- tooling/nargo/src/ops/debug.rs | 196 +++++++++++++++++++++++ tooling/nargo/src/ops/mod.rs | 5 +- tooling/nargo_cli/src/cli/check_cmd.rs | 23 +-- tooling/nargo_cli/src/cli/dap_cmd.rs | 29 ++-- tooling/nargo_cli/src/cli/debug_cmd.rs | 202 ++---------------------- tooling/nargo_cli/src/cli/export_cmd.rs | 4 +- tooling/nargo_cli/src/cli/test_cmd.rs | 12 +- 8 files changed, 253 insertions(+), 235 deletions(-) create mode 100644 tooling/nargo/src/ops/debug.rs diff --git a/tooling/nargo/src/ops/compile.rs b/tooling/nargo/src/ops/compile.rs index 7a3ddbfe3bc..9400c878f5f 100644 --- a/tooling/nargo/src/ops/compile.rs +++ b/tooling/nargo/src/ops/compile.rs @@ -1,9 +1,10 @@ use fm::FileManager; use noirc_driver::{ - CompilationResult, CompileOptions, CompiledContract, CompiledProgram, link_to_debug_crate, + CompilationResult, CompileOptions, CompiledContract, CompiledProgram, CrateId, check_crate, + link_to_debug_crate, }; use noirc_frontend::debug::DebugInstrumenter; -use noirc_frontend::hir::ParsedFiles; +use noirc_frontend::hir::{Context, ParsedFiles}; use crate::errors::CompileError; use crate::prepare_package; @@ -147,3 +148,15 @@ pub fn report_errors( Ok(t) } + +/// Run the lexing, parsing, name resolution, and type checking passes and report any warnings +/// and errors found. +/// TODO: check if it's ok for this fn to be here. Should it be in ops::check instead? +pub fn check_crate_and_report_errors( + context: &mut Context, + crate_id: CrateId, + options: &CompileOptions, +) -> Result<(), CompileError> { + let result = check_crate(context, crate_id, options); + report_errors(result, &context.file_manager, options.deny_warnings, options.silence_warnings) +} diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs new file mode 100644 index 00000000000..034fb317e5b --- /dev/null +++ b/tooling/nargo/src/ops/debug.rs @@ -0,0 +1,196 @@ +use std::path::Path; + +use acvm::acir::circuit::ExpressionWidth; +use fm::FileManager; +use noirc_driver::{ + CompileOptions, CompiledProgram, CrateId, DEFAULT_EXPRESSION_WIDTH, compile_no_check, + file_manager_with_stdlib, link_to_debug_crate, +}; +use noirc_frontend::{ + debug::DebugInstrumenter, + hir::{Context, FunctionNameMatch, ParsedFiles, def_map::TestFunction}, +}; + +use crate::{ + errors::CompileError, insert_all_files_for_workspace_into_file_manager, package::Package, + parse_all, prepare_package, workspace::Workspace, +}; + +use super::{ + compile_program, compile_program_with_debug_instrumenter, report_errors, transform_program, +}; + +pub struct TestDefinition { + pub name: String, + pub function: TestFunction, +} +pub fn get_test_function_for_debug( + crate_id: CrateId, + context: &Context, + test_name: &str, +) -> Result { + let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); + + let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); + + let (test_name, test_function) = match test_functions { + matchings if matchings.is_empty() => { + return Err(format!("`{}` does not match with any test function", test_name)); + } + matchings if matchings.len() == 1 => { + let (name, test_func) = matchings.into_iter().next().unwrap(); + (name, test_func) + } + _ => { + return Err(format!("`{}` matches with more than one test function", test_name)); + } + }; + + let test_function_has_arguments = !context + .def_interner + .function_meta(&test_function.get_id()) + .function_signature() + .0 + .is_empty(); + + if test_function_has_arguments { + return Err(String::from("Cannot debug tests with arguments")); + } + Ok(TestDefinition { name: test_name, function: test_function }) +} + +pub fn compile_test_fn_for_debugging( + test_def: &TestDefinition, + context: &mut Context, + package: &Package, + compile_options: CompileOptions, +) -> Result { + let compiled_program = + compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); + let compiled_program = transform_program(compiled_program, expression_width); + Ok(compiled_program) +} + +pub fn compile_bin_package_for_debugging( + workspace: &Workspace, + package: &Package, + compile_options: &CompileOptions, +) -> Result { + let (workspace_file_manager, mut parsed_files) = load_workspace_files(workspace); + + let expression_width = + get_target_width(package.expression_width, compile_options.expression_width); + + let compilation_result = if compile_options.instrument_debug { + let debug_state = + instrument_package_files(&mut parsed_files, &workspace_file_manager, package); + + compile_program_with_debug_instrumenter( + &workspace_file_manager, + &parsed_files, + workspace, + package, + compile_options, + None, + debug_state, + ) + } else { + compile_program( + &workspace_file_manager, + &parsed_files, + workspace, + package, + compile_options, + None, + ) + }; + + report_errors( + compilation_result, + &workspace_file_manager, + compile_options.deny_warnings, + compile_options.silence_warnings, + ) + .map(|compiled_program| transform_program(compiled_program, expression_width)) +} + +pub fn compile_options_for_debugging( + acir_mode: bool, + skip_instrumentation: bool, + expression_width: Option, + compile_options: CompileOptions, +) -> CompileOptions { + CompileOptions { + instrument_debug: !skip_instrumentation, + force_brillig: !acir_mode, + expression_width, + ..compile_options + } +} + +pub fn prepare_package_for_debug<'a>( + file_manager: &'a FileManager, + parsed_files: &'a mut ParsedFiles, + package: &'a Package, + workspace: &Workspace, +) -> (Context<'a, 'a>, CrateId) { + let debug_instrumenter = instrument_package_files(parsed_files, file_manager, package); + + // -- This :down: is from nargo::ops(compile).compile_program_with_debug_instrumenter + let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); + link_to_debug_crate(&mut context, crate_id); + context.debug_instrumenter = debug_instrumenter; + context.package_build_path = workspace.package_build_path(package); + (context, crate_id) +} + +pub fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { + let mut file_manager = file_manager_with_stdlib(Path::new("")); + insert_all_files_for_workspace_into_file_manager(workspace, &mut file_manager); + + let parsed_files = parse_all(&file_manager); + (file_manager, parsed_files) +} + +/// Add debugging instrumentation to all parsed files belonging to the package +/// being compiled +fn instrument_package_files( + parsed_files: &mut ParsedFiles, + file_manager: &FileManager, + package: &Package, +) -> DebugInstrumenter { + // Start off at the entry path and read all files in the parent directory. + let entry_path_parent = package + .entry_path + .parent() + .unwrap_or_else(|| panic!("The entry path is expected to be a single file within a directory and so should have a parent {:?}", package.entry_path)); + + let mut debug_instrumenter = DebugInstrumenter::default(); + + for (file_id, parsed_file) in parsed_files.iter_mut() { + let file_path = + file_manager.path(*file_id).expect("Parsed file ID not found in file manager"); + for ancestor in file_path.ancestors() { + if ancestor == entry_path_parent { + // file is in package + debug_instrumenter.instrument_module(&mut parsed_file.0, *file_id); + } + } + } + + debug_instrumenter +} + +// This is the same as in compile_cmd, perhaps we should move it to ops::compile? +fn get_target_width( + package_default_width: Option, + compile_options_width: Option, +) -> ExpressionWidth { + if let (Some(manifest_default_width), None) = (package_default_width, compile_options_width) { + manifest_default_width + } else { + compile_options_width.unwrap_or(DEFAULT_EXPRESSION_WIDTH) + } +} diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 32bef27fbee..fcab1b57e86 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -1,7 +1,7 @@ pub use self::check::check_program; pub use self::compile::{ - collect_errors, compile_contract, compile_program, compile_program_with_debug_instrumenter, - compile_workspace, report_errors, + check_crate_and_report_errors, collect_errors, compile_contract, compile_program, + compile_program_with_debug_instrumenter, compile_workspace, report_errors, }; pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; @@ -14,6 +14,7 @@ pub use self::test::{ mod check; mod compile; +pub mod debug; mod execute; mod optimize; mod test; diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index 2247f40f181..80d32cd52b6 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -4,17 +4,15 @@ use clap::Args; use fm::FileManager; use iter_extended::btree_map; use nargo::{ - errors::CompileError, insert_all_files_for_workspace_into_file_manager, ops::report_errors, - package::Package, parse_all, prepare_package, workspace::Workspace, + errors::CompileError, insert_all_files_for_workspace_into_file_manager, + ops::check_crate_and_report_errors, package::Package, parse_all, prepare_package, + workspace::Workspace, }; use nargo_toml::PackageSelection; use noir_artifact_cli::fs::artifact::write_to_file; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; -use noirc_driver::{CompileOptions, CrateId, check_crate, compute_function_abi}; -use noirc_frontend::{ - hir::{Context, ParsedFiles}, - monomorphization::monomorphize, -}; +use noirc_driver::{CompileOptions, check_crate, compute_function_abi}; +use noirc_frontend::{hir::ParsedFiles, monomorphization::monomorphize}; use super::{LockType, PackageOptions, WorkspaceCommand}; @@ -151,17 +149,6 @@ fn create_input_toml_template( toml::to_string(&map).unwrap() } -/// Run the lexing, parsing, name resolution, and type checking passes and report any warnings -/// and errors found. -pub(crate) fn check_crate_and_report_errors( - context: &mut Context, - crate_id: CrateId, - options: &CompileOptions, -) -> Result<(), CompileError> { - let result = check_crate(context, crate_id, options); - report_errors(result, &context.file_manager, options.deny_warnings, options.silence_warnings) -} - #[cfg(test)] mod tests { use noirc_abi::{AbiParameter, AbiType, AbiVisibility, Sign}; diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index c48e4e7ecaf..2981c43277a 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -2,8 +2,17 @@ use acvm::acir::circuit::ExpressionWidth; use clap::Args; use dap::errors::ServerError; use dap::events::OutputEventBody; +use dap::requests::Command; +use dap::responses::ResponseBody; +use dap::server::Server; +use dap::types::{Capabilities, OutputEventCategory}; use nargo::constants::PROVER_INPUT_FILE; -use nargo::ops::{TestStatus, test_status_program_compile_pass}; +use nargo::ops::debug::{ + TestDefinition, compile_bin_package_for_debugging, compile_options_for_debugging, + compile_test_fn_for_debugging, get_test_function_for_debug, load_workspace_files, + prepare_package_for_debug, +}; +use nargo::ops::{TestStatus, check_crate_and_report_errors, test_status_program_compile_pass}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; @@ -13,23 +22,11 @@ use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; - use std::io::{BufReader, BufWriter, Read, Write}; use std::path::Path; -use dap::requests::Command; -use dap::responses::ResponseBody; -use dap::server::Server; -use dap::types::{Capabilities, OutputEventCategory}; use serde_json::Value; -use super::check_cmd::check_crate_and_report_errors; -use super::compile_cmd::get_target_width; -use super::debug_cmd::{ - TestDefinition, compile_bin_package_for_debugging, compile_options_for_debugging, - compile_test_fn_for_debugging, get_test_function, load_workspace_files, - prepare_package_for_debug, -}; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -116,9 +113,7 @@ fn compile_main( package: &Package, compile_options: &CompileOptions, ) -> Result { - let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); - compile_bin_package_for_debugging(workspace, package, compile_options, expression_width) + compile_bin_package_for_debugging(workspace, package, compile_options) .map_err(|_| LoadError::Generic("Failed to compile project".into())) } @@ -136,7 +131,7 @@ fn compile_test( check_crate_and_report_errors(&mut context, crate_id, &compile_options) .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; - let test = get_test_function(crate_id, &context, &test_name) + let test = get_test_function_for_debug(crate_id, &context, &test_name) .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; let program = compile_test_fn_for_debugging(&test, &mut context, package, compile_options) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index ae1c9faab5e..ad974198e8f 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -2,35 +2,29 @@ use std::path::Path; use std::time::Duration; use acvm::FieldElement; -use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::{WitnessMap, WitnessStack}; use clap::Args; use fm::FileManager; use nargo::constants::PROVER_INPUT_FILE; -use nargo::errors::CompileError; +use nargo::ops::debug::{ + TestDefinition, compile_bin_package_for_debugging, compile_options_for_debugging, + compile_test_fn_for_debugging, get_test_function_for_debug, load_workspace_files, + prepare_package_for_debug, +}; use nargo::ops::{ - TestStatus, compile_program, compile_program_with_debug_instrumenter, report_errors, - test_status_program_compile_fail, test_status_program_compile_pass, + TestStatus, check_crate_and_report_errors, test_status_program_compile_fail, + test_status_program_compile_pass, }; use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; -use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all, prepare_package}; use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; use noir_debugger::{DebugExecutionResult, Project, RunParams}; use noirc_abi::Abi; -use noirc_driver::{ - CompileOptions, CompiledProgram, compile_no_check, file_manager_with_stdlib, - link_to_debug_crate, -}; -use noirc_frontend::debug::DebugInstrumenter; -use noirc_frontend::graph::CrateId; -use noirc_frontend::hir::def_map::TestFunction; -use noirc_frontend::hir::{Context, FunctionNameMatch, ParsedFiles}; +use noirc_driver::{CompileOptions, CompiledProgram}; +use noirc_frontend::hir::Context; -use super::check_cmd::check_crate_and_report_errors; -use super::compile_cmd::get_target_width; use super::test_cmd::TestResult; use super::test_cmd::formatters::Formatter; use super::{LockType, WorkspaceCommand}; @@ -49,7 +43,7 @@ pub(crate) struct DebugCommand { /// The name of the package to execute #[clap(long)] - pub(super) package: Option, + package: Option, #[clap(flatten)] compile_options: CompileOptions, @@ -131,20 +125,6 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr } } -pub(crate) fn compile_options_for_debugging( - acir_mode: bool, - skip_instrumentation: bool, - expression_width: Option, - compile_options: CompileOptions, -) -> CompileOptions { - CompileOptions { - instrument_debug: !skip_instrumentation, - force_brillig: !acir_mode, - expression_width, - ..compile_options - } -} - fn print_test_result(test_result: TestResult, file_manager: &FileManager) { let formatter: Box = Box::new(PrettyFormatter); formatter @@ -201,90 +181,6 @@ fn debug_test_fn( ) } -pub(super) fn compile_test_fn_for_debugging( - test_def: &TestDefinition, - context: &mut Context, - package: &Package, - compile_options: CompileOptions, -) -> Result { - let compiled_program = - compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; - let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); - let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - Ok(compiled_program) -} - -pub(crate) fn compile_bin_package_for_debugging( - workspace: &Workspace, - package: &Package, - compile_options: &CompileOptions, - expression_width: ExpressionWidth, -) -> Result { - let (workspace_file_manager, mut parsed_files) = load_workspace_files(workspace); - - let compilation_result = if compile_options.instrument_debug { - let debug_state = - instrument_package_files(&mut parsed_files, &workspace_file_manager, package); - - compile_program_with_debug_instrumenter( - &workspace_file_manager, - &parsed_files, - workspace, - package, - compile_options, - None, - debug_state, - ) - } else { - compile_program( - &workspace_file_manager, - &parsed_files, - workspace, - package, - compile_options, - None, - ) - }; - - report_errors( - compilation_result, - &workspace_file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - .map(|compiled_program| nargo::ops::transform_program(compiled_program, expression_width)) -} - -/// Add debugging instrumentation to all parsed files belonging to the package -/// being compiled -fn instrument_package_files( - parsed_files: &mut ParsedFiles, - file_manager: &FileManager, - package: &Package, -) -> DebugInstrumenter { - // Start off at the entry path and read all files in the parent directory. - let entry_path_parent = package - .entry_path - .parent() - .unwrap_or_else(|| panic!("The entry path is expected to be a single file within a directory and so should have a parent {:?}", package.entry_path)); - - let mut debug_instrumenter = DebugInstrumenter::default(); - - for (file_id, parsed_file) in parsed_files.iter_mut() { - let file_path = - file_manager.path(*file_id).expect("Parsed file ID not found in file manager"); - for ancestor in file_path.ancestors() { - if ancestor == entry_path_parent { - // file is in package - debug_instrumenter.instrument_module(&mut parsed_file.0, *file_id); - } - } - } - - debug_instrumenter -} - fn debug_main( package: &Package, workspace: Workspace, @@ -292,11 +188,8 @@ fn debug_main( run_params: RunParams, package_params: PackageParams, ) -> Result<(), CliError> { - let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); - let compiled_program = - compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width)?; + compile_bin_package_for_debugging(&workspace, package, &compile_options)?; run_async(package, compiled_program, &workspace, run_params, package_params)?; @@ -318,7 +211,8 @@ fn debug_test( check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; - let test = get_test_function(crate_id, &context, &test_name)?; + let test = get_test_function_for_debug(crate_id, &context, &test_name) + .map_err(CliError::Generic)?; let test_result = debug_test_fn( &test, @@ -334,76 +228,6 @@ fn debug_test( Ok(()) } -pub(super) struct TestDefinition { - pub name: String, - pub function: TestFunction, -} - -// TODO: move to nargo::ops and reuse in test_cmd? -pub(super) fn get_test_function( - crate_id: CrateId, - context: &Context, - test_name: &str, -) -> Result { - let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); - - let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); - - let (test_name, test_function) = match test_functions { - matchings if matchings.is_empty() => { - return Err(CliError::Generic(format!( - "`{}` does not match with any test function", - test_name - ))); - } - matchings if matchings.len() == 1 => { - let (name, test_func) = matchings.into_iter().next().unwrap(); - (name, test_func) - } - _ => { - return Err(CliError::Generic(format!( - "`{}` matches with more than one test function", - test_name - ))); - } - }; - - let test_function_has_arguments = !context - .def_interner - .function_meta(&test_function.get_id()) - .function_signature() - .0 - .is_empty(); - - if test_function_has_arguments { - return Err(CliError::Generic(String::from("Cannot debug tests with arguments"))); - } - Ok(TestDefinition { name: test_name, function: test_function }) -} - -pub(super) fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { - let mut file_manager = file_manager_with_stdlib(Path::new("")); - insert_all_files_for_workspace_into_file_manager(workspace, &mut file_manager); - - let parsed_files = parse_all(&file_manager); - (file_manager, parsed_files) -} -pub(super) fn prepare_package_for_debug<'a>( - file_manager: &'a FileManager, - parsed_files: &'a mut ParsedFiles, - package: &'a Package, - workspace: &Workspace, -) -> (Context<'a, 'a>, CrateId) { - let debug_instrumenter = instrument_package_files(parsed_files, file_manager, package); - - // -- This :down: is from nargo::ops(compile).compile_program_with_debug_instrumenter - let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - link_to_debug_crate(&mut context, crate_id); - context.debug_instrumenter = debug_instrumenter; - context.package_build_path = workspace.package_build_path(package); - (context, crate_id) -} - fn run_async( package: &Package, program: CompiledProgram, diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 373dfce86a9..639e43ff52c 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,5 +1,5 @@ use nargo::errors::CompileError; -use nargo::ops::report_errors; +use nargo::ops::{check_crate_and_report_errors, report_errors}; use noir_artifact_cli::fs::artifact::save_program_to_file; use noirc_errors::CustomDiagnostic; use noirc_frontend::hir::ParsedFiles; @@ -18,8 +18,6 @@ use clap::Args; use crate::errors::CliError; -use super::check_cmd::check_crate_and_report_errors; - use super::{LockType, PackageOptions, WorkspaceCommand}; /// Exports functions marked with #[export] attribute diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 2a5a2a9173b..59543260f84 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -14,15 +14,19 @@ use clap::Args; use fm::FileManager; use formatters::{Formatter, JsonFormatter, PrettyFormatter, TerseFormatter}; use nargo::{ - PrintOutput, foreign_calls::DefaultForeignCallBuilder, - insert_all_files_for_workspace_into_file_manager, ops::TestStatus, package::Package, parse_all, - prepare_package, workspace::Workspace, + PrintOutput, + foreign_calls::DefaultForeignCallBuilder, + insert_all_files_for_workspace_into_file_manager, + ops::{TestStatus, check_crate_and_report_errors}, + package::Package, + parse_all, prepare_package, + workspace::Workspace, }; use nargo_toml::PackageSelection; use noirc_driver::{CompileOptions, check_crate}; use noirc_frontend::hir::{FunctionNameMatch, ParsedFiles}; -use crate::{cli::check_cmd::check_crate_and_report_errors, errors::CliError}; +use crate::errors::CliError; use super::{LockType, PackageOptions, WorkspaceCommand}; From 382f22955dd1dd7d995029e40e14afe9f124a70a Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 10 Mar 2025 21:58:02 -0300 Subject: [PATCH 37/60] cargo fmt --- tooling/nargo_cli/src/cli/debug_cmd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index ad974198e8f..2e322bf3a2b 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -211,8 +211,8 @@ fn debug_test( check_crate_and_report_errors(&mut context, crate_id, &compile_options)?; - let test = get_test_function_for_debug(crate_id, &context, &test_name) - .map_err(CliError::Generic)?; + let test = + get_test_function_for_debug(crate_id, &context, &test_name).map_err(CliError::Generic)?; let test_result = debug_test_fn( &test, From 14784610c15cdc7f45a55688abf642c8cabb92b8 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 11 Mar 2025 15:36:13 -0300 Subject: [PATCH 38/60] Update tooling/debugger/src/context.rs Co-authored-by: Martin Verzilli --- tooling/debugger/src/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index a0b30021f3a..cedc2df6851 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -355,7 +355,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { circuits, unconstrained_functions, acir_opcode_addresses, - initial_witness: initial_witness.clone(), // keeping it to be able to restart the context by itself + initial_witness: initial_witness.clone(), // we keep it so the context can restart itself acvm: initialize_acvm( blackbox_solver, circuits, From 88964920f8954c4e2ca10b1ad9660489636f5bf4 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 11 Mar 2025 15:37:34 -0300 Subject: [PATCH 39/60] Update tooling/debugger/src/repl.rs Co-authored-by: Martin Verzilli --- tooling/debugger/src/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index e196492c190..90c31bb9edc 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -129,7 +129,7 @@ impl<'a> AsyncReplDebugger<'a> { self.last_result = DebugCommandResult::Done } - println!("Debugger ready for receiving messages.."); + println!("Debugger ready to receive messages.."); loop { self.send_status(DebuggerStatus::Idle); // recv blocks until it receives message From 5fcf3446c8b0d016996ee56c8f31bdf5f1ae4d53 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 11 Mar 2025 16:54:26 -0300 Subject: [PATCH 40/60] simplify instrument_module function --- compiler/noirc_frontend/src/debug/mod.rs | 37 +++++++++--------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index b00ad36bdf1..d8e36f9e2eb 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -2,11 +2,7 @@ use crate::ast::PathSegment; use crate::parse_program; use crate::parser::{ParsedModule, ParsedSubModule}; use crate::signed_field::SignedField; -use crate::{ - ast, - ast::Path, - parser::{Item, ItemKind}, -}; +use crate::{ast, ast::Path, parser::ItemKind}; use fm::FileId; use noirc_errors::debug_info::{DebugFnId, DebugFunction}; use noirc_errors::{Location, Span}; @@ -60,25 +56,18 @@ impl Default for DebugInstrumenter { impl DebugInstrumenter { pub fn instrument_module(&mut self, module: &mut ParsedModule, file: FileId) { module.items.iter_mut().for_each(|item| { - if let Item { kind: ItemKind::Function(f), .. } = item { - self.walk_fn(&mut f.def); - } - if let Item { - kind: - ItemKind::Submodules(ParsedSubModule { - is_contract: true, - contents: contract_module @ ParsedModule { .. }, - .. - }), - .. - } = item - { - contract_module.items.iter_mut().for_each(|item| { - if let Item { kind: ItemKind::Function(f), .. } = item { - self.walk_fn(&mut f.def); - } - }); - self.insert_state_set_oracle(contract_module, 8, file); + match &mut item.kind { + // Instrument top-level functions of a module + ItemKind::Function(f) => self.walk_fn(&mut f.def), + // Instrument contract module + ItemKind::Submodules(ParsedSubModule { + is_contract: true, + contents: contract_module, + .. + }) => { + self.instrument_module(contract_module, file); + } + _ => (), } }); From 559b7e8ad15aa9e96e5a2ed5cf8b925b4e4a7df8 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 11 Mar 2025 18:24:28 -0300 Subject: [PATCH 41/60] Minor tweaks and comments --- tooling/debugger/src/context.rs | 11 ++++++++--- tooling/debugger/src/dap.rs | 6 +++--- tooling/debugger/src/lib.rs | 6 +++--- tooling/debugger/src/repl.rs | 6 +++--- tooling/nargo/src/foreign_calls/rpc.rs | 7 +++---- tooling/nargo_cli/src/cli/dap_cmd.rs | 8 ++++---- tooling/nargo_cli/src/cli/debug_cmd.rs | 6 +++--- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index cedc2df6851..18516920222 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -273,9 +273,8 @@ pub enum DebugExecutionResult { Error(NargoError), } -//TODO: find a better name #[derive(Debug, Clone)] -pub struct Project { +pub struct DebugProject { pub compiled_program: CompiledProgram, pub initial_witness: WitnessMap, pub root_dir: PathBuf, @@ -285,8 +284,14 @@ pub struct Project { #[derive(Debug, Clone)] pub struct RunParams { + /// Use pedantic ACVM solving pub pedantic_solving: bool, - pub raw_source_printing: bool, + + /// Option for configuring the source_code_printer + /// This option only applies for the Repl interface + pub raw_source_printing: Option, + + /// JSON RPC url to solve oracle calls pub oracle_resolver_url: Option, } diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 099070cf488..b96f67b576d 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -5,7 +5,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::{NargoError, PrintOutput}; -use crate::Project; +use crate::DebugProject; use crate::context::{DebugCommandResult, DebugLocation, RunParams}; use crate::context::{DebugContext, DebugExecutionResult}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; @@ -61,7 +61,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn new( server: &'a mut Server, solver: &'a B, - project: &'a Project, + project: &'a DebugProject, debug_artifact: &'a DebugArtifact, foreign_call_resolver_url: Option, ) -> Self { @@ -628,7 +628,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn run_session( server: &mut Server, - project: Project, + project: DebugProject, run_params: RunParams, ) -> Result { let debug_artifact = DebugArtifact { diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 3f1393806f1..5e8384dfeac 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -11,16 +11,16 @@ use ::dap::errors::ServerError; use ::dap::server::Server; // TODO: extract these pub structs to its own module pub use context::DebugExecutionResult; -pub use context::Project; +pub use context::DebugProject; pub use context::RunParams; -pub fn run_repl_session(project: Project, run_params: RunParams) -> DebugExecutionResult { +pub fn run_repl_session(project: DebugProject, run_params: RunParams) -> DebugExecutionResult { repl::run(project, run_params) } pub fn run_dap_loop( server: &mut Server, - project: Project, + project: DebugProject, run_params: RunParams, ) -> Result { dap::run_session(server, project, run_params) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 90c31bb9edc..a2694370ec4 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,4 +1,4 @@ -use crate::Project; +use crate::DebugProject; use crate::context::{ DebugCommandResult, DebugContext, DebugExecutionResult, DebugLocation, DebugStackFrame, RunParams, @@ -637,7 +637,7 @@ impl DebugCommander { } } -pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { +pub fn run(project: DebugProject, run_params: RunParams) -> DebugExecutionResult { let debug_artifact = DebugArtifact { debug_symbols: project.compiled_program.debug.clone(), file_map: project.compiled_program.file_map.clone(), @@ -660,7 +660,7 @@ pub fn run(project: Project, run_params: RunParams) -> DebugExecutionResult { project.initial_witness, status_tx, command_rx, - run_params.raw_source_printing, + run_params.raw_source_printing.unwrap_or(false), run_params.pedantic_solving, ); debugger.start_debugging(foreign_call_executor); diff --git a/tooling/nargo/src/foreign_calls/rpc.rs b/tooling/nargo/src/foreign_calls/rpc.rs index c6b3d50bb20..d21389ec006 100644 --- a/tooling/nargo/src/foreign_calls/rpc.rs +++ b/tooling/nargo/src/foreign_calls/rpc.rs @@ -20,6 +20,7 @@ pub struct RPCForeignCallExecutor { /// JSON RPC client to resolve foreign calls external_resolver: HttpClient, /// External resolver target. We are keeping it to be able to restart httpClient if necessary + /// see noir-lang/noir#7463 target_url: String, /// Root path to the program or workspace in execution. root_path: Option, @@ -130,12 +131,10 @@ where match result { Ok(parsed_response) => Ok(parsed_response), - // TODO: This is a workaround + // TODO: This is a workaround for noir-lang/noir#7463 // The client is losing connection with the server and it's not being able to manage it // so we are re-creating the HttpClient when it happens - Err(jsonrpsee::core::ClientError::Transport(err)) => { - println!("We got a transport error: {err:?}"); - println!("Restarting http client..."); + Err(jsonrpsee::core::ClientError::Transport(_)) => { self.external_resolver = build_http_client(&self.target_url); let parsed_response = self.send_foreign_call(foreign_call)?; Ok(parsed_response) diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 2981c43277a..8d892a1e759 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -17,7 +17,7 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; use noir_artifact_cli::fs::inputs::read_inputs_from_file; -use noir_debugger::{DebugExecutionResult, Project, RunParams}; +use noir_debugger::{DebugExecutionResult, DebugProject, RunParams}; use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::debug_info::DebugInfo; @@ -145,7 +145,7 @@ fn load_and_compile_project( prover_name: &str, compile_options: CompileOptions, test_name: Option, -) -> Result<(Project, Option), LoadError> { +) -> Result<(DebugProject, Option), LoadError> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace @@ -177,7 +177,7 @@ fn load_and_compile_project( .encode(&inputs_map, None) .map_err(|_| LoadError::Generic("Failed to encode inputs".into()))?; - let project = Project { + let project = DebugProject { compiled_program, initial_witness, root_dir: workspace.root_dir.clone(), @@ -263,7 +263,7 @@ fn loop_uninitialized_dap( RunParams { oracle_resolver_url, pedantic_solving, - raw_source_printing: false, // TODO: should we get this from dap args? + raw_source_printing: None, }, )?; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 2e322bf3a2b..13cc4a0b78a 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -20,7 +20,7 @@ use nargo::workspace::Workspace; use nargo_toml::PackageSelection; use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noir_debugger::{DebugExecutionResult, Project, RunParams}; +use noir_debugger::{DebugExecutionResult, DebugProject, RunParams}; use noirc_abi::Abi; use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_frontend::hir::Context; @@ -102,7 +102,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr }; let run_params = RunParams { pedantic_solving: args.compile_options.pedantic_solving, - raw_source_printing: args.raw_source_printing.unwrap_or(false), + raw_source_printing: args.raw_source_printing, oracle_resolver_url: args.oracle_resolver, }; let workspace_clone = workspace.clone(); @@ -243,7 +243,7 @@ fn run_async( println!("[{}] Starting debugger", package.name); let initial_witness = parse_initial_witness(package, &package_params.prover_name, abi)?; - let project = Project { + let project = DebugProject { compiled_program: program, initial_witness, root_dir: workspace.root_dir.clone(), From 725580ca796555a19b154444f0c5a7824c98ea56 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Tue, 11 Mar 2025 18:41:33 -0300 Subject: [PATCH 42/60] Add doc to nargo::errors::execution_error_from --- tooling/nargo/src/errors.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 59646198872..87b0d72ab37 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -243,6 +243,8 @@ pub fn try_to_diagnose_runtime_error( Some(error.with_call_stack(source_locations)) } +/// Map the given OpcodeResolutionError to the corresponding ExecutionError +/// In case of resulting in an ExecutionError::AssertionFailedThis it propagates the payload pub fn execution_error_from( error: OpcodeResolutionError, call_stack: &[ResolvedOpcodeLocation], From 5ebfe5e575b526ee4f4a65078628b16aab4390c1 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 13:10:48 -0300 Subject: [PATCH 43/60] simplify DebugInstrumenter.insert_state_set_oracle signature --- compiler/noirc_frontend/src/debug/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index d8e36f9e2eb..8c8fa30f212 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -73,7 +73,7 @@ impl DebugInstrumenter { // this part absolutely must happen after ast traversal above // so that oracle functions don't get wrapped, resulting in infinite recursion: - self.insert_state_set_oracle(module, 8, file); + self.insert_state_set_oracle(module, file); } fn insert_var(&mut self, var_name: &str) -> Option { @@ -506,8 +506,8 @@ impl DebugInstrumenter { } } - fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32, file: FileId) { - let member_assigns = (1..=n) + fn insert_state_set_oracle(&self, module: &mut ParsedModule, file: FileId) { + let member_assigns = (1..=MAX_MEMBER_ASSIGN_DEPTH) .map(|i| format!["__debug_member_assign_{i}"]) .collect::>() .join(",\n"); From b34b6abda4fb4f6bb8f0cf41a5dd092b8cfb3738 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 13:22:09 -0300 Subject: [PATCH 44/60] remove TODO comments since topics were already discussed --- tooling/debugger/src/dap.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index b96f67b576d..213e97bdb88 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -128,11 +128,6 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< Command::Disconnect(_) => { eprintln!("INFO: ending debugging session"); self.running = false; - // TODO: review deleting this line - // responding ack here makes the server to close connection - // so the dap_cmd then can't use it to send the test_result to the user - // - // self.server.respond(req.ack()?)?; break; } Command::SetBreakpoints(_) => { @@ -377,22 +372,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< hit_breakpoint_ids: Some(breakpoint_ids), }))?; } - DebugCommandResult::Error(_err) => { - // TODO: review: - // is it better for the user to have the possibility to restart the debug session and get out of the error - // or is it better to finish the session automatically? so the user does not have to manually stop the execution? - - // self.server.send_event(Event::Stopped(StoppedEventBody { - // reason: StoppedEventReason::Exception, - // description: Some("Stopped on exception".into()), - // thread_id: Some(0), - // preserve_focus_hint: Some(false), - // text: Some(format!("{err:#?}")), - // all_threads_stopped: Some(false), - // hit_breakpoint_ids: None, - // }))?; - self.server.send_event(Event::Terminated(None))?; - } + DebugCommandResult::Error(_) => self.server.send_event(Event::Terminated(None))?, } Ok(()) } From 8f0fc9e12edce62d6b7294909ebd82569d1794ed Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 14:01:23 -0300 Subject: [PATCH 45/60] enable silence-warnings when debugging --- tooling/nargo/src/ops/debug.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs index 034fb317e5b..25268762726 100644 --- a/tooling/nargo/src/ops/debug.rs +++ b/tooling/nargo/src/ops/debug.rs @@ -123,6 +123,10 @@ pub fn compile_options_for_debugging( compile_options: CompileOptions, ) -> CompileOptions { CompileOptions { + // for instrumenting the program the debugger + // will import functions that may not be used, + // which would generate compilation warnings + silence_warnings: true, instrument_debug: !skip_instrumentation, force_brillig: !acir_mode, expression_width, From 2568961161fd106a5d26d0e0d8b3fcec4b68e815 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 16:29:27 -0300 Subject: [PATCH 46/60] disable deny_warnings option when debugging --- tooling/nargo/src/ops/debug.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs index 25268762726..1cbd3bd61fe 100644 --- a/tooling/nargo/src/ops/debug.rs +++ b/tooling/nargo/src/ops/debug.rs @@ -123,10 +123,14 @@ pub fn compile_options_for_debugging( compile_options: CompileOptions, ) -> CompileOptions { CompileOptions { - // for instrumenting the program the debugger + // Compilation warnings are disabled when + // compiling for debugging + // + // For instrumenting the program the debugger // will import functions that may not be used, // which would generate compilation warnings silence_warnings: true, + deny_warnings: false, instrument_debug: !skip_instrumentation, force_brillig: !acir_mode, expression_width, From 872e148d6722ae311138c454c44d40f7f035e91e Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 17:17:28 -0300 Subject: [PATCH 47/60] Include new features to debugger documentation --- .../debugger/debugging_with_the_repl.md | 47 ++++++++++++++-- .../how_to/debugger/debugging_with_vs_code.md | 18 ++++-- docs/docs/reference/debugger/debugger_repl.md | 42 +++++++++----- .../reference/debugger/debugger_vscode.md | 52 ++++++++++++------ docs/docs/tooling/debugger.md | 4 -- .../static/img/debugger/debugger-codelens.png | Bin 0 -> 93022 bytes 6 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 docs/static/img/debugger/debugger-codelens.png diff --git a/docs/docs/how_to/debugger/debugging_with_the_repl.md b/docs/docs/how_to/debugger/debugging_with_the_repl.md index 1d64dae3f37..aa662fa1a74 100644 --- a/docs/docs/how_to/debugger/debugging_with_the_repl.md +++ b/docs/docs/how_to/debugger/debugging_with_the_repl.md @@ -1,7 +1,7 @@ --- title: Using the REPL Debugger description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. + Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. keywords: [ Nargo, @@ -14,7 +14,7 @@ sidebar_position: 1 #### Pre-requisites -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. +In order to use the REPL debugger, first you need to install recent enough versions of Nargo. ## Debugging a simple circuit @@ -38,7 +38,7 @@ At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 1 -> fn main(x : Field, y : pub Field) { 2 assert(x != y); 3 } -> +> ``` The debugger displays the current Noir code location, and it is now waiting for us to drive it. @@ -84,7 +84,7 @@ Some commands operate only for unconstrained functions, such as `memory` and `me ``` > memory Unconstrained VM memory not available -> +> ``` Before continuing, we can take a look at the initial witness map: @@ -115,7 +115,7 @@ _1 = 2 > ``` -Now we can inspect the current state of local variables. For that we use the `vars` command. +Now we can inspect the current state of local variables. For that we use the `vars` command. ``` > vars @@ -162,3 +162,40 @@ Finished execution Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). + +## Debugging a test function + +Let's debug a simple test: + +```rust +#[noir] +fn test_simple_equal() { + let x = 2; + let y = 1 + 1; + assert(x == y, "should be equal"); +} +``` + +To debug a test function using the REPL debugger, navigate to a Noir project directory inside a terminal, and run the `nargo debug` command passing the `--test-name your_test_name_here` argument. + +```bash +nargo debug --test-name test_simple_equal +``` + +After that, the debugger has started and works the same as debugging a main function, you can use any of the above explained commands to control the execution of the test function. + +### Test result + +The debugger does not end the session automatically. Once you finish debugging the execution of the test function you will notice that the debugger remains in the `Execution finished` state. When you are done debugging the test function you can exit the debugger by using the `quit` command. Once you finish the debugging session you should see the test result. + +```text +$ nargo debug --test-name test_simple_equal +[simple_noir_project] Starting debugger +At opcode 0:0 :: BRILLIG CALL func 0: inputs: [], outputs: [] +> continue +(Continuing execution...) +Finished execution +> quit +[simple_noir_project] Circuit witness successfully solved +[simple_noir_project] Testing test_simple_equal... ok +``` diff --git a/docs/docs/how_to/debugger/debugging_with_vs_code.md b/docs/docs/how_to/debugger/debugging_with_vs_code.md index ecd64fc2653..beb37170c8e 100644 --- a/docs/docs/how_to/debugger/debugging_with_vs_code.md +++ b/docs/docs/how_to/debugger/debugging_with_vs_code.md @@ -13,7 +13,7 @@ keywords: sidebar_position: 0 --- -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. #### Pre-requisites @@ -23,7 +23,15 @@ This guide will show you how to use VS Code with the vscode-noir extension to de ## Running the debugger -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. +The easiest way to start debugging is to open the file you want to debug, and click on `Debug` codelens over main functions or `Debug test` over `#[test]` functions + +If you don't see the codelens options `Compile|Info|..|Debug` over the `main` function or `Run test| Debug test` over a test function then you probably have the codelens feature disabled. To enable it open the extension configuration page and check the `Enable Code Lens` setting. + +![Debugger codelens](@site/static/img/debugger/debugger-codelens.png) + +Another way of starting the debugger is to press `F5` on the file you want to debug. This will cause the debugger to launch, using your `Prover.toml` file as input. + +Once the debugger has started you should see something like this: You should see something like this: @@ -37,11 +45,11 @@ You will now see two categories of variables: Locals and Witness Map. ![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. 2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. @@ -57,7 +65,7 @@ We can also inspect the values of variables by directly hovering on them on the ![Hover locals](@site/static/img/debugger/6-hover.png) -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. We just need to click to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). diff --git a/docs/docs/reference/debugger/debugger_repl.md b/docs/docs/reference/debugger/debugger_repl.md index 46e2011304e..639f96bd398 100644 --- a/docs/docs/reference/debugger/debugger_repl.md +++ b/docs/docs/reference/debugger/debugger_repl.md @@ -1,7 +1,7 @@ --- title: REPL Debugger description: - Noir Debugger REPL options and commands. + Noir Debugger REPL options and commands. keywords: [ Nargo, @@ -20,14 +20,14 @@ Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes ### Options -| Option | Description | -| --------------------- | ------------------------------------------------------------ | +| Option | Description | +| --------------------------------- | ----------------------------------------------------------------------------------- | | `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| -| `--package ` | The name of the package to debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--test-name ` | The name (or substring) of the test function to debug | +| `--oracle-resolver `| JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | None of these options are required. @@ -35,6 +35,15 @@ None of these options are required. Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. ::: +:::note +If the `--test-name` option is provided the debugger will debug the matching function instead of the package `main` function. +This argument must only match one function. If the given name matches with more than one test function the debugger will not start. +::: + +:::note +For debugging aztec-contract tests that interact the TXE, the json rpc server url must be provided by setting the `--oracle-resolver` option +::: + ## REPL commands Once the debugger is running, it accepts the following commands. @@ -53,6 +62,7 @@ Available commands: out step until a new source location is reached and the current stack frame is finished break LOCATION:OpcodeLocation add a breakpoint at an opcode location + break line:i64 add a breakpoint at an opcode associated to the given source code line over step until a new source location is reached without diving into function calls restart restart the debugging session @@ -94,7 +104,7 @@ Step until the next Noir source code location. While other commands, such as [`i ``` -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. @@ -129,11 +139,11 @@ Step until the end of the current function call. For example: 7 -> assert(deep_entry_point(x) == 4); 8 multiple_values_entry_point(x); 9 } - 10 + 10 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { 12 ... ... - 55 + 55 56 unconstrained fn deep_entry_point(x: u32) -> u32 { 57 -> level_1(x + 1) 58 } @@ -180,7 +190,7 @@ Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcode ... 1.43 | Return 2 EXPR [ (1, _1) -2 ] -``` +``` The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. @@ -249,6 +259,10 @@ In this example, issuing a `break 1.2` command adds break on opcode 1.2, as deno Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. +#### `break [line]` (or shorthand `b [line]`) + +Similar to `break [opcode]`, but instead of selecting the opcode by index selects the opcode location by matching the source code location + #### `delete [Opcode]` (or shorthand `d [Opcode]`) Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). @@ -260,7 +274,7 @@ Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` c Show variable values available at this point in execution. :::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. @@ -357,4 +371,4 @@ Update a memory cell with the given value. For example: :::note This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file +::: diff --git a/docs/docs/reference/debugger/debugger_vscode.md b/docs/docs/reference/debugger/debugger_vscode.md index c027332b3b0..0d91e480f44 100644 --- a/docs/docs/reference/debugger/debugger_vscode.md +++ b/docs/docs/reference/debugger/debugger_vscode.md @@ -17,16 +17,15 @@ sidebar_position: 0 The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. ## Creating and editing launch configuration files -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. ![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) -A `launch.json` file will be created, populated with basic defaults. +A `launch.json` file will be created, populated with basic defaults. ### Noir Debugger launch.json properties @@ -34,7 +33,7 @@ A `launch.json` file will be created, populated with basic defaults. _String, optional._ -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. #### proverName @@ -47,7 +46,7 @@ Name of the prover input to use. Defaults to `Prover`, which looks for a file na _Boolean, optional._ If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - + #### skipInstrumentation _Boolean, optional._ @@ -58,11 +57,32 @@ Skips variables debugging instrumentation of code, making debugging less conveni Skipping instrumentation causes the debugger to be unable to inspect local variables. ::: +#### testName + +_String, optional._ + +Test name (or substring) of the test function to debug. Is not required for the name to be exact as long as it's unequivocal +For the debugger to run only one test function should match the name lookup + +:::note +When provided the debugger will debug the matching function instead of the package `main` function. +::: + +#### oracleResolver + +_String, optional._ + +JSON RPC url to solve oracle calls + +:::note +when running using the `Debug test` codelens, the debugger will set this option with the `TXE_TARGET` environment variable value +::: + ## `nargo dap [OPTIONS]` -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. @@ -72,11 +92,11 @@ If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server ### Options -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | +| Option | Description | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/docs/docs/tooling/debugger.md b/docs/docs/tooling/debugger.md index 200b5fc423a..e00a5e744e9 100644 --- a/docs/docs/tooling/debugger.md +++ b/docs/docs/tooling/debugger.md @@ -17,10 +17,6 @@ In order to use either version of the debugger, you will need to install recent - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/static/img/debugger/debugger-codelens.png b/docs/static/img/debugger/debugger-codelens.png new file mode 100644 index 0000000000000000000000000000000000000000..a71f5f02a73d9442e102e8f40c693da7c07e2aa4 GIT binary patch literal 93022 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 PLx7iui?6f5DLV5nUT1m9 literal 0 HcmV?d00001 From cbea20097ae8497305269bb41241c5ca2257b66a Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 12 Mar 2025 17:26:53 -0300 Subject: [PATCH 48/60] cargo fmt --- tooling/nargo/src/ops/debug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs index 1cbd3bd61fe..84e2867d971 100644 --- a/tooling/nargo/src/ops/debug.rs +++ b/tooling/nargo/src/ops/debug.rs @@ -123,7 +123,7 @@ pub fn compile_options_for_debugging( compile_options: CompileOptions, ) -> CompileOptions { CompileOptions { - // Compilation warnings are disabled when + // Compilation warnings are disabled when // compiling for debugging // // For instrumenting the program the debugger From 78c359b1620396c7fe7e9ab8ba38865c2864e56c Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 14 Mar 2025 14:19:29 -0300 Subject: [PATCH 49/60] Tweak debugger docs text --- docs/docs/reference/debugger/debugger_repl.md | 2 +- docs/docs/reference/debugger/debugger_vscode.md | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/docs/reference/debugger/debugger_repl.md b/docs/docs/reference/debugger/debugger_repl.md index 639f96bd398..71a201213b8 100644 --- a/docs/docs/reference/debugger/debugger_repl.md +++ b/docs/docs/reference/debugger/debugger_repl.md @@ -41,7 +41,7 @@ This argument must only match one function. If the given name matches with more ::: :::note -For debugging aztec-contract tests that interact the TXE, the json rpc server url must be provided by setting the `--oracle-resolver` option +For debugging aztec-contract tests that interact with the TXE ([see further details here](https://docs.aztec.network/developers/guides/smart_contracts/testing)), a JSON RPC server URL must be provided by setting the `--oracle-resolver` option ::: ## REPL commands diff --git a/docs/docs/reference/debugger/debugger_vscode.md b/docs/docs/reference/debugger/debugger_vscode.md index 0d91e480f44..5f3baefdc17 100644 --- a/docs/docs/reference/debugger/debugger_vscode.md +++ b/docs/docs/reference/debugger/debugger_vscode.md @@ -61,21 +61,23 @@ Skipping instrumentation causes the debugger to be unable to inspect local varia _String, optional._ -Test name (or substring) of the test function to debug. Is not required for the name to be exact as long as it's unequivocal -For the debugger to run only one test function should match the name lookup +Test name (or substring) of the test function to debug. The name is not required to match exactly, as long as it's non-ambiguous. +For the debugger to run, only one test function should match the name lookup. + +ie: if there are two test functions `test_simple_assert` and `test_increment`, setting `--test-name test_` will fail with `'test_' matches with more than one test function`. Instead, setting `--test_name test_simple` is not ambiguous, so the debugger will start debugging the `test_simple_assert` test function. :::note -When provided the debugger will debug the matching function instead of the package `main` function. +When provided, the debugger will debug the matching function instead of the package `main` function. ::: #### oracleResolver _String, optional._ -JSON RPC url to solve oracle calls +JSON RPC URL to solve oracle calls. :::note -when running using the `Debug test` codelens, the debugger will set this option with the `TXE_TARGET` environment variable value +When the debugger is run using the `Debug test` codelens, this option is set from the `TXE_TARGET` environment variable value. ::: ## `nargo dap [OPTIONS]` From f1c23aceb54cc60ff3479e142a7da2c5bacadd44 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 17 Mar 2025 15:39:36 -0300 Subject: [PATCH 50/60] Use exact matching test if multiple matches when debugging tests --- tooling/nargo/src/ops/debug.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs index 84e2867d971..ac58b5cc2c5 100644 --- a/tooling/nargo/src/ops/debug.rs +++ b/tooling/nargo/src/ops/debug.rs @@ -37,12 +37,24 @@ pub fn get_test_function_for_debug( matchings if matchings.is_empty() => { return Err(format!("`{}` does not match with any test function", test_name)); } - matchings if matchings.len() == 1 => { - let (name, test_func) = matchings.into_iter().next().unwrap(); - (name, test_func) - } - _ => { - return Err(format!("`{}` matches with more than one test function", test_name)); + matchings if matchings.len() == 1 => matchings.into_iter().next().unwrap(), + matchings => { + let exact_match_op = matchings + .into_iter() + .filter(|(name, _)| name.split("::").last() == Some(test_name)) + .collect::>(); + // There can be multiple matches but only one that matches exactly + // this would be the case of tests names that englobe others + // i.e.: + // - test_something + // - unconstrained_test_something + // in this case, looking up "test_something" throws two matchings + // but only one matches exact + if exact_match_op.len() == 1 { + exact_match_op.into_iter().next().unwrap() + } else { + return Err(format!("`{}` matches with more than one test function", test_name)); + } } }; From ac55d8b3d691b0e041f788bd6286a9e6a0fe9d05 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Mon, 17 Mar 2025 15:41:16 -0300 Subject: [PATCH 51/60] Create debug-test tests for every noir_test_success case --- tooling/debugger/build.rs | 75 ++++++++++++++++++++-- tooling/debugger/ignored-noir-tests.txt | 2 + tooling/debugger/tests/debug.rs | 83 ++++++++++++++++++------- 3 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 tooling/debugger/ignored-noir-tests.txt diff --git a/tooling/debugger/build.rs b/tooling/debugger/build.rs index 4d75cdd4b57..974f52f5bfa 100644 --- a/tooling/debugger/build.rs +++ b/tooling/debugger/build.rs @@ -1,8 +1,7 @@ use std::collections::HashSet; use std::fs::File; -use std::io::Write; +use std::io::{BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; - const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { @@ -33,14 +32,17 @@ fn main() { println!("cargo:rerun-if-changed={}", test_dir.as_os_str().to_str().unwrap()); generate_debugger_tests(&mut test_file, &test_dir); + generate_debugger_test_tests(&mut test_file, &test_dir); } fn generate_debugger_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; let test_data_dir = test_data_dir.join(test_sub_dir); - let test_case_dirs = - std::fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); + let test_case_dirs = std::fs::read_dir(test_data_dir) + .unwrap() + .flatten() + .filter(|c| c.path().is_dir() && c.path().join("Nargo.toml").exists()); let ignored_tests_contents = std::fs::read_to_string("ignored-tests.txt").unwrap(); let ignored_tests = ignored_tests_contents.lines().collect::>(); @@ -70,3 +72,68 @@ fn debug_{test_name}() {{ .expect("Could not write templated test file."); } } + +fn generate_debugger_test_tests(test_file: &mut File, test_data_dir: &Path) { + let test_sub_dir = "noir_test_success"; + let test_data_dir = test_data_dir.join(test_sub_dir); + + let test_case_dirs = std::fs::read_dir(test_data_dir) + .unwrap() + .flatten() + .filter(|c| c.path().is_dir() && c.path().join("Nargo.toml").exists()); + let ignored_tests_contents = std::fs::read_to_string("ignored-noir-tests.txt").unwrap(); + let ignored_tests = ignored_tests_contents.lines().collect::>(); + + for test_dir in test_case_dirs { + let test_file_name = + test_dir.file_name().into_string().expect("Directory can't be converted to string"); + if test_file_name.contains('-') { + panic!( + "Invalid test directory: {test_file_name}. Cannot include `-`, please convert to `_`" + ); + }; + let test_dir = &test_dir.path(); + + let file_name = test_dir.join("src").join("main.nr"); + let buf_reader = + BufReader::new(File::open(file_name.clone()).expect("Could not open file")); + let lines = buf_reader.lines(); + let test_names: Vec = lines + .filter_map(|line_res| { + line_res.ok().map(|line| if line.contains("fn test_") { Some(line) } else { None }) + }) + .flatten() + .collect(); + for test_name_line in test_names { + // TODO: get test name by regex perhaps? + let test_name = test_name_line + .split("fn ") + .collect::>() + .get(1) + .unwrap() + .split("<") + .next() + .unwrap() + .split("(") + .next() + .unwrap(); + println!("cargo::warning=Generating test for {test_name:?}"); + + let ignored = ignored_tests.contains(test_name); + + write!( + test_file, + r#" + #[test] + {ignored} + fn debug_test_{test_file_name}_{test_name}() {{ + debugger_test_success("{test_dir}", "{test_name}"); + }} + "#, + test_dir = test_dir.display(), + ignored = if ignored { "#[ignore]" } else { "" }, + ) + .expect("Could not write templated test file."); + } + } +} diff --git a/tooling/debugger/ignored-noir-tests.txt b/tooling/debugger/ignored-noir-tests.txt new file mode 100644 index 00000000000..06cb533e365 --- /dev/null +++ b/tooling/debugger/ignored-noir-tests.txt @@ -0,0 +1,2 @@ +test_assert_message_preserved_during_optimization +test_vec_new_bad diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 07985742085..c1c6ace6b72 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -3,7 +3,7 @@ mod tests { // Some of these imports are consumed by the injected tests use assert_cmd::cargo::cargo_bin; - use rexpect::spawn_bash; + use rexpect::{session::PtyReplSession, spawn_bash}; // include tests generated by `build.rs` include!(concat!(env!("OUT_DIR"), "/debug.rs")); @@ -12,42 +12,77 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); + let mut dbg_session = start_debug_session(&format!( + "{nargo_bin} debug --program-dir {test_program_dir} --force-brillig --expression-width 3" + )); + + // send continue which should run to the program to end + // given we haven't set any breakpoints. + send_continue_and_check_no_panic(&mut dbg_session); + + send_quit(&mut dbg_session); + dbg_session + .exp_regex(".*Circuit witness successfully solved.*") + .expect("Expected circuit witness to be successfully solved."); + + exit(dbg_session); + } + + pub fn debugger_test_success(test_program_dir: &str, test_name: &str) { + let nargo_bin = + cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); + + let mut dbg_session = start_debug_session( + &(format!( + "{nargo_bin} debug --program-dir {test_program_dir} --test-name {test_name} --force-brillig --expression-width 3" + )), + ); + + // send continue which should run to the program to end + // given we haven't set any breakpoints. + send_continue_and_check_no_panic(&mut dbg_session); + + send_quit(&mut dbg_session); + dbg_session + .exp_regex(".*Testing .*\\.\\.\\. .*ok.*") + .expect("Expected test to be successful"); + + exit(dbg_session); + } + + fn start_debug_session(command: &str) -> PtyReplSession { let timeout_seconds = 30; let mut dbg_session = spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); // Start debugger and test that it loads for the given program. + dbg_session.execute(command, ".*\\Starting debugger.*").expect("Could not start debugger"); dbg_session - .execute( - &format!( - "{nargo_bin} debug --program-dir {test_program_dir} --force-brillig --expression-width 3" - ), - ".*\\Starting debugger.*", - ) - .expect("Could not start debugger"); + } + + fn send_quit(dbg_session: &mut PtyReplSession) { + // Run the "quit" command, then check that the debugger confirms + // having successfully solved the circuit witness. + dbg_session.send_line("quit").expect("Failed to quit debugger"); + } + + /// Exit the bash session. + fn exit(mut dbg_session: PtyReplSession) { + dbg_session.send_line("exit").expect("Failed to quit bash session"); + } - // While running the debugger, issue a "continue" cmd, - // which should run to the program to end given - // we haven't set any breakpoints. - // ">" is the debugger's prompt, so finding one - // after running "continue" indicates that the - // debugger has not panicked until the end of the program. + /// While running the debugger, issue a "continue" cmd, + /// which should run to the program to end or the next breakpoint + /// ">" is the debugger's prompt, so finding one + /// after running "continue" indicates that the + /// debugger has not panicked. + fn send_continue_and_check_no_panic(dbg_session: &mut PtyReplSession) { dbg_session .send_line("c") .expect("Debugger panicked while attempting to step through program."); dbg_session .exp_string(">") .expect("Failed while waiting for debugger to step through program."); - - // Run the "quit" command, then check that the debugger confirms - // having successfully solved the circuit witness. - dbg_session.send_line("quit").expect("Failed to quit debugger"); - dbg_session - .exp_regex(".*Circuit witness successfully solved.*") - .expect("Expected circuit witness to be successfully solved."); - - // Exit the bash session. - dbg_session.send_line("exit").expect("Failed to quit bash session"); } #[test] From 4516a5e53a91937a361ac236beeaf48f53465b93 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Mar 2025 15:26:17 -0300 Subject: [PATCH 52/60] noir_debugger: enable rpc feature when declaring nargo dependency --- tooling/debugger/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/debugger/Cargo.toml b/tooling/debugger/Cargo.toml index 88047a0c281..6bebdd04d15 100644 --- a/tooling/debugger/Cargo.toml +++ b/tooling/debugger/Cargo.toml @@ -16,7 +16,7 @@ build-data.workspace = true [dependencies] acvm.workspace = true fm.workspace = true -nargo.workspace = true +nargo = { workspace = true, features = ["rpc"] } noirc_frontend = { workspace = true, features = ["bn254"] } noirc_printable_type.workspace = true noirc_errors.workspace = true From 0fea2b8da9f62eea087cc732a82ce529061b9c27 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Mar 2025 15:33:32 -0300 Subject: [PATCH 53/60] Remove warning message when building debugger tests --- tooling/debugger/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tooling/debugger/build.rs b/tooling/debugger/build.rs index 974f52f5bfa..899918ef6b8 100644 --- a/tooling/debugger/build.rs +++ b/tooling/debugger/build.rs @@ -117,7 +117,6 @@ fn generate_debugger_test_tests(test_file: &mut File, test_data_dir: &Path) { .split("(") .next() .unwrap(); - println!("cargo::warning=Generating test for {test_name:?}"); let ignored = ignored_tests.contains(test_name); From 82667e02e862fea2738b193660ce8b667c8c4b45 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Mar 2025 15:01:10 -0300 Subject: [PATCH 54/60] Fix debugger_expected_call_stack test The dbg_session read buffer was not being able to retain all the lines produced after all the commands sent without reading the results --- tooling/debugger/tests/debug.rs | 119 +++++++++++++++----------------- 1 file changed, 54 insertions(+), 65 deletions(-) diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index c1c6ace6b72..8a4e1fa5f02 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + use std::collections::VecDeque; + // Some of these imports are consumed by the injected tests use assert_cmd::cargo::cargo_bin; @@ -108,8 +110,39 @@ mod tests { ) .expect("Could not start debugger"); - let num_steps = 16; - for _ in 1..=num_steps { + let expected_lines_by_command: Vec> = vec![ + VecDeque::from(["fn main(x: Field, y: pub Field) {"]), + VecDeque::from(["fn main(x: Field, y: pub Field) {"]), + VecDeque::from(["fn main(x: Field, y: pub Field) {"]), + VecDeque::from([ + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + ]), + VecDeque::from([ + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + ]), + VecDeque::from([" let x = unsafe { baz(x) };", "}"]), + VecDeque::from([" let x = unsafe { baz(x) };"]), + VecDeque::from([" foo(x);", "fn foo(x: Field) {"]), + VecDeque::from([" foo(x);", "fn foo(x: Field) {"]), + VecDeque::from([ + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + ]), + VecDeque::from([ + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + ]), + VecDeque::from([" foo(x);", " let y = unsafe { baz(x) };", "}"]), + VecDeque::from([" foo(x);", " let y = unsafe { baz(x) };"]), + VecDeque::from([" foo(x);", " bar(y);", "fn bar(y: Field) {"]), + VecDeque::from([" foo(x);", " bar(y);", "fn bar(y: Field) {"]), + VecDeque::from([" foo(x);", " bar(y);", " assert(y != 0);"]), + ]; + for mut expected_lines in expected_lines_by_command { // While running the debugger, issue a "next" cmd, // which should run to the program to the next source line given // we haven't set any breakpoints. @@ -122,73 +155,29 @@ mod tests { dbg_session .exp_string(">") .expect("Failed while waiting for debugger to step through program."); - } - let mut lines = vec![]; - while let Ok(line) = dbg_session.read_line() { - if !(line.starts_with(">next") || line.starts_with("At ") || line.starts_with("...")) { - lines.push(line); + while let Some(expected_line) = expected_lines.pop_front() { + let line = loop { + let read_line = dbg_session.read_line().unwrap(); + if !(read_line.contains("> next") + || read_line.starts_with("At ") + || read_line.starts_with("...")) + { + break read_line; + } + }; + let ascii_line: String = line.chars().filter(char::is_ascii).collect(); + let line_expected_to_contain = expected_line.trim_start(); + println!("{ascii_line}"); + assert!( + ascii_line.contains(line_expected_to_contain), + "{:?}\ndid not contain\n{:?}", + ascii_line, + line_expected_to_contain, + ); } } - let lines_expected_to_contain: Vec<&str> = vec![ - "> next", - " let x = unsafe { baz(x) };", - "unconstrained fn baz(x: Field) -> Field {", - "> next", - " let x = unsafe { baz(x) };", - "unconstrained fn baz(x: Field) -> Field {", - "> next", - " let x = unsafe { baz(x) };", - "}", - "> next", - " let x = unsafe { baz(x) };", - "> next", - " foo(x);", - "fn foo(x: Field) {", - "> next", - " foo(x);", - "fn foo(x: Field) {", - "> next", - " foo(x);", - " let y = unsafe { baz(x) };", - "unconstrained fn baz(x: Field) -> Field {", - "> next", - " foo(x);", - " let y = unsafe { baz(x) };", - "unconstrained fn baz(x: Field) -> Field {", - "> next", - " foo(x);", - " let y = unsafe { baz(x) };", - "}", - "> next", - " foo(x);", - " let y = unsafe { baz(x) };", - "> next", - " foo(x);", - " bar(y);", - "fn bar(y: Field) {", - "> next", - " foo(x);", - " bar(y);", - "fn bar(y: Field) {", - "> next", - " foo(x);", - " bar(y);", - " assert(y != 0);", - ]; - - for (line, line_expected_to_contain) in lines.into_iter().zip(lines_expected_to_contain) { - let ascii_line: String = line.chars().filter(char::is_ascii).collect(); - let line_expected_to_contain = line_expected_to_contain.trim_start(); - assert!( - ascii_line.contains(line_expected_to_contain), - "{:?}\ndid not contain\n{:?}", - ascii_line, - line_expected_to_contain, - ); - } - // Run the "quit" command dbg_session.send_line("quit").expect("Failed to quit debugger"); From 814a0af3cca2efe198e3c8cd0c8eec6e19d6a6e2 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Mar 2025 17:00:40 -0300 Subject: [PATCH 55/60] Repl: wait for debugger to be idle after command * Also fix test to support REPL prompt to be rendered at the beginning of debugger output (since now debugger runs in a different thread) --- tooling/debugger/src/repl.rs | 12 ++++-------- tooling/debugger/tests/debug.rs | 8 +++++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index a2694370ec4..9dfe2c09b6c 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -550,9 +550,8 @@ impl DebugCommander { } fn call_debugger(&self, command: DebugCommandAPI) { - self.when_not_busy(|| { - self.command_sender.send(command).expect("Could not communicate with debugger") - }) + self.command_sender.send(command).expect("Could not communicate with debugger"); + self.wait_for_idle(); } fn get_final_result(&self) -> DebugExecutionResult { @@ -564,15 +563,12 @@ impl DebugCommander { } } - fn when_not_busy(&self, callback: F) - where - F: FnOnce(), + fn wait_for_idle(&self) { loop { let status = self.debugger_status(); - let DebuggerStatus::Busy = status else { break }; + if let DebuggerStatus::Idle = status { break }; } - callback(); } pub fn step_acir_opcode(&self) { diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 8a4e1fa5f02..d631ccffbdd 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -97,7 +97,7 @@ mod tests { spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); let test_program_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../test_programs/execution_success/regression_7195"); + .join("../../test_programs/execution_success/regression_7195").canonicalize().unwrap(); let test_program_dir = test_program_path.display(); // Start debugger and test that it loads for the given program. @@ -156,12 +156,14 @@ mod tests { .exp_string(">") .expect("Failed while waiting for debugger to step through program."); + let at_filename = format!("At {test_program_dir}"); while let Some(expected_line) = expected_lines.pop_front() { let line = loop { let read_line = dbg_session.read_line().unwrap(); if !(read_line.contains("> next") - || read_line.starts_with("At ") - || read_line.starts_with("...")) + || read_line.contains("At opcode") + || read_line.contains(at_filename.as_str()) + || read_line.contains("...")) { break read_line; } From 8492fd5adc4241453a1971c6e7c448a54f0e1d24 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Wed, 19 Mar 2025 17:04:12 -0300 Subject: [PATCH 56/60] cargo fmt --- tooling/debugger/src/context.rs | 2 +- tooling/debugger/src/repl.rs | 7 ++++--- tooling/debugger/tests/debug.rs | 4 +++- tooling/nargo/src/ops/execute.rs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index efc172ef517..01a9178c7d2 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -13,7 +13,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; use nargo::NargoError; -use nargo::errors::{execution_error_from, ExecutionError, Location, ResolvedOpcodeLocation}; +use nargo::errors::{ExecutionError, Location, ResolvedOpcodeLocation, execution_error_from}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::{CompiledProgram, DebugFile}; diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 9dfe2c09b6c..d559830fdb8 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -563,11 +563,12 @@ impl DebugCommander { } } - fn wait_for_idle(&self) - { + fn wait_for_idle(&self) { loop { let status = self.debugger_status(); - if let DebuggerStatus::Idle = status { break }; + if let DebuggerStatus::Idle = status { + break; + }; } } diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index d631ccffbdd..3ba8389e83b 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -97,7 +97,9 @@ mod tests { spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); let test_program_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../test_programs/execution_success/regression_7195").canonicalize().unwrap(); + .join("../../test_programs/execution_success/regression_7195") + .canonicalize() + .unwrap(); let test_program_dir = test_program_path.display(); // Start debugger and test that it loads for the given program. diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 0c4e9b3b306..5917c5d331d 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -8,7 +8,7 @@ use acvm::{AcirField, BlackBoxFunctionSolver}; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::NargoError; -use crate::errors::{ExecutionError, execution_error_from, ResolvedOpcodeLocation}; +use crate::errors::{ExecutionError, ResolvedOpcodeLocation, execution_error_from}; use crate::foreign_calls::ForeignCallExecutor; struct ProgramExecutor<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { From 1bd0906eeeb36f47d1c0d7a3247597b46f68f3e0 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 20 Mar 2025 12:24:02 -0300 Subject: [PATCH 57/60] ignore ski_calculus tests since they require an unstable feature --- tooling/debugger/ignored-noir-tests.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tooling/debugger/ignored-noir-tests.txt b/tooling/debugger/ignored-noir-tests.txt index 06cb533e365..c32ecdc23f7 100644 --- a/tooling/debugger/ignored-noir-tests.txt +++ b/tooling/debugger/ignored-noir-tests.txt @@ -1,2 +1,12 @@ test_assert_message_preserved_during_optimization test_vec_new_bad +test_identity +test_identity_and_show +test_logic +test_logic_and_show +test_numeric +test_numeric_and_show +test_pow +test_pow_and_show +test_add +test_add_and_show From affdcdeb5e9430097df48ddaee807a5e4cdf2895 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 20 Mar 2025 17:07:34 -0300 Subject: [PATCH 58/60] Add gear to 'Debug test' codelens to mimic Rust's codelens --- tooling/lsp/src/requests/code_lens_request.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index 98f4d128b1d..013f9e6970e 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -12,6 +12,7 @@ use crate::{ }; const ARROW: &str = "▶\u{fe0e}"; +const GEAR: &str = "⚙"; const TEST_COMMAND: &str = "nargo.test"; const TEST_CODELENS_TITLE: &str = "Run Test"; const COMPILE_COMMAND: &str = "nargo.compile"; @@ -129,7 +130,7 @@ pub(crate) fn collect_lenses_for_package( lenses.push(test_lens); let debug_test_command = Command { - title: DEBUG_TEST_CODELENS_TITLE.to_string(), + title: format!("{GEAR} {DEBUG_TEST_CODELENS_TITLE}"), command: DEBUG_TEST_COMMAND.into(), arguments: Some( [ From 717a9e70a821bb60f60b82bc9aa7e5820f81415b Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 21 Mar 2025 15:45:00 -0300 Subject: [PATCH 59/60] Rename DebugCommander and generate debugger tests function --- tooling/debugger/build.rs | 4 ++-- tooling/debugger/src/repl.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tooling/debugger/build.rs b/tooling/debugger/build.rs index 899918ef6b8..204957ae987 100644 --- a/tooling/debugger/build.rs +++ b/tooling/debugger/build.rs @@ -32,7 +32,7 @@ fn main() { println!("cargo:rerun-if-changed={}", test_dir.as_os_str().to_str().unwrap()); generate_debugger_tests(&mut test_file, &test_dir); - generate_debugger_test_tests(&mut test_file, &test_dir); + generate_test_runner_debugger_tests(&mut test_file, &test_dir); } fn generate_debugger_tests(test_file: &mut File, test_data_dir: &Path) { @@ -73,7 +73,7 @@ fn debug_{test_name}() {{ } } -fn generate_debugger_test_tests(test_file: &mut File, test_data_dir: &Path) { +fn generate_test_runner_debugger_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "noir_test_success"; let test_data_dir = test_data_dir.join(test_sub_dir); diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index d559830fdb8..e7456604c62 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -540,11 +540,11 @@ impl<'a> AsyncReplDebugger<'a> { } } -struct DebugCommander { +struct DebugController { command_sender: Sender, status_receiver: Receiver, } -impl DebugCommander { +impl DebugController { fn debugger_status(&self) -> DebuggerStatus { self.status_receiver.recv().expect("Debugger closed connection unexpectedly") } @@ -664,7 +664,7 @@ pub fn run(project: DebugProject, run_params: RunParams) -> DebugExecutionResult }); let context = - RefCell::new(DebugCommander { command_sender: command_tx, status_receiver: status_rx }); + RefCell::new(DebugController { command_sender: command_tx, status_receiver: status_rx }); let ref_context = &context; ref_context.borrow().show_current_vm_status(); From fa028e7e392fd651cfcbeb4f0c82d51c3cf9b26c Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Fri, 21 Mar 2025 15:52:06 -0300 Subject: [PATCH 60/60] remove useless white spaces --- tooling/debugger/tests/debug.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 3ba8389e83b..9ed46003e9b 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -117,32 +117,32 @@ mod tests { VecDeque::from(["fn main(x: Field, y: pub Field) {"]), VecDeque::from(["fn main(x: Field, y: pub Field) {"]), VecDeque::from([ - " let x = unsafe { baz(x) };", + "let x = unsafe { baz(x) };", "unconstrained fn baz(x: Field) -> Field {", ]), VecDeque::from([ - " let x = unsafe { baz(x) };", + "let x = unsafe { baz(x) };", "unconstrained fn baz(x: Field) -> Field {", ]), - VecDeque::from([" let x = unsafe { baz(x) };", "}"]), - VecDeque::from([" let x = unsafe { baz(x) };"]), - VecDeque::from([" foo(x);", "fn foo(x: Field) {"]), - VecDeque::from([" foo(x);", "fn foo(x: Field) {"]), + VecDeque::from(["let x = unsafe { baz(x) };", "}"]), + VecDeque::from(["let x = unsafe { baz(x) };"]), + VecDeque::from(["foo(x);", "fn foo(x: Field) {"]), + VecDeque::from(["foo(x);", "fn foo(x: Field) {"]), VecDeque::from([ - " foo(x);", - " let y = unsafe { baz(x) };", + "foo(x);", + "let y = unsafe { baz(x) };", "unconstrained fn baz(x: Field) -> Field {", ]), VecDeque::from([ - " foo(x);", - " let y = unsafe { baz(x) };", + "foo(x);", + "let y = unsafe { baz(x) };", "unconstrained fn baz(x: Field) -> Field {", ]), - VecDeque::from([" foo(x);", " let y = unsafe { baz(x) };", "}"]), - VecDeque::from([" foo(x);", " let y = unsafe { baz(x) };"]), - VecDeque::from([" foo(x);", " bar(y);", "fn bar(y: Field) {"]), - VecDeque::from([" foo(x);", " bar(y);", "fn bar(y: Field) {"]), - VecDeque::from([" foo(x);", " bar(y);", " assert(y != 0);"]), + VecDeque::from(["foo(x);", "let y = unsafe { baz(x) };", "}"]), + VecDeque::from(["foo(x);", "let y = unsafe { baz(x) };"]), + VecDeque::from(["foo(x);", "bar(y);", "fn bar(y: Field) {"]), + VecDeque::from(["foo(x);", "bar(y);", "fn bar(y: Field) {"]), + VecDeque::from(["foo(x);", "bar(y);", "assert(y != 0);"]), ]; for mut expected_lines in expected_lines_by_command { // While running the debugger, issue a "next" cmd, @@ -171,8 +171,7 @@ mod tests { } }; let ascii_line: String = line.chars().filter(char::is_ascii).collect(); - let line_expected_to_contain = expected_line.trim_start(); - println!("{ascii_line}"); + let line_expected_to_contain = expected_line.trim(); assert!( ascii_line.contains(line_expected_to_contain), "{:?}\ndid not contain\n{:?}",