]>
Commit | Line | Data |
---|---|---|
c7243566 AG |
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 | ||
20 | from __future__ import print_function | |
21 | from bcc import BPF | |
22 | from ctypes import c_ushort, c_int, c_ulonglong | |
23 | from time import sleep | |
24 | from sys import argv | |
25 | ||
26 | def usage(): | |
27 | print("USAGE: %s [interval [count]]" % argv[0]) | |
28 | exit() | |
29 | ||
30 | # define BPF program | |
31 | bpf_text = """ | |
32 | #include <uapi/linux/ptrace.h> | |
33 | #include <linux/mm.h> | |
34 | ||
35 | BPF_HASH(ev_start, u32, u64); | |
36 | BPF_HISTOGRAM(ev_delta_hist, u64); | |
37 | ||
38 | /* Trace UFFD page fault start event. */ | |
39 | static 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. */ | |
49 | static 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(). */ | |
66 | int 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(). */ | |
77 | int retprobe_handle_userfault(struct pt_regs *ctx) | |
78 | { | |
79 | do_event_end(); | |
80 | return 0; | |
81 | } | |
82 | """ | |
83 | ||
84 | # arguments | |
85 | interval = 10 | |
86 | count = -1 | |
87 | if 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 | |
98 | b = BPF(text=bpf_text) | |
99 | # attach KRPOBEs | |
100 | b.attach_kprobe(event="handle_userfault", fn_name="probe_handle_userfault") | |
101 | b.attach_kretprobe(event="handle_userfault", fn_name="retprobe_handle_userfault") | |
102 | ||
103 | # header | |
104 | print("Tracing UFFD-WP write fault latency... Hit Ctrl-C to end.") | |
105 | ||
106 | # output | |
107 | loop = 0 | |
108 | do_exit = 0 | |
109 | while (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() |