From b7f25406b0f2bbc77cb5e6e3786b24362300ba18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 24 Jan 2024 12:26:51 -0500 Subject: [PATCH] feat: Allow inspecting variables and stack trace in DAP mode --- tooling/debugger/src/dap.rs | 209 ++++++++++++++++++++++++++++-------- 1 file changed, 166 insertions(+), 43 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 30d52890214..17ff81c52f0 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use acvm::acir::circuit::{Circuit, OpcodeLocation}; use acvm::acir::native_types::WitnessMap; +use acvm::brillig_vm::Registers; use acvm::BlackBoxFunctionSolver; use codespan_reporting::files::{Files, SimpleFile}; @@ -18,12 +19,12 @@ use dap::requests::{Command, Request, SetBreakpointsArguments}; use dap::responses::{ ContinueResponse, DisassembleResponse, ResponseBody, ScopesResponse, SetBreakpointsResponse, SetExceptionBreakpointsResponse, SetInstructionBreakpointsResponse, StackTraceResponse, - ThreadsResponse, + ThreadsResponse, VariablesResponse, }; use dap::server::Server; use dap::types::{ - Breakpoint, DisassembledInstruction, Source, StackFrame, SteppingGranularity, - StoppedEventReason, Thread, + Breakpoint, DisassembledInstruction, Scope, Source, StackFrame, SteppingGranularity, + StoppedEventReason, Thread, Variable, }; use nargo::artifacts::debug::DebugArtifact; @@ -41,6 +42,24 @@ pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> { source_breakpoints: BTreeMap>, } +enum ScopeReferences { + Locals = 1, + WitnessMap = 2, + BrilligRegisters = 3, + InvalidScope = 0, +} + +impl From for ScopeReferences { + fn from(value: i64) -> Self { + match value { + 1 => Self::Locals, + 2 => Self::WitnessMap, + 3 => Self::BrilligRegisters, + _ => Self::InvalidScope, + } + } +} + // BTreeMap impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { @@ -176,7 +195,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement); match granularity { SteppingGranularity::Instruction => self.handle_step(req)?, - _ => self.handle_next(req)?, + _ => self.handle_next_into(req)?, } } Command::StepOut(ref args) => { @@ -184,7 +203,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement); match granularity { SteppingGranularity::Instruction => self.handle_step(req)?, - _ => self.handle_next(req)?, + _ => self.handle_next_out(req)?, } } Command::Next(ref args) => { @@ -192,18 +211,17 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement); match granularity { SteppingGranularity::Instruction => self.handle_step(req)?, - _ => self.handle_next(req)?, + _ => self.handle_next_over(req)?, } } Command::Continue(_) => { self.handle_continue(req)?; } Command::Scopes(_) => { - // FIXME: this needs a proper implementation when we can - // show the parameters and variables - self.server.respond( - req.success(ResponseBody::Scopes(ScopesResponse { scopes: vec![] })), - )?; + self.handle_scopes(req)?; + } + Command::Variables(ref _args) => { + self.handle_variables(req)?; } _ => { eprintln!("ERROR: unhandled command: {:?}", req.command); @@ -213,37 +231,40 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { Ok(()) } + fn build_stack_trace(&self) -> Vec { + self.context + .get_source_call_stack() + .iter() + .enumerate() + .map(|(index, (opcode_location, source_location))| { + let line_number = + self.debug_artifact.location_line_number(*source_location).unwrap(); + let column_number = + self.debug_artifact.location_column_number(*source_location).unwrap(); + StackFrame { + id: index as i64, + name: format!("frame #{index}"), + source: Some(Source { + path: self.debug_artifact.file_map[&source_location.file] + .path + .to_str() + .map(String::from), + ..Source::default() + }), + line: line_number as i64, + column: column_number as i64, + instruction_pointer_reference: Some(opcode_location.to_string()), + ..StackFrame::default() + } + }) + .collect::>() + .into_iter() + .rev() + .collect() + } + fn handle_stack_trace(&mut self, req: Request) -> Result<(), ServerError> { - let opcode_location = self.context.get_current_opcode_location(); - let source_location = self.context.get_current_source_location(); - let frames = match source_location { - None => vec![], - Some(locations) => locations - .iter() - .enumerate() - .map(|(index, location)| { - let line_number = self.debug_artifact.location_line_number(*location).unwrap(); - let column_number = - self.debug_artifact.location_column_number(*location).unwrap(); - let ip_reference = opcode_location.map(|location| location.to_string()); - StackFrame { - id: index as i64, - name: format!("frame #{index}"), - source: Some(Source { - path: self.debug_artifact.file_map[&location.file] - .path - .to_str() - .map(String::from), - ..Source::default() - }), - line: line_number as i64, - column: column_number as i64, - instruction_pointer_reference: ip_reference, - ..StackFrame::default() - } - }) - .collect(), - }; + let frames = self.build_stack_trace(); let total_frames = Some(frames.len() as i64); self.server.respond(req.success(ResponseBody::StackTrace(StackTraceResponse { stack_frames: frames, @@ -315,9 +336,23 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { self.handle_execution_result(result) } - fn handle_next(&mut self, req: Request) -> Result<(), ServerError> { + fn handle_next_into(&mut self, req: Request) -> Result<(), ServerError> { let result = self.context.next_into(); - eprintln!("INFO: stepped by statement with result {result:?}"); + eprintln!("INFO: stepped into by statement with result {result:?}"); + self.server.respond(req.ack()?)?; + self.handle_execution_result(result) + } + + fn handle_next_out(&mut self, req: Request) -> Result<(), ServerError> { + let result = self.context.next_out(); + eprintln!("INFO: stepped out by statement with result {result:?}"); + self.server.respond(req.ack()?)?; + self.handle_execution_result(result) + } + + fn handle_next_over(&mut self, req: Request) -> Result<(), ServerError> { + let result = self.context.next_over(); + eprintln!("INFO: stepped over by statement with result {result:?}"); self.server.respond(req.ack()?)?; self.handle_execution_result(result) } @@ -548,6 +583,94 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { )?; Ok(()) } + + fn handle_scopes(&mut self, req: Request) -> Result<(), ServerError> { + self.server.respond(req.success(ResponseBody::Scopes(ScopesResponse { + scopes: vec![ + Scope { + name: String::from("Locals"), + variables_reference: ScopeReferences::Locals as i64, + ..Scope::default() + }, + Scope { + name: String::from("Witness Map"), + variables_reference: ScopeReferences::WitnessMap as i64, + ..Scope::default() + }, + Scope { + name: String::from("Brillig Registers"), + variables_reference: ScopeReferences::BrilligRegisters as i64, + ..Scope::default() + }, + ], + })))?; + Ok(()) + } + + fn build_local_variables(&self) -> Vec { + let mut variables: Vec<_> = self + .context + .get_variables() + .iter() + .map(|(name, value, _var_type)| Variable { + name: String::from(*name), + value: format!("{:?}", *value), + ..Variable::default() + }) + .collect(); + variables.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap()); + variables + } + + fn build_witness_map(&self) -> Vec { + self.context + .get_witness_map() + .clone() + .into_iter() + .map(|(witness, value)| Variable { + name: format!("_{}", witness.witness_index()), + value: format!("{value:?}"), + ..Variable::default() + }) + .collect() + } + + fn build_brillig_registers(&self) -> Vec { + self.context + .get_brillig_registers() + .unwrap_or(&Registers { inner: vec![] }) + .inner + .iter() + .enumerate() + .map(|(index, value)| Variable { + name: format!("R{index}"), + value: format!("{value:?}"), + ..Variable::default() + }) + .collect() + } + + fn handle_variables(&mut self, req: Request) -> Result<(), ServerError> { + let Command::Variables(ref args) = req.command else { + unreachable!("handle_variables called on a different request"); + }; + let scope: ScopeReferences = args.variables_reference.into(); + let variables: Vec<_> = match scope { + ScopeReferences::Locals => self.build_local_variables(), + ScopeReferences::WitnessMap => self.build_witness_map(), + ScopeReferences::BrilligRegisters => self.build_brillig_registers(), + _ => { + eprintln!( + "handle_variables with an unknown variables_reference {}", + args.variables_reference + ); + vec![] + } + }; + self.server + .respond(req.success(ResponseBody::Variables(VariablesResponse { variables })))?; + Ok(()) + } } pub fn run_session(