qemu/scripts/userfaultfd-wrlat.py
<<
>>
Prefs
   1#!/usr/bin/python3
   2#
   3# userfaultfd-wrlat Summarize userfaultfd write fault latencies.
   4#                   Events are continuously accumulated for the
   5#                   run, while latency distribution histogram is
   6#                   dumped each 'interval' seconds.
   7#
   8#                   For Linux, uses BCC, eBPF.
   9#
  10# USAGE: userfaultfd-lat [interval [count]]
  11#
  12# Copyright Virtuozzo GmbH, 2020
  13#
  14# Authors:
  15#   Andrey Gruzdev   <andrey.gruzdev@virtuozzo.com>
  16#
  17# This work is licensed under the terms of the GNU GPL, version 2 or
  18# later.  See the COPYING file in the top-level directory.
  19
  20from __future__ import print_function
  21from bcc import BPF
  22from ctypes import c_ushort, c_int, c_ulonglong
  23from time import sleep
  24from sys import argv
  25
  26def usage():
  27    print("USAGE: %s [interval [count]]" % argv[0])
  28    exit()
  29
  30# define BPF program
  31bpf_text = """
  32#include <uapi/linux/ptrace.h>
  33#include <linux/mm.h>
  34
  35BPF_HASH(ev_start, u32, u64);
  36BPF_HISTOGRAM(ev_delta_hist, u64);
  37
  38/* Trace UFFD page fault start event. */
  39static void do_event_start()
  40{
  41    /* Using "(u32)" to drop group ID which is upper 32 bits */
  42    u32 tid = (u32) bpf_get_current_pid_tgid();
  43    u64 ts = bpf_ktime_get_ns();
  44
  45    ev_start.update(&tid, &ts);
  46}
  47
  48/* Trace UFFD page fault end event. */
  49static void do_event_end()
  50{
  51    /* Using "(u32)" to drop group ID which is upper 32 bits */
  52    u32 tid = (u32) bpf_get_current_pid_tgid();
  53    u64 ts = bpf_ktime_get_ns();
  54    u64 *tsp;
  55
  56    tsp = ev_start.lookup(&tid);
  57    if (tsp) {
  58        u64 delta = ts - (*tsp);
  59        /* Transform time delta to milliseconds */
  60        ev_delta_hist.increment(bpf_log2l(delta / 1000000));
  61        ev_start.delete(&tid);
  62    }
  63}
  64
  65/* KPROBE for handle_userfault(). */
  66int probe_handle_userfault(struct pt_regs *ctx, struct vm_fault *vmf,
  67        unsigned long reason)
  68{
  69    /* Trace only UFFD write faults. */
  70    if (reason & VM_UFFD_WP) {
  71        do_event_start();
  72    }
  73    return 0;
  74}
  75
  76/* KRETPROBE for handle_userfault(). */
  77int retprobe_handle_userfault(struct pt_regs *ctx)
  78{
  79    do_event_end();
  80    return 0;
  81}
  82"""
  83
  84# arguments
  85interval = 10
  86count = -1
  87if len(argv) > 1:
  88    try:
  89        interval = int(argv[1])
  90        if interval == 0:
  91            raise
  92        if len(argv) > 2:
  93            count = int(argv[2])
  94    except:    # also catches -h, --help
  95        usage()
  96
  97# load BPF program
  98b = BPF(text=bpf_text)
  99# attach KRPOBEs
 100b.attach_kprobe(event="handle_userfault", fn_name="probe_handle_userfault")
 101b.attach_kretprobe(event="handle_userfault", fn_name="retprobe_handle_userfault")
 102
 103# header
 104print("Tracing UFFD-WP write fault latency... Hit Ctrl-C to end.")
 105
 106# output
 107loop = 0
 108do_exit = 0
 109while (1):
 110    if count > 0:
 111        loop += 1
 112        if loop > count:
 113            exit()
 114    try:
 115        sleep(interval)
 116    except KeyboardInterrupt:
 117        pass; do_exit = 1
 118
 119    print()
 120    b["ev_delta_hist"].print_log2_hist("msecs")
 121    if do_exit:
 122        exit()
 123