xref: /qemu/scripts/userfaultfd-wrlat.py (revision 727385c4)
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