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