Skip to content

Commit 8f37f5c

Browse files
committed
tracing: Tracepoint for connected blocks
Can, for example, be used to benchmark block connections.
1 parent 4224dec commit 8f37f5c

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

contrib/tracing/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,79 @@ Warning: incomplete message (only 32568 out of 53552 bytes)! inbound msg 'tx' fr
163163
164164
Possibly lost 2 samples
165165
```
166+
167+
### connectblock_benchmark.bt
168+
169+
A `bpftrace` script to benchmark the `ConnectBlock()` function during, for
170+
example, a blockchain re-index. Based on the `validation:block_connected` USDT
171+
tracepoint.
172+
173+
The script takes three positional arguments. The first two arguments, the start,
174+
and end height indicate between which blocks the benchmark should be run. The
175+
third acts as a duration threshold in milliseconds. When the `ConnectBlock()`
176+
function takes longer than the threshold, information about the block, is
177+
printed. For more details, see the header comment in the script.
178+
179+
By default, `bpftrace` limits strings to 64 bytes due to the limited stack size
180+
in the kernel VM. Block hashes as zero-terminated hex strings are 65 bytes which
181+
exceed the string limit. The string size limit can be set to 65 bytes with the
182+
environment variable `BPFTRACE_STRLEN`.
183+
184+
The following command can be used to benchmark, for example, `ConnectBlock()`
185+
between height 20000 and 38000 on SigNet while logging all blocks that take
186+
longer than 25ms to connect.
187+
188+
```
189+
$ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 20000 38000 25
190+
```
191+
192+
In a different terminal, starting Bitcoin Core in SigNet mode and with
193+
re-indexing enabled.
194+
195+
```
196+
$ ./src/bitcoind -signet -reindex
197+
```
198+
199+
This produces the following output.
200+
```
201+
Attaching 5 probes...
202+
ConnectBlock Benchmark between height 20000 and 38000 inclusive
203+
Logging blocks taking longer than 25 ms to connect.
204+
Starting Connect Block Benchmark between height 20000 and 38000.
205+
BENCH 39 blk/s 59 tx/s 59 inputs/s 20 sigops/s (height 20038)
206+
Block 20492 (000000f555653bb05e2f3c6e79925e01a20dd57033f4dc7c354b46e34735d32b) 20 tx 2319 ins 2318 sigops took 38 ms
207+
BENCH 1840 blk/s 2117 tx/s 4478 inputs/s 2471 sigops/s (height 21879)
208+
BENCH 1816 blk/s 4972 tx/s 4982 inputs/s 125 sigops/s (height 23695)
209+
BENCH 2095 blk/s 2890 tx/s 2910 inputs/s 152 sigops/s (height 25790)
210+
BENCH 1684 blk/s 3979 tx/s 4053 inputs/s 288 sigops/s (height 27474)
211+
BENCH 1155 blk/s 3216 tx/s 3252 inputs/s 115 sigops/s (height 28629)
212+
BENCH 1797 blk/s 2488 tx/s 2503 inputs/s 111 sigops/s (height 30426)
213+
BENCH 1849 blk/s 6318 tx/s 6569 inputs/s 12189 sigops/s (height 32275)
214+
BENCH 946 blk/s 20209 tx/s 20775 inputs/s 83809 sigops/s (height 33221)
215+
Block 33406 (0000002adfe4a15cfcd53bd890a89bbae836e5bb7f38bac566f61ad4548c87f6) 25 tx 2045 ins 2090 sigops took 29 ms
216+
Block 33687 (00000073231307a9828e5607ceb8156b402efe56747271a4442e75eb5b77cd36) 52 tx 1797 ins 1826 sigops took 26 ms
217+
BENCH 582 blk/s 21581 tx/s 27673 inputs/s 60345 sigops/s (height 33803)
218+
BENCH 1035 blk/s 19735 tx/s 19776 inputs/s 51355 sigops/s (height 34838)
219+
Block 35625 (0000006b00b347390c4768ea9df2655e9ff4b120f29d78594a2a702f8a02c997) 20 tx 3374 ins 3371 sigops took 49 ms
220+
BENCH 887 blk/s 17857 tx/s 22191 inputs/s 24404 sigops/s (height 35725)
221+
Block 35937 (000000d816d13d6e39b471cd4368db60463a764ba1f29168606b04a22b81ea57) 75 tx 3943 ins 3940 sigops took 61 ms
222+
BENCH 823 blk/s 16298 tx/s 21031 inputs/s 18440 sigops/s (height 36548)
223+
Block 36583 (000000c3e260556dbf42968aae3f904dba8b8c1ff96a6f6e3aa5365d2e3ad317) 24 tx 2198 ins 2194 sigops took 34 ms
224+
Block 36700 (000000b3b173de9e65a3cfa738d976af6347aaf83fa17ab3f2a4d2ede3ddfac4) 73 tx 1615 ins 1611 sigops took 31 ms
225+
Block 36832 (0000007859578c02c1ac37dabd1b9ec19b98f350b56935f5dd3a41e9f79f836e) 34 tx 1440 ins 1436 sigops took 26 ms
226+
BENCH 613 blk/s 16718 tx/s 25074 inputs/s 23022 sigops/s (height 37161)
227+
Block 37870 (000000f5c1086291ba2d943fb0c3bc82e71c5ee341ee117681d1456fbf6c6c38) 25 tx 1517 ins 1514 sigops took 29 ms
228+
BENCH 811 blk/s 16031 tx/s 20921 inputs/s 18696 sigops/s (height 37972)
229+
230+
Took 14055 ms to connect the blocks between height 20000 and 38000.
231+
232+
Histogram of block connection times in milliseconds (ms).
233+
@durations:
234+
[0] 16838 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
235+
[1] 882 |@@ |
236+
[2, 4) 236 | |
237+
[4, 8) 23 | |
238+
[8, 16) 9 | |
239+
[16, 32) 9 | |
240+
[32, 64) 4 | |
241+
```
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/env bpftrace
2+
3+
/*
4+
5+
USAGE:
6+
7+
BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt <start height> <end height> <logging threshold in ms>
8+
9+
- The environment variable BPFTRACE_STRLEN needs to be set to 65 chars as
10+
strings are limited to 64 chars by default. Hex strings with Bitcoin block
11+
hashes are 64 hex chars + 1 null-termination char.
12+
- <start height> sets the height at which the benchmark should start. Setting
13+
the start height to 0 starts the benchmark immediately, even before the
14+
first block is connected.
15+
- <end height> sets the height after which the benchmark should end. Setting
16+
the end height to 0 disables the benchmark. The script only logs blocks
17+
over <logging threshold in ms>.
18+
- Threshold <logging threshold in ms>
19+
20+
This script requires a 'bitcoind' binary compiled with eBPF support and the
21+
'validation:block_connected' USDT. By default, it's assumed that 'bitcoind' is
22+
located in './src/bitcoind'. This can be modified in the script below.
23+
24+
EXAMPLES:
25+
26+
BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 300000 680000 1000
27+
28+
When run together 'bitcoind -reindex', this benchmarks the time it takes to
29+
connect the blocks between height 300.000 and 680.000 (inclusive) and prints
30+
details about all blocks that take longer than 1000ms to connect. Prints a
31+
histogram with block connection times when the benchmark is finished.
32+
33+
34+
BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 0 0 500
35+
36+
When running together 'bitcoind', all newly connected blocks that
37+
take longer than 500ms to connect are logged. A histogram with block
38+
connection times is shown when the script is terminated.
39+
40+
*/
41+
42+
BEGIN
43+
{
44+
$start_height = $1;
45+
$end_height = $2;
46+
$logging_threshold_ms = $3;
47+
48+
if ($end_height < $start_height) {
49+
printf("Error: start height (%d) larger than end height (%d)!\n", $start_height, $end_height);
50+
exit();
51+
}
52+
53+
if ($end_height > 0) {
54+
printf("ConnectBlock benchmark between height %d and %d inclusive\n", $start_height, $end_height);
55+
} else {
56+
printf("ConnectBlock logging starting at height %d\n", $start_height);
57+
}
58+
59+
if ($logging_threshold_ms > 0) {
60+
printf("Logging blocks taking longer than %d ms to connect.\n", $3);
61+
}
62+
63+
if ($start_height == 0) {
64+
@start = nsecs;
65+
}
66+
}
67+
68+
/*
69+
Attaches to the 'validation:block_connected' USDT and collects stats when the
70+
connected block is between the start and end height (or the end height is
71+
unset).
72+
*/
73+
usdt:./src/bitcoind:validation:block_connected /arg1 >= $1 && (arg1 <= $2 || $2 == 0 )/
74+
{
75+
$height = arg1;
76+
$transactions = arg2;
77+
$inputs = arg3;
78+
$sigops = arg4;
79+
$duration = (uint64) arg5;
80+
81+
@height = $height;
82+
83+
@blocks = @blocks + 1;
84+
@transactions = @transactions + $transactions;
85+
@inputs = @inputs + $inputs;
86+
@sigops = @sigops + $sigops;
87+
88+
@durations = hist($duration / 1000);
89+
90+
if ($height == $1 && $height != 0) {
91+
@start = nsecs;
92+
printf("Starting Connect Block Benchmark between height %d and %d.\n", $1, $2);
93+
}
94+
95+
if ($2 > 0 && $height >= $2) {
96+
@end = nsecs;
97+
$duration = @end - @start;
98+
printf("\nTook %d ms to connect the blocks between height %d and %d.\n", $duration / 1000000, $1, $2);
99+
exit();
100+
}
101+
}
102+
103+
/*
104+
Attaches to the 'validation:block_connected' USDT and logs information about
105+
blocks where the time it took to connect the block is above the
106+
<logging threshold in ms>.
107+
*/
108+
usdt:./src/bitcoind:validation:block_connected / (uint64) arg5 / 1000> $3 /
109+
{
110+
$hash_str = str(arg0);
111+
$height = (int32) arg1;
112+
$transactions = (uint64) arg2;
113+
$inputs = (int32) arg3;
114+
$sigops = (int64) arg4;
115+
$duration = (int64) arg5;
116+
117+
printf("Block %d (%s) %4d tx %5d ins %5d sigops took %4d ms\n", $height, $hash_str, $transactions, $inputs, $sigops, (uint64) $duration / 1000);
118+
}
119+
120+
121+
/*
122+
Prints stats about the blocks, transactions, inputs, and sigops processed in
123+
the last second (if any).
124+
*/
125+
interval:s:1 {
126+
if (@blocks > 0) {
127+
printf("BENCH %4d blk/s %6d tx/s %7d inputs/s %8d sigops/s (height %d)\n", @blocks, @transactions, @inputs, @sigops, @height);
128+
129+
zero(@blocks);
130+
zero(@transactions);
131+
zero(@inputs);
132+
zero(@sigops);
133+
}
134+
}
135+
136+
END
137+
{
138+
printf("\nHistogram of block connection times in milliseconds (ms).\n");
139+
print(@durations);
140+
141+
clear(@durations);
142+
clear(@blocks);
143+
clear(@transactions);
144+
clear(@inputs);
145+
clear(@sigops);
146+
clear(@height);
147+
clear(@start);
148+
clear(@end);
149+
}
150+

doc/tracing.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@ to user-space in full. Messages longer than a 32kb might be cut off. This can
9393
be detected in tracing scripts by comparing the message size to the length of
9494
the passed message.
9595

96+
### Context `validation`
97+
98+
#### Tracepoint `validation:block_connected`
99+
100+
Is called *after* a block is connected to the chain. Can, for example, be used
101+
to benchmark block connections together with `-reindex`.
102+
103+
Arguments passed:
104+
1. Block Header Hash as `pointer to C-style String` (64 characters)
105+
2. Block Height as `int32`
106+
3. Transactions in the Block as `uint64`
107+
4. Inputs spend in the Block as `int32`
108+
5. SigOps in the Block (excluding coinbase SigOps) `uint64`
109+
6. Time it took to connect the Block in microseconds (µs) as `uint64`
110+
7. Block Header Hash as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
111+
112+
Note: The 7th argument can't be accessed by bpftrace and is purposefully chosen
113+
to be the block header hash as bytes. See [bpftrace argument limit] for more
114+
details.
115+
116+
[bpftrace argument limit]: #bpftrace-argument-limit
117+
96118
## Adding tracepoints to Bitcoin Core
97119

98120
To add a new tracepoint, `#include <util/trace.h>` in the compilation unit where

src/validation.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <util/rbf.h>
4949
#include <util/strencodings.h>
5050
#include <util/system.h>
51+
#include <util/trace.h>
5152
#include <util/translation.h>
5253
#include <validationinterface.h>
5354
#include <warnings.h>
@@ -1997,6 +1998,16 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
19971998
int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
19981999
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal);
19992000

2001+
TRACE7(validation, block_connected,
2002+
block.GetHash().ToString().c_str(),
2003+
pindex->nHeight,
2004+
block.vtx.size(),
2005+
nInputs,
2006+
nSigOpsCost,
2007+
GetTimeMicros() - nTimeStart, // in microseconds (µs)
2008+
block.GetHash().data()
2009+
);
2010+
20002011
return true;
20012012
}
20022013

0 commit comments

Comments
 (0)