Skip to content

Commit 13ba0f4

Browse files
committed
Add usertrace command and enhance replay with debugger options
This commit adds the usertrace command for tracing Stylus contracts at the user function level, and enhances the replay command with: - Configurable debugger selection (gdb, lldb, stylusdb, auto) - Support for multi-contract debugging via --contracts option - Documentation for stylusdb debugger usage - Initial support for calling Solidity contracts - Test infrastructure for replay and usertrace functionality The usertrace command is a placeholder that will be fully implemented to capture and visualize user function calls using stylusdb.
1 parent 49b065b commit 13ba0f4

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)