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) │ └──────────────────────────────────────────┘