log_utxocache_flush.py raw

   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)
 110