Add BCC/eBPF script to analyze userfaultfd write fault latency distribution. Signed-off-by: Andrey Gruzdev <andrey.gruzdev@virtuozzo.com> Reviewed-by: Peter Xu <peterx@redhat.com> Message-Id: <20210129101407.103458-6-andrey.gruzdev@virtuozzo.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
		
			
				
	
	
		
			123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/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()
 |