#!/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 # # 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 #include 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()