|
@@ -0,0 +1,122 @@
|
|
|
+#!/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()
|