Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Implement DAP protocol in Nargo #3627

Merged
merged 22 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3693eb6
feat: empty skeleton for dap command
ggiraldez Nov 6, 2023
01ad804
feat: map errors from Dap into CliErrors and start basic server loop
ggiraldez Nov 7, 2023
422d13f
WIP
ggiraldez Nov 10, 2023
fcf43c1
WIP: next, continue and disassembly view
ggiraldez Nov 14, 2023
cff989e
feat: rewrite offset_opcode_location and add unit tests
ggiraldez Nov 17, 2023
e1da132
feat: render the circuit and Brillig opcodes for disassembled view
ggiraldez Nov 18, 2023
5a1877c
chore: refactor dap_cmd for readability
ggiraldez Nov 18, 2023
e3f3d4f
chore: refactor, move DAP implementation into a new file
ggiraldez Nov 18, 2023
2fc41cb
chore: extract stack trace and disassemble handlers
ggiraldez Nov 20, 2023
b0be45b
chore: revert backend-interface crate rename
ggiraldez Nov 20, 2023
9b1d251
chore: remove unneeded dependency
ggiraldez Nov 20, 2023
a650368
feat: implement instruction and source breakpoints
ggiraldez Nov 24, 2023
27cb858
feat: handle breakpoints correctly and emit events when they are reached
ggiraldez Nov 27, 2023
588c681
feat: handle stepping granularity
ggiraldez Nov 27, 2023
8aa9327
chore: cargo fmt & clippy
ggiraldez Nov 29, 2023
5315992
Apply suggestions from code review
ggiraldez Dec 12, 2023
6a52ab2
chore: fix compilation errors and improve some comments
ggiraldez Dec 12, 2023
46632a8
Merge remote-tracking branch 'upstream/master' into dap
ggiraldez Dec 14, 2023
ebbcdb3
Merge remote-tracking branch 'upstream/master' into dap
ggiraldez Dec 19, 2023
51238e1
chore: fix compilation errors after merge
ggiraldez Dec 19, 2023
a1bad3f
Merge remote-tracking branch 'upstream/master' into dap
ggiraldez Dec 19, 2023
cbda305
chore: fix test compilation
ggiraldez Dec 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ gloo-utils = { version = "0.1", features = ["serde"] }
js-sys = "0.3.62"
getrandom = "0.2"

# Debugger
dap = "0.4.1-alpha1"

cfg-if = "1.0.0"
clap = { version = "4.3.19", features = ["derive", "env"] }
Expand Down
4 changes: 4 additions & 0 deletions tooling/debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ acvm.workspace = true
nargo.workspace = true
noirc_printable_type.workspace = true
noirc_errors.workspace = true
noirc_driver.workspace = true
fm.workspace = true
thiserror.workspace = true
codespan-reporting.workspace = true
dap.workspace = true
easy-repl = "0.2.1"
owo-colors = "3"
serde_json.workspace = true
224 changes: 223 additions & 1 deletion tooling/debugger/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,109 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
.and_then(|location| self.debug_artifact.debug_symbols[0].opcode_location(location))
}

fn get_opcodes_sizes(&self) -> Vec<usize> {
self.get_opcodes()
.iter()
.map(|opcode| match opcode {
Opcode::Brillig(brillig_block) => brillig_block.bytecode.len(),
_ => 1,
})
.collect()
}

/// Offsets the given location by the given number of opcodes (including
/// Brillig opcodes). If the offset would move the location outside of a
/// valid circuit location, returns None and the number of remaining
/// opcodes/instructions left which span outside the valid range in the
/// second element of the returned tuple.
pub(super) fn offset_opcode_location(
&self,
location: &Option<OpcodeLocation>,
mut offset: i64,
) -> (Option<OpcodeLocation>, i64) {
if offset == 0 {
return (*location, 0);
}
let Some(location) = location else {
return (None, offset);
};

let (mut acir_index, mut brillig_index) = match location {
OpcodeLocation::Acir(acir_index) => (*acir_index, 0),
OpcodeLocation::Brillig { acir_index, brillig_index } => (*acir_index, *brillig_index),
};
let opcode_sizes = self.get_opcodes_sizes();
if offset > 0 {
while offset > 0 {
let opcode_size = opcode_sizes[acir_index] as i64 - brillig_index as i64;
if offset >= opcode_size {
acir_index += 1;
offset -= opcode_size;
brillig_index = 0;
} else {
brillig_index += offset as usize;
offset = 0;
}
if acir_index >= opcode_sizes.len() {
return (None, offset);
}
}
} else {
while offset < 0 {
if brillig_index > 0 {
if brillig_index > (-offset) as usize {
brillig_index -= (-offset) as usize;
offset = 0;
} else {
offset += brillig_index as i64;
brillig_index = 0;
}
} else {
if acir_index == 0 {
return (None, offset);
}
acir_index -= 1;
let opcode_size = opcode_sizes[acir_index] as i64;
if opcode_size <= -offset {
offset += opcode_size;
} else {
brillig_index = (opcode_size + offset) as usize;
offset = 0;
}
}
}
}
if brillig_index > 0 {
(Some(OpcodeLocation::Brillig { acir_index, brillig_index }), 0)
} else {
(Some(OpcodeLocation::Acir(acir_index)), 0)
}
}

pub(super) fn render_opcode_at_location(&self, location: &Option<OpcodeLocation>) -> String {
let opcodes = self.get_opcodes();
match location {
None => String::from("invalid"),
Some(OpcodeLocation::Acir(acir_index)) => {
let opcode = &opcodes[*acir_index];
if let Opcode::Brillig(ref brillig) = opcode {
let first_opcode = &brillig.bytecode[0];
format!("BRILLIG {first_opcode:?}")
} else {
format!("{opcode:?}")
}
}
Some(OpcodeLocation::Brillig { acir_index, brillig_index }) => {
if let Opcode::Brillig(ref brillig) = opcodes[*acir_index] {
let opcode = &brillig.bytecode[*brillig_index];
format!(" | {opcode:?}")
} else {
String::from(" | invalid")
}
}
}
}

fn step_brillig_opcode(&mut self) -> DebugCommandResult {
let Some(mut solver) = self.brillig_solver.take() else {
unreachable!("Missing Brillig solver");
Expand Down Expand Up @@ -311,6 +414,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
self.breakpoints.iter()
}

pub(super) fn clear_breakpoints(&mut self) {
self.breakpoints.clear();
}

pub(super) fn is_solved(&self) -> bool {
matches!(self.acvm.get_status(), ACVMStatus::Solved)
}
Expand All @@ -327,7 +434,10 @@ mod tests {

use acvm::{
acir::{
circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs},
circuit::{
brillig::{Brillig, BrilligInputs, BrilligOutputs},
opcodes::BlockId,
},
native_types::Expression,
},
brillig_vm::brillig::{
Expand Down Expand Up @@ -535,4 +645,116 @@ mod tests {
assert!(matches!(result, DebugCommandResult::Done));
assert_eq!(context.get_current_opcode_location(), None);
}

#[test]
fn test_offset_opcode_location() {
let blackbox_solver = &StubbedSolver;
let opcodes = vec![
Opcode::Brillig(Brillig {
inputs: vec![],
outputs: vec![],
bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop],
predicate: None,
}),
Opcode::MemoryInit { block_id: BlockId(0), init: vec![] },
Opcode::Brillig(Brillig {
inputs: vec![],
outputs: vec![],
bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop],
predicate: None,
}),
Opcode::AssertZero(Expression::default()),
];
let circuit = Circuit { opcodes, ..Circuit::default() };
let debug_artifact =
DebugArtifact { debug_symbols: vec![], file_map: BTreeMap::new(), warnings: vec![] };
let context = DebugContext::new(
blackbox_solver,
&circuit,
&debug_artifact,
WitnessMap::new(),
Box::new(DefaultForeignCallExecutor::new(true)),
);

assert_eq!(context.offset_opcode_location(&None, 0), (None, 0));
assert_eq!(context.offset_opcode_location(&None, 2), (None, 2));
assert_eq!(context.offset_opcode_location(&None, -2), (None, -2));
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 0),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 1),
(Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 2),
(Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 3),
(Some(OpcodeLocation::Acir(1)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 4),
(Some(OpcodeLocation::Acir(2)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 5),
(Some(OpcodeLocation::Brillig { acir_index: 2, brillig_index: 1 }), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 7),
(Some(OpcodeLocation::Acir(3)), 0)
);
assert_eq!(context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 8), (None, 0));
assert_eq!(context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), 20), (None, 12));
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(1)), 2),
(Some(OpcodeLocation::Brillig { acir_index: 2, brillig_index: 1 }), 0)
);
assert_eq!(context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), -1), (None, -1));
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(0)), -10),
(None, -10)
);

assert_eq!(
context.offset_opcode_location(
&Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }),
-1
),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(
&Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }),
-2
),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(1)), -3),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(2)), -4),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(
&Some(OpcodeLocation::Brillig { acir_index: 2, brillig_index: 1 }),
-5
),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(3)), -7),
(Some(OpcodeLocation::Acir(0)), 0)
);
assert_eq!(
context.offset_opcode_location(&Some(OpcodeLocation::Acir(2)), -2),
(Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }), 0)
);
}
}
Loading
Loading