Replaying Transactions
The thru-replay tool can be used to replay transactions run on Thru,
and supports debugging with GDB. This can help you debug crashes and other
issues in your program.
To make it easier to debug programs, you should build your program with debug
information.
In C and C++, you can do this with the SDK_EXTRAS environment variable: In Rust, build the program in debug mode:
In addition to the .bin file, you’ll find a file at target/thruvm/bin/<program-name>.elf that
contains the program’s debug information. Make a note of this file for later use.
After this, upload your program in debug mode to the network, and run a transaction.
Note that this step is optional, and may not be possible if you are debugging a
transaction that has already been run. However, it makes it significantly easier to debug
your program.
Step 2: Prepare the command to run with GDB
Next, you’ll need to gather all the information to replay your program. You’ll need:
- The transaction signature. It will look something like this:
tsX2EQZzPxVgI23KmdAByeVj-yZ9JE-46q_AhjURj0vRnHdMRP3at6H_dVXDFdUtUwnW_ICnLnk0ROJWWdTIWbDh1P
- For each program in your transaction, you’ll need the program’s address as well as the path
to the program’s debug information. For instance:
tafhdJHH_OVQYN4-Cimt7Y1XclSytf9Ox_iuetV8CKmz=./build/thruvm/bin/tn_token_program_rust.elf
If you don’t have the debug information for a program, you can skip it. However, you won’t
be able to view the source information for that program.
Step 3: Run the command
Now you can run the command to replay the transaction with GDB. You’ll need to pass the transaction
signature, and for each program in your transaction, you’ll need to pass the program’s address and the
path to the program’s debug information.
thru-replay --gdb \
--signature $transaction-signature \
--elf $program-address-1=$program-debug-info-path-1 \
--elf $program-address-2=$program-debug-info-path-2 ...
For example:
thru-replay --gdb \
--signature tsX2EQZzPxVgI23KmdAByeVj-yZ9JE-46q_AhjURj0vRnHdMRP3at6H_dVXDFdUtUwnW_ICnLnk0ROJWWdTIWbDh1P \
--elf tafhdJHH_OVQYN4-Cimt7Y1XclSytf9Ox_iuetV8CKmz=./build/thruvm/bin/tn_token_program_rust.elf
You should see some output like this. You’ll be in an interactive GDB session.
Fetching transaction tsCRsKwMAupg5jUbus7GSeN8XgUS_lafAIo6YPUCfJW_zIYTgNJ5xCufxghWfiL1LGAPBWsCWHFHa0srws70WEAx38...
Fetching 3 account states at slot 66 (pre-transaction)...
Fetching state roots ending at slot 66...
--log-path not specified; using autogenerated path
Log at "/tmp/fd-0.0.0_3872496_user_machine-1_2025_12_16_17_38_45_883488412_GMT+00"
Note: RW account ta1kDh1kmoJuRkoBChLLRk-ZOvzAHFNWNrK1iDCBJToaxR not in pre-tx state - will be created by transaction
Starting debug server on port 9001...
GDB connected!
add symbol table from file "./build/thruvm/bin/tn_token_program_rust.elf" at
.text_addr = 0x3000000
Reading symbols from ./build/thruvm/bin/tn_token_program_rust.elf...
add symbol table from file "./build/thruvm/bin/tn_token_program_rust.elf" at
.text_addr = 0x30001000000
Reading symbols from ./build/thruvm/bin/tn_token_program_rust.elf...
Remote debugging using localhost:9001
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0000030001000000 in _start ()
(gdb)
You can debug the program as you would normally do with GDB:
(gdb) break start
Breakpoint 1 at 0x300006c: start. (2 locations)
(gdb) c
Continuing.
Breakpoint 1.2, token_program::start (instr_data=0x0 $core::intrinsics::cold_path, instr_data_sz=0)
at examples/token_program/main.rs:20
20 #[entry(stack_size = 8192)]