0xB10C
bitcoin++ 2022
pls no photos, ty
Buzzword: observabillity
extended Berkeley Packet Filter
"user-defined, sandboxed programs executed Linux kernel"
Tracing the kernel and userspace applications
| dynamic | static | |
|---|---|---|
| kernel space | kprobes / kretprobes | (raw) tracepoints |
| user space | uprobes / uretprobes | Userspace, Statically Defined Tracing (USDT) |
Userspace, Statically Defined Tracing
TRACE macros for tracepoints with 0 to 12 arguments
#define TRACE(context, event)
#define TRACE1(context, event, a)
#define TRACE2(context, event, a, b)
...
#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
in src/util/trace.h
net:outbound_message: sending an outbound P2P message to a peer
TRACE6(net, outbound_message,
peer->GetId(),
peer->m_addr_name.c_str(),
peer->ConnectionTypeAsString().c_str(),
msg.m_type.c_str(),
msg.data.size(),
msg.data.data()
);
in src/net.cpp
| bpftrace | BPF Compiler Collection (bcc) |
|---|---|
| high-level tracing language | toolkit for creating tracing programs |
| basic, quick scripting | more complex tools |
| only prints information (as logs, histograms, CSV, ...) | no limitations |
| github.com/iovisor/bpftrace | github.com/iovisor/bcc |
#!/usr/bin/env bpftrace
BEGIN {
printf("Logging P2P traffic\n")
}
usdt:./src/bitcoind:net:inbound_message {
$peer_id = (int64) arg0;
$peer_addr = str(arg1);
$peer_type = str(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);
}
contrib/tracing/log_p2p_traffic.bt
C program that's compiled to eBPF bytecode by BCC
BPF_PERF_OUTPUT(inbound_messages);
int trace_inbound_message(struct pt_regs *ctx) {
struct p2p_message msg = {};
bpf_usdt_readarg(1, ctx, &msg.peer_id);
bpf_usdt_readarg_p(2, ctx, &msg.peer_addr, MAX_PEER_ADDR_LENGTH);
bpf_usdt_readarg_p(3, ctx, &msg.peer_conn_type, MAX_PEER_CONN_TYPE_LENGTH);
bpf_usdt_readarg_p(4, ctx, &msg.msg_type, MAX_MSG_TYPE_LENGTH);
bpf_usdt_readarg(5, ctx, &msg.msg_size);
inbound_messages.perf_submit(ctx, &msg, sizeof(msg));
return 0;
};
Snippet from contrib/tracing/p2p_monitor.py
Python script loading the C program, hooking into the tracepoints, and waiting for inbound messages
ctx = USDT(path=str(bitcoind_path))
ctx.enable_probe(probe="net:inbound_message", fn_name="trace_inbound_message")
bpf = BPF(text=c_prog_from_prev_slide, usdt_contexts=[ctx])
def handle_inbound(_, data, __):
event = bpf["inbound_messages"].event(data)
print(f"inbound from {event.peer_id}: {event.msg_type}")
bpf["inbound_messages"].open_perf_buffer(handle_inbound)
while True:
bpf.perf_buffer_poll()
Snippet from contrib/tracing/p2p_monitor.py
ssh workshop@[IPv4] -i [priv key file] # without the .pub
# for example
ssh workshop@13.37.83.33 -i ~/.ssh/id_bpp22workshop
Tracing with eBPF is programable
┌──────────────────┐ ┌──────────────┐
│ tracing script │ │ bitcoind │
│==================│ 2. │==============│
│ eBPF │ tracing │ hooks │ │
│ code │ logic │ into┌─┤►tracepoint 1─┼───┐ 3.
└────┬───┴──▲──────┘ ├─┤►tracepoint 2 │ │ pass args
1. │ │ 4. │ │ ... │ │ to eBPF
User compiles │ │ pass data to │ └──────────────┘ │ program
Space & loads │ │ tracing script │ │
─────────────────┼──────┼─────────────────┼────────────────────┼───
Kernel │ │ │ │
Space ┌──┬─▼──────┴─────────────────┴────────────┐ │
│ │ eBPF program │◄──────┘
│ └───────────────────────────────────────┤
│ eBPF kernel Virtual Machine (sandboxed) │
└──────────────────────────────────────────┘