New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tracing: reduce instructions generated by net:message tracepoints #23809
Conversation
|
b559e63
to
50a6826
Compare
In the net:{inbound,outbound}_message tracepoint, we are calling the ConnectionTypeAsString helper function that converts the enum to a string. This has been shown to generate quite a few instructions, even when there is no code hooked into the tracepoint. Here's the output from gdb when tracing the number of instructions between two breakpoints around this tracepoint (see [1] for how to do this): Recorded 293 instructions in 27 functions Tracepoints should be almost zero cost, 293 instructions is a bit much for effectively doing nothing most of the time. Instead of calling the ConnectionTypeAsString function, simply add a ConnectionType getter that returns the connection type enum directly. This way the tracepoint simplifies to nine instructions that load values into registers in preparation for a tracepoint call, which may or may not be there depending on if there are any attached ebpf programs. With this patch applied: Recorded 9 instructions in 1 functions This tightens up these tracepoints in preparation for enabling tracing in release builds[2]. Signed-off-by: William Casarin <jb55@jb55.com> [1] bitcoin#23724 (comment) [2] bitcoin#23724
50a6826
to
ca7a124
Compare
Concept and tested ACK (modulo ❌). This is a important step for #23724. One possible problem I see with this is that some tracing script will get confused when we remove and then add a new connection type: e.g. the enum with the value 5 has a new meaning. Tracepoint documentation would be outdated too.
[0] Line 120 in 98a2ddc
[1] I didn't find any other way of doing this in bpftrace. Would be nice if the if-else could at least be extracted into a function.. diff --git a/contrib/tracing/log_p2p_traffic.bt b/contrib/tracing/log_p2p_traffic.bt
index f62956aa5..f038ae1b2 100755
--- a/contrib/tracing/log_p2p_traffic.bt
+++ b/contrib/tracing/log_p2p_traffic.bt
@@ -9,20 +9,51 @@ usdt:./src/bitcoind:net:inbound_message
{
$peer_id = (int64) arg0;
$peer_addr = str(arg1);
- $peer_type = str(arg2);
+ $peer_type = (int32) arg2;
$msg_type = str(arg3);
$msg_len = arg4;
- printf("inbound '%s' msg from peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type, $peer_addr, $msg_len);
+
+ $peer_type_str = "unknown";
+ if ($peer_type == 0) {
+ $peer_type_str = "inbound";
+ } else if ($peer_type == 1) {
+ $peer_type_str = "outbound-full-relay";
+ } else if ($peer_type == 2) {
+ $peer_type_str = "manual";
+ } else if ($peer_type == 3) {
+ $peer_type_str = "feeler";
+ } else if ($peer_type == 4) {
+ $peer_type_str = "block-relay";
+ } else if ($peer_type == 5) {
+ $peer_type_str = "addr-fetch";
+ }
+
+ printf("inbound '%s' msg from peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type_str, $peer_addr, $msg_len);
}
usdt:./src/bitcoind:net:outbound_message
{
$peer_id = (int64) arg0;
$peer_addr = str(arg1);
- $peer_type = str(arg2);
+ $peer_type = (int32) arg2;
$msg_type = str(arg3);
$msg_len = arg4;
- printf("outbound '%s' msg to peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type, $peer_addr, $msg_len);
+ $peer_type_str = "unknown";
+ if ($peer_type == 0) {
+ $peer_type_str = "inbound";
+ } else if ($peer_type == 1) {
+ $peer_type_str = "outbound-full-relay";
+ } else if ($peer_type == 2) {
+ $peer_type_str = "manual";
+ } else if ($peer_type == 3) {
+ $peer_type_str = "feeler";
+ } else if ($peer_type == 4) {
+ $peer_type_str = "block-relay";
+ } else if ($peer_type == 5) {
+ $peer_type_str = "addr-fetch";
+ }
+
+ printf("outbound '%s' msg to peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type_str, $peer_addr, $msg_len);
} |
Should probably also change the example tracepoint in Line 193 in ca7a124
|
On Sat, Dec 18, 2021 at 06:32:22AM -0800, 0xB10C wrote:
Should probably also change the example tracepoint in `doc/tracing.md`
https://github.com/bitcoin/bitcoin/blob/ca7a12479a9aae6ff20de355cc28dcd2f71a74cb/doc/tracing.md?plain=1#L193
good catch
|
[0] https://github.com/bitcoin/bitcoin/blob/98a2ddcd6ed01a38cd0dad7c1abc7023a60d3fd0/src/net.h#L120
[1] I didn't find any other way of doing this in bpftrace. Would be nice if the if-else could at least be extracted into a function..
oops missed this one. perhaps we can just use a map here. will update.
|
bpftrace doesn't support functions, best I could do was with a pre-processor macro but that seems like a somewhat horrible solution which now would require a build step: diff --git a/contrib/tracing/log_p2p_traffic.bt b/contrib/tracing/log_p2p_traffic.bt
index f62956aa5e..9ed8a6aa10 100755
--- a/contrib/tracing/log_p2p_traffic.bt
+++ b/contrib/tracing/log_p2p_traffic.bt
@@ -1,15 +1,25 @@
#!/usr/bin/env bpftrace
+#define CONNTYPE(ct) \
+ (ct == 0 ? "inbound":\
+ (ct == 1 ? "outbound-full-relay":\
+ (ct == 2 ? "manual":\
+ (ct == 3 ? "feeler":\
+ (ct == 4 ? "block-relay":\
+ (ct == 5 ? "addr-fetch":\
+ "unknown"\
+ ))))))
+
BEGIN
{
- printf("Logging P2P traffic\n")
+ printf("Logging P2P traffic\n");
}
usdt:./src/bitcoind:net:inbound_message
{
$peer_id = (int64) arg0;
$peer_addr = str(arg1);
- $peer_type = str(arg2);
+ $peer_type = CONNTYPE(arg2);
$msg_type = str(arg3);
$msg_len = arg4;
printf("inbound '%s' msg from peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type, $peer_addr, $msg_len);
@@ -19,7 +29,7 @@ usdt:./src/bitcoind:net:outbound_message
{
$peer_id = (int64) arg0;
$peer_addr = str(arg1);
- $peer_type = str(arg2);
+ $peer_type = CONNTYPE(arg2);
$msg_type = str(arg3);
$msg_len = arg4; I tried with a bpf map but it was complaining about how the 64-byte(?) strings in the map were taking up too much stack size... I was able to reduce the stack size with BPFTRACE_STRLEN=32 but that conflicts with the docs for this file, since we might need it for some peer_addr logs? Honestly bpftrace is looking like a poor tool for this particular job. |
bpftrace doesn't have any functions as of writing this commit, so we have to do a ternary lookup hack that introduces a bit of code duplication. Alternatively you could try to use a bpf map, but this uses a lot of stack space in bpftrace which makes it harder to run this script effectively. Signed-off-by: William Casarin <jb55@jb55.com>
ca7a124
to
e049b7d
Compare
I counter your inelegant patch with my own, less readable version. with no support for basic abstractions, we might just need to hack it for now, and perhaps wait for bpftrace updates before we can clean this up. I really don't feel like adding a I updated docs + pushed this patch: |
doing some back of the envelope calculations, the amount of instructions this saves wouldn't even be noticable across the entire duration of IBD. going to just drop it to save review time. |
In the net:{inbound,outbound}_message tracepoint, we are calling the
ConnectionTypeAsString helper function that converts the enum to a
string. This has been shown to generate quite a few instructions, even
when there is no code hooked into the tracepoint.
Here's the output from gdb when tracing the number of instructions
between two breakpoints around this tracepoint (see [1] for how to do
this):
Tracepoints should be almost zero cost, 293 instructions is a bit much
for effectively doing nothing most of the time.
Instead of calling the ConnectionTypeAsString function, simply add a
ConnectionType getter that returns the connection type enum directly.
This way the tracepoint simplifies to nine instructions that load values
into registers in preparation for a tracepoint call, which may or may
not be there depending on if there are any attached ebpf programs.
With this patch applied:
This tightens up these tracepoints in preparation for enabling tracing
in release builds[2].
Signed-off-by: William Casarin jb55@jb55.com
[1] #23724 (comment)
[2] #23724