1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
#!/usr/bin/env python3
# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import sys
import ctypes
from bcc import BPF, USDT
"""Example logging Bitcoin Core utxo set cache flushes utilizing
the utxocache:flush tracepoint."""
# USAGE: ./contrib/tracing/log_utxocache_flush.py path/to/bitcoind
# BCC: The C program to be compiled to an eBPF program (by BCC) and loaded into
# a sandboxed Linux kernel VM.
program = """
# include <uapi/linux/ptrace.h>
struct data_t
{
u64 duration;
u32 mode;
u64 coins_count;
u64 coins_mem_usage;
bool is_flush_prune;
bool is_full_flush;
};
// BPF perf buffer to push the data to user space.
BPF_PERF_OUTPUT(flush);
int trace_flush(struct pt_regs *ctx) {
struct data_t data = {};
bpf_usdt_readarg(1, ctx, &data.duration);
bpf_usdt_readarg(2, ctx, &data.mode);
bpf_usdt_readarg(3, ctx, &data.coins_count);
bpf_usdt_readarg(4, ctx, &data.coins_mem_usage);
bpf_usdt_readarg(5, ctx, &data.is_flush_prune);
bpf_usdt_readarg(5, ctx, &data.is_full_flush);
flush.perf_submit(ctx, &data, sizeof(data));
return 0;
}
"""
FLUSH_MODES = [
'NONE',
'IF_NEEDED',
'PERIODIC',
'ALWAYS'
]
class Data(ctypes.Structure):
# define output data structure corresponding to struct data_t
_fields_ = [
("duration", ctypes.c_uint64),
("mode", ctypes.c_uint32),
("coins_count", ctypes.c_uint64),
("coins_mem_usage", ctypes.c_uint64),
("is_flush_prune", ctypes.c_bool),
("is_full_flush", ctypes.c_bool)
]
def print_event(event):
print("%-15d %-10s %-15d %-15s %-8s %-8s" % (
event.duration,
FLUSH_MODES[event.mode],
event.coins_count,
"%.2f kB" % (event.coins_mem_usage/1000),
event.is_flush_prune,
event.is_full_flush
))
def main(bitcoind_path):
bitcoind_with_usdts = USDT(path=str(bitcoind_path))
# attaching the trace functions defined in the BPF program
# to the tracepoints
bitcoind_with_usdts.enable_probe(
probe="flush", fn_name="trace_flush")
b = BPF(text=program, usdt_contexts=[bitcoind_with_usdts])
def handle_flush(_, data, size):
""" Coins Flush handler.
Called each time coin caches and indexes are flushed."""
event = ctypes.cast(data, ctypes.POINTER(Data)).contents
print_event(event)
b["flush"].open_perf_buffer(handle_flush)
print("Logging utxocache flushes. Ctrl-C to end...")
print("%-15s %-10s %-15s %-15s %-8s %-8s" % ("Duration (µs)", "Mode",
"Coins Count", "Memory Usage",
"Prune", "Full Flush"))
while True:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit(0)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("USAGE: ", sys.argv[0], "path/to/bitcoind")
exit(1)
path = sys.argv[1]
main(path)
|