log_utxocache_flush.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2021-2022 The Limenka 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 Limenka utxo set cache flushes utilizing
  11      the utxocache:flush tracepoint."""
  12  
  13  # USAGE:  ./contrib/tracing/log_utxocache_flush.py path/to/limenkad
  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      'ALWAYS'
  49  ]
  50  
  51  
  52  class Data(ctypes.Structure):
  53      # define output data structure corresponding to struct data_t
  54      _fields_ = [
  55          ("duration", ctypes.c_uint64),
  56          ("mode", ctypes.c_uint32),
  57          ("coins_count", ctypes.c_uint64),
  58          ("coins_mem_usage", ctypes.c_uint64),
  59          ("is_flush_for_prune", ctypes.c_bool)
  60      ]
  61  
  62  
  63  def print_event(event):
  64      print("%-15d %-10s %-15d %-15s %-8s" % (
  65          event.duration,
  66          FLUSH_MODES[event.mode],
  67          event.coins_count,
  68          "%.2f kB" % (event.coins_mem_usage/1000),
  69          event.is_flush_for_prune
  70      ))
  71  
  72  
  73  def main(pid):
  74      print(f"Hooking into limenkad with pid {pid}")
  75      limenkad_with_usdts = USDT(pid=int(pid))
  76  
  77      # attaching the trace functions defined in the BPF program
  78      # to the tracepoints
  79      limenkad_with_usdts.enable_probe(
  80          probe="flush", fn_name="trace_flush")
  81      b = BPF(text=program, usdt_contexts=[limenkad_with_usdts])
  82  
  83      def handle_flush(_, data, size):
  84          """ Coins Flush handler.
  85            Called each time coin caches and indexes are flushed."""
  86          event = ctypes.cast(data, ctypes.POINTER(Data)).contents
  87          print_event(event)
  88  
  89      b["flush"].open_perf_buffer(handle_flush)
  90      print("Logging utxocache flushes. Ctrl-C to end...")
  91      print("%-15s %-10s %-15s %-15s %-8s" % ("Duration (µs)", "Mode",
  92                                              "Coins Count", "Memory Usage",
  93                                              "Flush for Prune"))
  94  
  95      while True:
  96          try:
  97              b.perf_buffer_poll()
  98          except KeyboardInterrupt:
  99              exit(0)
 100  
 101  
 102  if __name__ == "__main__":
 103      if len(sys.argv) != 2:
 104          print("USAGE: ", sys.argv[0], "<pid of limenkad>")
 105          exit(1)
 106  
 107      pid = sys.argv[1]
 108      main(pid)
 109