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