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

chore(debugger): Docs #4145

Merged
merged 27 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2015d62
first stub
mverzilli Jan 23, 2024
4d940cb
Update debugging_with_the_repl.md
mverzilli Jan 23, 2024
848f206
Update debugging_with_the_repl.md
mverzilli Jan 23, 2024
c9b86e7
Initial drafts for missing parts
mverzilli Jan 24, 2024
6a5dea3
Polish quickstart doc
mverzilli Jan 24, 2024
fd7773b
Fix debugger doc folders casing
mverzilli Jan 24, 2024
e042a2b
Fix debugger how to category
mverzilli Jan 24, 2024
c1a8b33
Polish VS Code reference
mverzilli Jan 24, 2024
123d799
Polish repl how to
mverzilli Jan 24, 2024
bffcd34
Polished vscode reference
mverzilli Jan 24, 2024
eabc04f
Polish REPL reference
mverzilli Jan 24, 2024
3fbba2b
Polish known limitations
mverzilli Jan 24, 2024
e6311be
Replace Brillig with unconstrained lingo
mverzilli Jan 25, 2024
3127a89
Use admonitions for notes
mverzilli Jan 25, 2024
d7f23d9
Remove quickstart from debugger intro
mverzilli Jan 25, 2024
0ae76d8
Move tooling to root level
mverzilli Jan 25, 2024
338b269
Remove references to Brillig
mverzilli Mar 13, 2024
1ca9c3a
Merge branch 'master' into chore/debugger-docs
mverzilli Mar 13, 2024
51cf8f6
Remove minimum versions for release
mverzilli Mar 15, 2024
d35e25f
Merge branch 'master' into chore/debugger-docs
mverzilli Mar 15, 2024
abd4641
Merge branch 'master' into chore/debugger-docs
mverzilli Mar 18, 2024
daa3f68
Revert accidental manual change on versioned docs
mverzilli Mar 18, 2024
73a4cb1
Add missing line at eof
mverzilli Mar 18, 2024
1038475
Fix typo
mverzilli Mar 18, 2024
1525d15
Add new spellchecker exception
mverzilli Mar 18, 2024
982570f
Merge branch 'master' into chore/debugger-docs
mverzilli Mar 19, 2024
147b7d7
Replace confusing mention to _hydration_
mverzilli Mar 22, 2024
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
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"Maddiaa",
"mathbb",
"memfs",
"memset",
"merkle",
"metas",
"minreq",
Expand Down
38 changes: 0 additions & 38 deletions docs/docs/getting_started/tooling/index.mdx

This file was deleted.

6 changes: 6 additions & 0 deletions docs/docs/how_to/debugger/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"label": "Debugging",
"position": 5,
"collapsible": true,
"collapsed": true
}
164 changes: 164 additions & 0 deletions docs/docs/how_to/debugger/debugging_with_the_repl.md
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).
68 changes: 68 additions & 0 deletions docs/docs/how_to/debugger/debugging_with_vs_code.md
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.
1 change: 1 addition & 0 deletions docs/docs/how_to/merkle-proof.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description:
merkle tree with a specified root, at a given index.
keywords:
[merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree]
sidebar_position: 4
---

Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/noir/concepts/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ fn main(x : [Field]) // can't compile, has variable size
fn main(....// i think you got it by now
```

Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove:
Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove:

```rust
fn main(x : [Field]) {
Expand Down Expand Up @@ -190,7 +190,7 @@ Supported attributes include:
- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function`
- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details
- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details.
- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details
- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details

### Field Attribute

Expand Down
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 docs/docs/reference/debugger/debugger_known_limitations.md
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.
:::

Loading
Loading