| #!/usr/bin/python3 | |
| # | |
| # userfaultfd-wrlat Summarize userfaultfd write fault latencies. | |
| # Events are continuously accumulated for the | |
| # run, while latency distribution histogram is | |
| # dumped each 'interval' seconds. | |
| # | |
| # For Linux, uses BCC, eBPF. | |
| # | |
| # USAGE: userfaultfd-lat [interval [count]] | |
| # | |
| # Copyright Virtuozzo GmbH, 2020 | |
| # | |
| # Authors: | |
| # Andrey Gruzdev <andrey.gruzdev@virtuozzo.com> | |
| # | |
| # This work is licensed under the terms of the GNU GPL, version 2 or | |
| # later. See the COPYING file in the top-level directory. | |
| from __future__ import print_function | |
| from bcc import BPF | |
| from ctypes import c_ushort, c_int, c_ulonglong | |
| from time import sleep | |
| from sys import argv | |
| def usage(): | |
| print("USAGE: %s [interval [count]]" % argv[0]) | |
| exit() | |
| # define BPF program | |
| bpf_text = """ | |
| #include <uapi/linux/ptrace.h> | |
| #include <linux/mm.h> | |
| BPF_HASH(ev_start, u32, u64); | |
| BPF_HISTOGRAM(ev_delta_hist, u64); | |
| /* Trace UFFD page fault start event. */ | |
| static void do_event_start() | |
| { | |
| /* Using "(u32)" to drop group ID which is upper 32 bits */ | |
| u32 tid = (u32) bpf_get_current_pid_tgid(); | |
| u64 ts = bpf_ktime_get_ns(); | |
| ev_start.update(&tid, &ts); | |
| } | |
| /* Trace UFFD page fault end event. */ | |
| static void do_event_end() | |
| { | |
| /* Using "(u32)" to drop group ID which is upper 32 bits */ | |
| u32 tid = (u32) bpf_get_current_pid_tgid(); | |
| u64 ts = bpf_ktime_get_ns(); | |
| u64 *tsp; | |
| tsp = ev_start.lookup(&tid); | |
| if (tsp) { | |
| u64 delta = ts - (*tsp); | |
| /* Transform time delta to milliseconds */ | |
| ev_delta_hist.increment(bpf_log2l(delta / 1000000)); | |
| ev_start.delete(&tid); | |
| } | |
| } | |
| /* KPROBE for handle_userfault(). */ | |
| int probe_handle_userfault(struct pt_regs *ctx, struct vm_fault *vmf, | |
| unsigned long reason) | |
| { | |
| /* Trace only UFFD write faults. */ | |
| if (reason & VM_UFFD_WP) { | |
| do_event_start(); | |
| } | |
| return 0; | |
| } | |
| /* KRETPROBE for handle_userfault(). */ | |
| int retprobe_handle_userfault(struct pt_regs *ctx) | |
| { | |
| do_event_end(); | |
| return 0; | |
| } | |
| """ | |
| # arguments | |
| interval = 10 | |
| count = -1 | |
| if len(argv) > 1: | |
| try: | |
| interval = int(argv[1]) | |
| if interval == 0: | |
| raise | |
| if len(argv) > 2: | |
| count = int(argv[2]) | |
| except: # also catches -h, --help | |
| usage() | |
| # load BPF program | |
| b = BPF(text=bpf_text) | |
| # attach KRPOBEs | |
| b.attach_kprobe(event="handle_userfault", fn_name="probe_handle_userfault") | |
| b.attach_kretprobe(event="handle_userfault", fn_name="retprobe_handle_userfault") | |
| # header | |
| print("Tracing UFFD-WP write fault latency... Hit Ctrl-C to end.") | |
| # output | |
| loop = 0 | |
| do_exit = 0 | |
| while (1): | |
| if count > 0: | |
| loop += 1 | |
| if loop > count: | |
| exit() | |
| try: | |
| sleep(interval) | |
| except KeyboardInterrupt: | |
| pass; do_exit = 1 | |
| print() | |
| b["ev_delta_hist"].print_log2_hist("msecs") | |
| if do_exit: | |
| exit() |