-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Description Adds documentation for the Noir debugger. ## Problem Part of #3015. ## Summary Adds quickstart, how to's and reference pages to Noir's docsite covering both the VS Code and REPL debuggers. ## Documentation Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
- Loading branch information
Showing
24 changed files
with
777 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,6 +113,7 @@ | |
"Maddiaa", | ||
"mathbb", | ||
"memfs", | ||
"memset", | ||
"merkle", | ||
"metas", | ||
"minreq", | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"label": "Debugging", | ||
"position": 5, | ||
"collapsible": true, | ||
"collapsed": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
--- | ||
title: Using the REPL Debugger | ||
description: | ||
Step by step guide on how to debug your Noir circuits with the REPL Debugger. | ||
keywords: | ||
[ | ||
Nargo, | ||
Noir CLI, | ||
Noir Debugger, | ||
REPL, | ||
] | ||
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. | ||
|
||
## Debugging a simple circuit | ||
|
||
Let's debug a simple circuit: | ||
|
||
```rust | ||
fn main(x : Field, y : pub Field) { | ||
assert(x != y); | ||
} | ||
``` | ||
|
||
To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: | ||
|
||
`$ nargo debug` | ||
|
||
You should be seeing this in your terminal: | ||
|
||
``` | ||
[main] Starting debugger | ||
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. | ||
|
||
Let's first take a look at the available commands. For that we'll use the `help` command. | ||
|
||
``` | ||
> help | ||
Available commands: | ||
opcodes display ACIR opcodes | ||
into step into to the next opcode | ||
next step until a new source location is reached | ||
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 | ||
over step until a new source location is reached | ||
without diving into function calls | ||
restart restart the debugging session | ||
delete LOCATION:OpcodeLocation delete breakpoint at an opcode location | ||
witness show witness map | ||
witness index:u32 display a single witness from the witness map | ||
witness index:u32 value:String update a witness with the given value | ||
memset index:usize value:String update a memory cell with the given | ||
value | ||
continue continue execution until the end of the | ||
program | ||
vars show variable values available at this point | ||
in execution | ||
stacktrace display the current stack trace | ||
memory show memory (valid when executing unconstrained code) | ||
step step to the next ACIR opcode | ||
Other commands: | ||
help Show this help message | ||
quit Quit repl | ||
``` | ||
|
||
Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: | ||
|
||
``` | ||
> memory | ||
Unconstrained VM memory not available | ||
> | ||
``` | ||
|
||
Before continuing, we can take a look at the initial witness map: | ||
|
||
``` | ||
> witness | ||
_0 = 1 | ||
_1 = 2 | ||
> | ||
``` | ||
|
||
Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: | ||
|
||
``` | ||
> witness | ||
_0 = 1 | ||
_1 = 2 | ||
> witness 1 3 | ||
_1 = 3 | ||
> witness | ||
_0 = 1 | ||
_1 = 3 | ||
> witness 1 2 | ||
_1 = 2 | ||
> witness | ||
_0 = 1 | ||
_1 = 2 | ||
> | ||
``` | ||
|
||
Now we can inspect the current state of local variables. For that we use the `vars` command. | ||
|
||
``` | ||
> vars | ||
> | ||
``` | ||
|
||
We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. | ||
|
||
``` | ||
> vars | ||
> next | ||
At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 | ||
1 -> fn main(x : Field, y : pub Field) { | ||
2 assert(x != y); | ||
3 } | ||
> vars | ||
x:Field = 0x01 | ||
``` | ||
|
||
As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. | ||
|
||
``` | ||
> next | ||
1 fn main(x : Field, y : pub Field) { | ||
2 -> assert(x != y); | ||
3 } | ||
> vars | ||
y:Field = 0x02 | ||
x:Field = 0x01 | ||
``` | ||
|
||
Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. | ||
|
||
Let's continue to the end: | ||
|
||
``` | ||
> continue | ||
(Continuing execution...) | ||
Finished execution | ||
> q | ||
[main] Circuit witness successfully solved | ||
``` | ||
|
||
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
--- | ||
title: Using the VS Code Debugger | ||
description: | ||
Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. | ||
keywords: | ||
[ | ||
Nargo, | ||
Noir CLI, | ||
Noir Debugger, | ||
VS Code, | ||
IDE, | ||
] | ||
sidebar_position: 0 | ||
--- | ||
|
||
This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. | ||
|
||
#### Pre-requisites | ||
|
||
- Nargo | ||
- vscode-noir | ||
- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). | ||
|
||
## 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. | ||
|
||
You should see something like this: | ||
|
||
![Debugger launched](@site/static/img/debugger/1-started.png) | ||
|
||
Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: | ||
|
||
![Debug pane icon](@site/static/img/debugger/2-icon.png) | ||
|
||
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. | ||
|
||
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. | ||
|
||
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. | ||
|
||
Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. | ||
|
||
![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) | ||
|
||
Now we can see in the variables pane that there's values for `digest`, `result` and `x`. | ||
|
||
![Inspecting locals](@site/static/img/debugger/5-assert.png) | ||
|
||
We can also inspect the values of variables by directly hovering on them on the code. | ||
|
||
![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. | ||
|
||
We just need to click the 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). | ||
|
||
![Breakpoint](@site/static/img/debugger/7-break.png) | ||
|
||
Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. | ||
|
||
That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
...s/getting_started/tooling/_category_.json → docs/docs/reference/debugger/_category_.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"position": 2, | ||
"label": "Tooling", | ||
"label": "Debugger", | ||
"position": 1, | ||
"collapsible": true, | ||
"collapsed": true | ||
} |
59 changes: 59 additions & 0 deletions
59
docs/docs/reference/debugger/debugger_known_limitations.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
--- | ||
title: Known limitations | ||
description: | ||
An overview of known limitations of the current version of the Noir debugger | ||
keywords: | ||
[ | ||
Nargo, | ||
Noir Debugger, | ||
VS Code, | ||
] | ||
sidebar_position: 2 | ||
--- | ||
|
||
# Debugger Known Limitations | ||
|
||
There are currently some limits to what the debugger can observe. | ||
|
||
## Mutable references | ||
|
||
The debugger is currently blind to any state mutated via a mutable reference. For example, in: | ||
|
||
``` | ||
let mut x = 1; | ||
let y = &mut x; | ||
*y = 2; | ||
``` | ||
|
||
The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. | ||
|
||
## Variables of type function or mutable references are opaque | ||
|
||
When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<<function>>` or `<<mutable ref>>`. | ||
|
||
## Debugger instrumentation affects resulting ACIR | ||
In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: | ||
|
||
``` | ||
... | ||
5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] | ||
| outputs=[] | ||
5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } | ||
5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } | ||
5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } | ||
5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } | ||
5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } | ||
5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } | ||
5.6 | Call { location: 8 } | ||
5.7 | Stop | ||
5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } | ||
... | ||
``` | ||
If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). | ||
|
||
:::note | ||
Skipping debugger instrumentation means you won't be able to inspect values of local variables. | ||
::: | ||
|
Oops, something went wrong.