123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								#!/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()
							 |