What is USDT Tracing?
USDT tracing allows you to:- Hook into specific points in Bitcoin Core’s execution
- Collect custom statistics and metrics
- Monitor hidden internals during runtime
- Debug performance issues and behavior
- Track network messages, block connections, mempool activity, and more
How USDT and eBPF Work
When to Use USDT Tracing
Tracing is useful for:- Performance Analysis: Identify bottlenecks in block processing, network I/O, or validation
- Network Monitoring: Track P2P message patterns and peer behavior
- Debugging: Investigate unexpected behavior or bugs
- Research: Study Bitcoin Core internals and behavior patterns
- Production Monitoring: Collect custom metrics without modifying code
- Security Analysis: Monitor for unusual patterns or attacks
USDT tracing is a Linux-specific feature. It requires a Linux system with eBPF support (kernel 4.1+, recommended 5.2+).
Tools for USDT Tracing
Two main eBPF front-ends support USDT:bpftrace
Best for one-liners and short scripts:BPF Compiler Collection (BCC)
Best for complex tools and daemons. Uses Python for scripting with C for eBPF programs. Examples for both tools are available in thecontrib/tracing/ directory of Bitcoin Core.
Available Tracepoints
Bitcoin Core includes tracepoints across several contexts:Network Tracepoints (net context)
net:inbound_message
Called when a message is received from a peer.
Arguments:
- Peer ID (
int64) - Peer address and port (
string, up to 68 chars) - Connection type (
string, max 20 chars: “inbound”, “outbound-full-relay”, etc.) - Message type (
string, max 20 chars: “inv”, “ping”, “getdata”, etc.) - Message size in bytes (
uint64) - Message bytes (
pointer to bytes)
net:outbound_message
Called when a message is sent to a peer. Same arguments as inbound_message.
net:inbound_connection
Called when a new inbound connection is opened.
Arguments:
- Peer ID (
int64) - Peer address and port (
string) - Connection type (
string) - Network type (
uint32: 1=IPv4, 2=IPv6, 3=Onion, 4=I2P, 5=CJDNS) - Total inbound connections (
uint64)
net:outbound_connection
Called when a new outbound connection is opened. Similar arguments to inbound_connection.
net:evicted_inbound_connection
Called when an inbound connection is evicted.
net:misbehaving_connection
Called when a connection is misbehaving.
Arguments:
- Peer ID (
int64) - Reason (
string, max 128 chars)
net:closed_connection
Called when a connection is closed.
Validation Tracepoints (validation context)
validation:block_connected
Called after a block is connected to the chain.
Arguments:
- Block header hash (
32 bytes, little-endian) - Block height (
int32) - Transactions in block (
uint64) - Inputs spent (
int32) - SigOps in block (
uint64) - Connection time in nanoseconds (
uint64)
UTXO Cache Tracepoints (utxocache context)
utxocache:flush
Called after UTXO cache is flushed.
Arguments:
- Flush duration in microseconds (
int64) - Flush mode (
uint32: 0=NONE, 1=IF_NEEDED, 2=PERIODIC, 3=FORCE_FLUSH, 4=FORCE_SYNC) - Cache size before flush (
uint64) - Cache memory usage in bytes (
uint64) - Pruning caused flush (
bool)
utxocache:add
Called when a coin is added to UTXO cache.
Arguments:
- Transaction ID (
32 bytes) - Output index (
uint32) - Block height (
uint32) - Coin value (
int64) - Is coinbase (
bool)
utxocache:spent
Called when a coin is spent. Similar arguments to add.
utxocache:uncache
Called when a coin is unloaded from cache (e.g., invalid transaction).
Mempool Tracepoints (mempool context)
mempool:added
Called when a transaction is added to mempool.
Arguments:
- Transaction ID (
32 bytes) - Virtual size (
int32) - Fee (
int64)
mempool:removed
Called when a transaction is removed from mempool.
Arguments:
- Transaction ID (
32 bytes) - Removal reason (
string, max 9 chars) - Virtual size (
int32) - Fee (
int64) - Entry time epoch (
uint64)
mempool:replaced
Called when a transaction is replaced (RBF).
mempool:rejected
Called when a transaction is rejected.
Arguments:
- Transaction ID (
32 bytes) - Reject reason (
string, max 118 chars)
Coin Selection Tracepoints (coin_selection context)
coin_selection:selected_coins
Called when coin selection completes.
Arguments:
- Wallet name (
string) - Algorithm name (
string) - Target value (
int64) - Waste metric (
int64) - Total selected value (
int64)
Listing Available Tracepoints
Several methods to list tracepoints in your Bitcoin Core binary:Using GDB
Using readelf
Using tplist (from BCC)
Example: Monitor Inbound Messages
Using bpftrace
Using BCC (Python)
Example: Benchmark Block Connections
-reindex to benchmark historical block processing.
Example: Monitor Mempool Activity
Important Limitations
eBPF Stack Size
The eBPF VM has a limited stack size of 512 bytes. Large data structures may not fit.bpftrace Argument Limit
On x86_64, bpftrace can only access the first 6 arguments (arg0 through arg5). If a tracepoint has more arguments, BCC can access all 12.
Message Size Limits
Network message tracepoints pass full messages, but the eBPF VM may truncate messages larger than 32 KB.Adding Custom Tracepoints
Developers can add new tracepoints to Bitcoin Core:Best Practices for Custom Tracepoints
- Clear Use Case: Define specific monitoring or debugging need
- Provide Examples: Include example scripts in
contrib/tracing/ - Semi-Stable API: Keep argument order stable for scripting
- Document Thoroughly: Add to tracepoint documentation
- Consider Limits: Respect eBPF VM stack size (512 bytes)
- String Arguments: Pass as C-style strings with documented max length
- Expensive Arguments: Gate with
TRACEPOINT_ACTIVE()check
Resources
Security Considerations
- Tracing requires root privileges (sudo)
- eBPF programs run in kernel space
- Only load trusted tracing scripts
- Be cautious with production systems
- Test scripts thoroughly before production use