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