Skip to content

Commit 8e2202c

Browse files
authored
Merge pull request #370 from walnuthq/feature/usertrace
Add usertrace command and enhance replay with debugger options
2 parents 0db3640 + 13ba0f4 commit 8e2202c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2354
-74
lines changed
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
# Stylus Debugger
2+
3+
This file explains stylus debugging capabilities via `stylusdb`.
4+
5+
## How to build it?
6+
7+
To use this, you need to install `stylusdb` tool from [stylusdb](https://github.com/walnuthq/stylusdb).
8+
9+
Install `cargo-stylus-beta` from the stylus-sdk-rs repository:
10+
11+
```bash
12+
git clone https://github.com/OffchainLabs/stylus-sdk-rs.git
13+
cd stylus-sdk-rs
14+
cargo install --path cargo-stylus
15+
```
16+
17+
Note: This will install the binary as `cargo-stylus-beta`. Use `cargo stylus-beta` in all commands below.
18+
19+
## How to run it?
20+
21+
Lets use https://github.com/OffchainLabs/stylus-hello-world as an example.
22+
23+
In one terminal, start debug node:
24+
25+
```bash
26+
docker run -it --rm --name nitro-dev -p 8547:8547 offchainlabs/nitro-node:v3.5.3-rc.3-653b078 --dev --http.addr 0.0.0.0 --http.api=net,web3,eth,arb,arbdebug,debug
27+
```
28+
29+
In another terminal, compile and deploy the example:
30+
31+
```bash
32+
git clone https://github.com/OffchainLabs/stylus-hello-world
33+
cd stylus-hello-world
34+
export RPC_URL=http://localhost:8547
35+
export PRIV_KEY=0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659
36+
cargo stylus-beta deploy --private-key=$PRIV_KEY --endpoint=$RPC_URL
37+
```
38+
39+
and you can expect the output like this
40+
```text
41+
...
42+
deployed code at address: 0xda52b25ddb0e3b9cc393b0690ac62245ac772527
43+
deployment tx hash: 0x307b1d712840327349d561dea948d957362d5d807a1dfa87413023159cbb23f2
44+
wasm already activated!
45+
46+
NOTE: We recommend running cargo stylus-beta cache bid da52b25ddb0e3b9cc393b0690ac62245ac772527 0 to cache your activated contract in ArbOS.
47+
Cached contracts benefit from cheaper calls. To read more about the Stylus contract cache, see
48+
https://docs.arbitrum.io/stylus/concepts/stylus-cache-manager
49+
$ export ADDR=0xda52b25ddb0e3b9cc393b0690ac62245ac772527
50+
$ cast send --rpc-url=$RPC_URL --private-key=$PRIV_KEY $ADDR "increment()"
51+
blockHash 0x3f6bea10728836b1f2c37e2aff3b69b1a7175b7607c8dc9df93aa3b4911536ed
52+
blockNumber 5
53+
contractAddress
54+
cumulativeGasUsed 992585
55+
effectiveGasPrice 100000000
56+
from 0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E
57+
gasUsed 992585
58+
logs []
59+
logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
60+
root
61+
status 1 (success)
62+
transactionHash 0x88b0ad9daa0b701d868a5f9a0132db7c0402178ba44ed8dec4ba76784c7194fd
63+
transactionIndex 1
64+
type 2
65+
blobGasPrice
66+
blobGasUsed
67+
authorizationList
68+
to 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF
69+
gasUsedForL1 936000
70+
l1BlockNumber 0
71+
timeboosted false
72+
```
73+
74+
### Run `replay` command
75+
76+
This is the way of using existing `replay` option, that will attach to either `lldb` or `gdb`:
77+
78+
```bash
79+
cargo stylus-beta replay \
80+
--tx=0x88b0ad9daa0b701d868a5f9a0132db7c0402178ba44ed8dec4ba76784c7194fd \
81+
--endpoint=$RPC_URL
82+
1 location added to breakpoint 1
83+
warning: This version of LLDB has no plugin for the language "rust". Inspection of frame variables will be limited.
84+
Process 9256 stopped
85+
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
86+
frame #0: 0x000000010102cbb8 libstylus_hello_world.dylib`user_entrypoint(len=4) at lib.rs:33:5
87+
30 // Define some persistent storage using the Solidity ABI.
88+
31 // `Counter` will be the entrypoint.
89+
32 sol_storage! {
90+
-> 33 #[entrypoint]
91+
34 pub struct Counter {
92+
35 uint256 number;
93+
36 }
94+
Target 0: (cargo-stylus) stopped.
95+
Process 9256 launched: '~/.cargo/bin/cargo-stylus' (arm64)
96+
(lldb) c
97+
Process 9256 resuming
98+
call completed successfully
99+
Process 9256 exited with status = 0 (0x00000000)
100+
(lldb) q
101+
```
102+
103+
### Run `usertrace` command
104+
105+
We have introduced a new `cargo` option called `usertrace`, that uses similar technology as `replay` option, but it rather attaches to `stylusdb`, instead of well known debuggers.
106+
107+
First, make sure you installed `colorama` package:
108+
109+
```bash
110+
$ python3 -m venv myvenv
111+
$ source ./myvenv/bin/activate
112+
(myvenv) $ pip3 install colorama
113+
```
114+
115+
We have introduced a new `cargo` option called `usertrace`, that uses similar technology as `replay` option, but it rather attaches to `stylusdb`, instead of well known debuggers.
116+
117+
``` bash
118+
$ cargo stylus-beta usertrace \
119+
--tx=0x88b0ad9daa0b701d868a5f9a0132db7c0402178ba44ed8dec4ba76784c7194fd \
120+
--endpoint=$RPC_URL
121+
=== STYLUS FUNCTION CALL TREE ===
122+
└─ #1 stylus_hello_world::__stylus_struct_entrypoint::h09ecd85e5c55b994 (lib.rs:33)
123+
input = size=4
124+
<anon> = stylus_sdk::host::VM { 0=<unavailable> }
125+
└─ #2 stylus_hello_world::Counter::increment::h5b9fb276c23de4f4 (lib.rs:64)
126+
self = 0x000000016fdeaa78
127+
└─ #3 stylus_hello_world::Counter::set_number::h5bd2c4836637ecb9 (lib.rs:49)
128+
self = 0x000000016fdeaa78
129+
new_number = ruint::Uint<256, 4> { limbs=unsigned long[4] { [0]=1, [1]=0, [2]=0, [3]=0 } }
130+
```
131+
132+
In your terminal, it will look as:
133+
134+
<img width="699" alt="Screenshot 2025-04-14 at 13 09 47" src="https://github.com/user-attachments/assets/45ea3aaa-afa7-48fe-a832-7bf878903a6b" />
135+
136+
You may see the calltrace in form of JSON in:
137+
138+
```
139+
/tmp/lldb_function_trace.json
140+
```
141+
142+
By default, it does not follow functions from `stylus_sdk::`, if you want to see those, use `--verbose-usertrace` option, e.g.:
143+
144+
```bash
145+
$ cargo stylus-beta usertrace \
146+
--tx=0x88b0ad9daa0b701d868a5f9a0132db7c0402178ba44ed8dec4ba76784c7194fd \
147+
--endpoint=$RPC_URL --verbose-usertrace
148+
```
149+
150+
Or, if you want to track calls from other libraries, just use `--trace-external-usertrace` as follows:
151+
152+
```bash
153+
cargo stylus-beta usertrace \
154+
--tx=0x88b0ad9daa0b701d868a5f9a0132db7c0402178ba44ed8dec4ba76784c7194fd \
155+
--endpoint=$RPC_URL --verbose-usertrace --trace-external-usertrace="std,core,other_contract"
156+
```
157+
158+
and it will track calls from `std::`, `core` and `other_contract::`.
159+
160+
### Run `replay` option with `stylusdb`
161+
162+
To use `stylusdb`, specify `--debugger stylusdb`.
163+
164+
```bash
165+
$ cargo stylus-beta replay --debugger stylusdb --tx <TX_HASH> [other args]
166+
```
167+
168+
If you want to debug multi-contract transaction, use:
169+
170+
```bash
171+
$ cargo stylus-beta replay --debugger stylusdb --tx <TX_HASH> \
172+
--contracts ADDR:PATH,0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e:../ \
173+
--endpoint=$RPC_URL
174+
...
175+
(stylusdb) stylus-contract breakpoint 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e external_contract::ServiceContract::increment
176+
Set breakpoint on external_contract::ServiceContract::increment in contract 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e (ID: 3, 1 locations)
177+
...
178+
```
179+
180+
### Setting Breakpoints in StylusDB
181+
182+
StylusDB provides the `stylus-contract` command for managing breakpoints in multi-contract debugging sessions. Here are the available commands and examples:
183+
184+
#### Adding Contracts
185+
186+
Before setting breakpoints, you need to add the contract to the debugger:
187+
188+
```bash
189+
(stylusdb) stylus-contract add 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF ./target/debug/libmy_contract.dylib
190+
Added contract 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF with library ./target/debug/libmy_contract.dylib
191+
```
192+
193+
#### Setting Breakpoints
194+
195+
Set breakpoints on specific functions within a contract:
196+
197+
```bash
198+
# Break on a specific function
199+
(stylusdb) stylus-contract breakpoint 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF stylus_hello_world::Counter::increment
200+
Set breakpoint on stylus_hello_world::Counter::increment in contract 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF (ID: 1, 1 locations)
201+
202+
# Break on the entrypoint
203+
(stylusdb) stylus-contract breakpoint 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF user_entrypoint
204+
Set breakpoint on user_entrypoint in contract 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF (ID: 2, 1 locations)
205+
206+
# Break on internal functions
207+
(stylusdb) stylus-contract breakpoint 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF stylus_hello_world::Counter::set_number
208+
Set breakpoint on stylus_hello_world::Counter::set_number in contract 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF (ID: 3, 1 locations)
209+
```
210+
211+
#### Other Useful Commands
212+
213+
```bash
214+
# List all registered contracts
215+
(stylusdb) stylus-contract list
216+
Registered contracts:
217+
0xA6E41fFD769491a42A6e5Ce453259b93983a22EF -> ./target/debug/libmy_contract.dylib (3 breakpoints)
218+
0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e -> ../external/target/debug/libexternal.dylib (1 breakpoint)
219+
220+
# Show the current call stack
221+
(stylusdb) stylus-contract stack
222+
Call stack: main -> 0xA6E41fFD769491a42A6e5Ce453259b93983a22EF -> 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e
223+
224+
# Switch debugging context to a specific contract
225+
(stylusdb) stylus-contract context 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e
226+
Switched context to contract 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e
227+
Module: ../external/target/debug/libexternal.dylib
228+
229+
# Show current context
230+
(stylusdb) stylus-contract context show
231+
Current context: 0xe1080224B632A93951A7CFA33EeEa9Fd81558b5e
232+
```
233+
234+
#### Standard LLDB Commands
235+
236+
In addition to `stylus-contract` commands, you can use standard LLDB commands:
237+
238+
```bash
239+
# Continue execution
240+
(stylusdb) c
241+
242+
# Step over
243+
(stylusdb) n
244+
245+
# Step into
246+
(stylusdb) s
247+
248+
# Print variable
249+
(stylusdb) p number
250+
251+
# Print with formatting (for Rust types)
252+
(stylusdb) expr -f hex -- number.limbs[0]
253+
254+
# Show backtrace
255+
(stylusdb) bt
256+
257+
# List breakpoints
258+
(stylusdb) breakpoint list
259+
260+
# Delete a breakpoint
261+
(stylusdb) breakpoint delete 1
262+
263+
# Quit debugger
264+
(stylusdb) q
265+
```
266+
267+
### Debugging Transactions with Solidity Contract Calls
268+
269+
When debugging transactions that involve calls from Stylus to Solidity contracts, you can use the `--addr-solidity` flag to mark specific addresses as Solidity contracts:
270+
271+
```bash
272+
$ cargo stylus-beta replay --debugger stylusdb --tx <TX_HASH> \
273+
--addr-solidity=0xda52b25ddb0e3b9cc393b0690ac62245ac772527 \
274+
--endpoint=$RPC_URL
275+
```
276+
277+
When the debugger encounters a call to a Solidity contract, it will display:
278+
279+
```
280+
════════ Solidity Contract Call ════════
281+
Contract: 0xda52b25ddb0e3b9cc393b0690ac62245ac772527
282+
Function selector: 0xd09de08a (increment())
283+
NOTE: This is a Solidity contract - skipping to next contract
284+
```
285+
286+
The debugger will:
287+
- Show the Solidity contract address
288+
- Display the function selector (first 4 bytes of calldata)
289+
- Attempt to decode the function name using 4byte.directory (if available)
290+
- Continue execution after the Solidity call returns
291+
292+
This allows you to trace execution flow across mixed Stylus/Solidity transactions, even though source-level debugging is only available for Stylus contracts.
293+
294+
#### Important Note on Contract Paths
295+
296+
When specifying contracts with the `--contracts` flag, you can only provide directory paths for Rust/Stylus contracts. Solidity contracts do not support full debugging and cannot be built from source directories.
297+
298+
**Incorrect usage (will fail):**
299+
```bash
300+
# This will error because ../erc20 is a Solidity contract directory
301+
cargo stylus-beta replay --debugger stylusdb \
302+
--tx=0xb590941f5de2a2164b76143ef4ca9d27df2d7c718c058fd2bbef4ac56b72d149 \
303+
--contracts 0xa6e41ffd769491a42a6e5ce453259b93983a22ef:.,0x1294b86822ff4976BfE136cB06CF43eC7FCF2574:../erc20
304+
```
305+
306+
This will produce an error:
307+
```
308+
error: could not find `Cargo.toml` in `/path/to/erc20` or any parent directory
309+
Error: failed to replay tx
310+
Caused by:
311+
failed to open ../erc20/target/aarch64-apple-darwin/debug/: No such file or directory
312+
```
313+
314+
**Correct usage:**
315+
```bash
316+
# Only specify the Solidity contract address without a path
317+
cargo stylus-beta replay --debugger stylusdb \
318+
--tx=0xb590941f5de2a2164b76143ef4ca9d27df2d7c718c058fd2bbef4ac56b72d149 \
319+
--contracts 0xa6e41ffd769491a42a6e5ce453259b93983a22ef:.,0x1294b86822ff4976BfE136cB06CF43eC7FCF2574
320+
```
321+
322+
For Solidity contracts:
323+
- Only provide the contract address (no `:path` suffix)
324+
- The debugger will show the contract address and function selectors
325+
- Source-level debugging is not available
326+
- Execution will continue after Solidity calls return

0 commit comments

Comments
 (0)