Skip to content

Commit

Permalink
feat: Allow inspecting variables and stack trace in DAP mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ggiraldez committed Jan 26, 2024
1 parent 96315d7 commit b7f2540
Showing 1 changed file with 166 additions and 43 deletions.
209 changes: 166 additions & 43 deletions tooling/debugger/src/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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;

Expand All @@ -41,6 +42,24 @@ pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> {
source_breakpoints: BTreeMap<FileId, Vec<(OpcodeLocation, i64)>>,
}

enum ScopeReferences {
Locals = 1,
WitnessMap = 2,
BrilligRegisters = 3,
InvalidScope = 0,
}

impl From<i64> for ScopeReferences {
fn from(value: i64) -> Self {
match value {
1 => Self::Locals,
2 => Self::WitnessMap,
3 => Self::BrilligRegisters,
_ => Self::InvalidScope,
}
}
}

// BTreeMap<FileId, Vec<(usize, OpcodeLocation)>

impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> {
Expand Down Expand Up @@ -176,34 +195,33 @@ 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) => {
let granularity =
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) => {
let granularity =
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);
Expand All @@ -213,37 +231,40 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> {
Ok(())
}

fn build_stack_trace(&self) -> Vec<StackFrame> {
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::<Vec<_>>()
.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,
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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<Variable> {
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<Variable> {
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<Variable> {
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<R: Read, W: Write, B: BlackBoxFunctionSolver>(
Expand Down

0 comments on commit b7f2540

Please sign in to comment.