Skip to main content

Debug Script

⏰ Estimated Time: 5 - 10 min
🔧 What You Will Need:
For detailed installation steps, refer to our Installation Guide

CKB-Debugger

Debugging Scripts on the Nervos CKB can be challenging without the right tools. We recommend using the CKB-Debugger, a powerful standalone command-line tool designed for off-chain Script development. With CKB-Debugger, you can efficiently identify and resolve issues in your Scripts, ensuring smooth execution and optimal performance.

Install

Install CKB-Debugger using cargo. We recommend using ≥v0.113.0.

cargo install --git https://github.com/nervosnetwork/ckb-standalone-debugger ckb-debugger

On MacOS, the protoc binary must be available to compile ckb-vm-pprof-converter. This can be installed via Homebrew:

brew install protobuf

Usage

ckb-debugger 0.113.0

USAGE:
ckb-debugger [FLAGS] [OPTIONS] --mode <mode> --tx-file <tx-file> [args]...

FLAGS:
-h, --help Prints help information
--prompt Set to true to prompt for stdin input before executing
--step Set to true to enable step mode, where we print PC address for each instruction
-V, --version Prints version information

OPTIONS:
--bin <bin> File used to replace the binary denoted in the script
-i, --cell-index <cell-index> Index of cell to run
-t, --cell-type <cell-type> Type of cell to run [possible values: input, output]
--decode <decode> Decode RISC-V instruction
--dump-file <dump-file> Dump file name
--gdb-listen <gdb-listen> Address to listen for GDB remote debugging server
--gdb-specify-depth <gdb-specify-depth> Specifies the depth of the exec/spawn stack [default: 0]
--max-cycles <max-cycles> Max cycles [default: 70000000]
--mode <mode>
Execution mode of debugger [default: full] [possible values: full, fast, gdb, probe, gdb_gdbstub]

--pprof <pprof> Performance profiling, specify output file for further use
--read-file <read-file>
Read content from local file or stdin. Then feed the content to syscall in scripts

-s, --script-group-type <script-group-type> Script group type [possible values: lock, type]
--script-hash <script-hash> Script hash
--script-version <script-version> Script version [default: 2]
--skip-end <skip-end> End address to skip printing debug info
--skip-start <skip-start> Start address to skip printing debug info
-f, --tx-file <tx-file> Filename containing JSON formatted transaction dump

ARGS:
<args>...

Examples

1. Execute Transactions Locally

To download an on-chain transaction from the network and execute it locally, use ckb-cli:

$ ckb-cli --url https://mainnet.ckbapp.dev/rpc mock-tx dump --tx-hash 0x5f0a4162622daa0e50b2cf8f49bc6ece22d1458d96fc12a094d6f074d6adbb55 --output-file mock_tx.json

Then execute the Lock Script or Type Script in the transaction:

$ ckb-debugger --tx-file mock_tx.json --cell-index 0 --cell-type input --script-group-type lock

Run result: 0
Total cycles consumed: 1697297(1.6M)
Transfer cycles: 12680(12.4K), running cycles: 1684617(1.6M)

To replace the Script in the transaction with a new version of the lock, use the --bin option:

// always_failure.c
int main() {
return 1;
}
$ ckb-debugger --tx-file mock_tx.json --cell-index 0 --cell-type input --script-group-type lock --bin always_failure

Run result: 1
Total cycles consumed: 1706(1.7K)
Transfer cycles: 764, running cycles: 942

2. Debug Failed Transactions

In most case, you want to debug failed transactions instead of successful ones to find out the reasons for the failures.

First, dump the transaction into a local file. Assume you are using the Lumos SDK to build the transaction, you can convert the txSkeleton type into a JSON file:

> let txJson = rpc.paramsFormatter.toRawTransaction(lumos.helpers.createTransactionFromSkeleton(txSkeleton))
> fs.writeFileSync('failed-tx.json', JSON.stringify(txJson, null, 2))

Next, convert the failed-tx.json to a dump transaction with context info using ckb-transaction-dumper:

$ ckb-transaction-dumper -t failed-tx.json -o full-failed-tx.json -r <mainnet/testnet/devnet RPC url>

Then, debug the Script. Remember to replace the cell-type and cell-index with the actual values in the following command:

$ ckb-debugger --tx-file full-failed-tx.json --cell-index 0 --cell-type output --script-group-type type

3. Debug Script with GDB

You can debug Scripts using GDB with ckb-debugger. Assume the Script is in fib.c:

fib.c
int fib(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fib(n-1) + fib(n-2);
}
}

int main() {
if (fib(5) != 5) {
return 1;
}
return 0;
}

After compiling fib.c to RISC-V binary fib, we can debug this Script in GDB mode:

$ ckb-debugger --mode gdb --gdb-listen 127.0.0.1:9999 --bin fib
$ riscv64-unknown-elf-gdb fib

$ (gdb) target remote 127.0.0.1:9999
$ (gdb) b fib
$ (gdb) c
Breakpoint 1, fib (n=5) at fib.c:2

At the Breakpoint 1, we see that fib (n=5) at fib.c:2.

4. Profiling Data with Flamegraph Visualization Tool

Use ckb-debugger to profile data for flamegraph visualization:

$ ckb-debugger --bin fib --pprof fib.pprof

Install inferno to conver the profile data:

$ cargo install inferno

Then, pass the file created by ckb-debugger into inferno-flamegraph:

$ cat fib.pprof | inferno-flamegraph > fib.svg

Open the resulting SVG file to view the flamegraph:

img


Additional Resources