1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python 2*387f9dfdSAndroid Build Coastguard Worker# @lint-avoid-python-3-compatibility-imports 3*387f9dfdSAndroid Build Coastguard Worker# 4*387f9dfdSAndroid Build Coastguard Worker# nfsdist Summarize NFS operation latency 5*387f9dfdSAndroid Build Coastguard Worker# for Linux, uses BCC and eBPF 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# USAGE: nfsdist [-h] [-T] [-m] [-p PID] [interval] [count] 8*387f9dfdSAndroid Build Coastguard Worker# 9*387f9dfdSAndroid Build Coastguard Worker# 4-Sep-2017 Samuel Nair created this 10*387f9dfdSAndroid Build Coastguard Worker 11*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 12*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 13*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime 14*387f9dfdSAndroid Build Coastguard Workerimport argparse 15*387f9dfdSAndroid Build Coastguard Worker 16*387f9dfdSAndroid Build Coastguard Worker# arguments 17*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 18*387f9dfdSAndroid Build Coastguard Worker ./nfsdist # show operation latency as a histogram 19*387f9dfdSAndroid Build Coastguard Worker ./nfsdist -p 181 # trace PID 181 only 20*387f9dfdSAndroid Build Coastguard Worker ./nfsdist 1 10 # print 1 second summaries, 10 times 21*387f9dfdSAndroid Build Coastguard Worker ./nfsdist -m 5 # 5s summaries, milliseconds 22*387f9dfdSAndroid Build Coastguard Worker""" 23*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 24*387f9dfdSAndroid Build Coastguard Worker description="Summarize NFS operation latency", 25*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 26*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 27*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--notimestamp", action="store_true", 28*387f9dfdSAndroid Build Coastguard Worker help="don't include timestamp on interval output") 29*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--milliseconds", action="store_true", 30*387f9dfdSAndroid Build Coastguard Worker help="output in milliseconds") 31*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", 32*387f9dfdSAndroid Build Coastguard Worker help="trace this PID only") 33*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", 34*387f9dfdSAndroid Build Coastguard Worker help="output interval, in seconds") 35*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999, 36*387f9dfdSAndroid Build Coastguard Worker help="number of outputs") 37*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 38*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 39*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 40*387f9dfdSAndroid Build Coastguard Workerpid = args.pid 41*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count) 42*387f9dfdSAndroid Build Coastguard Workerif args.milliseconds: 43*387f9dfdSAndroid Build Coastguard Worker factor = 1000000 44*387f9dfdSAndroid Build Coastguard Worker label = "msecs" 45*387f9dfdSAndroid Build Coastguard Workerelse: 46*387f9dfdSAndroid Build Coastguard Worker factor = 1000 47*387f9dfdSAndroid Build Coastguard Worker label = "usecs" 48*387f9dfdSAndroid Build Coastguard Worker if args.interval and int(args.interval) == 0: 49*387f9dfdSAndroid Build Coastguard Worker print("ERROR: interval 0. Exiting.") 50*387f9dfdSAndroid Build Coastguard Worker exit() 51*387f9dfdSAndroid Build Coastguard Workerdebug = 0 52*387f9dfdSAndroid Build Coastguard Worker 53*387f9dfdSAndroid Build Coastguard Worker# define BPF program 54*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 55*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 56*387f9dfdSAndroid Build Coastguard Worker#include <linux/fs.h> 57*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h> 58*387f9dfdSAndroid Build Coastguard Worker 59*387f9dfdSAndroid Build Coastguard Worker#define OP_NAME_LEN 8 60*387f9dfdSAndroid Build Coastguard Workertypedef struct dist_key { 61*387f9dfdSAndroid Build Coastguard Worker char op[OP_NAME_LEN]; 62*387f9dfdSAndroid Build Coastguard Worker u64 slot; 63*387f9dfdSAndroid Build Coastguard Worker} dist_key_t; 64*387f9dfdSAndroid Build Coastguard Worker 65*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(start, u32); 66*387f9dfdSAndroid Build Coastguard WorkerBPF_HISTOGRAM(dist, dist_key_t); 67*387f9dfdSAndroid Build Coastguard Worker 68*387f9dfdSAndroid Build Coastguard Worker// time operation 69*387f9dfdSAndroid Build Coastguard Workerint trace_entry(struct pt_regs *ctx) 70*387f9dfdSAndroid Build Coastguard Worker{ 71*387f9dfdSAndroid Build Coastguard Worker u64 pid_tgid = bpf_get_current_pid_tgid(); 72*387f9dfdSAndroid Build Coastguard Worker u32 pid = pid_tgid >> 32; 73*387f9dfdSAndroid Build Coastguard Worker u32 tid = (u32)pid_tgid; 74*387f9dfdSAndroid Build Coastguard Worker 75*387f9dfdSAndroid Build Coastguard Worker if (FILTER_PID) 76*387f9dfdSAndroid Build Coastguard Worker return 0; 77*387f9dfdSAndroid Build Coastguard Worker u64 ts = bpf_ktime_get_ns(); 78*387f9dfdSAndroid Build Coastguard Worker start.update(&tid, &ts); 79*387f9dfdSAndroid Build Coastguard Worker return 0; 80*387f9dfdSAndroid Build Coastguard Worker} 81*387f9dfdSAndroid Build Coastguard Worker 82*387f9dfdSAndroid Build Coastguard Workerstatic int trace_return(struct pt_regs *ctx, const char *op) 83*387f9dfdSAndroid Build Coastguard Worker{ 84*387f9dfdSAndroid Build Coastguard Worker u64 *tsp; 85*387f9dfdSAndroid Build Coastguard Worker u64 pid_tgid = bpf_get_current_pid_tgid(); 86*387f9dfdSAndroid Build Coastguard Worker u32 pid = pid_tgid >> 32; 87*387f9dfdSAndroid Build Coastguard Worker u32 tid = (u32)pid_tgid; 88*387f9dfdSAndroid Build Coastguard Worker 89*387f9dfdSAndroid Build Coastguard Worker // fetch timestamp and calculate delta 90*387f9dfdSAndroid Build Coastguard Worker tsp = start.lookup(&tid); 91*387f9dfdSAndroid Build Coastguard Worker if (tsp == 0) { 92*387f9dfdSAndroid Build Coastguard Worker return 0; // missed start or filtered 93*387f9dfdSAndroid Build Coastguard Worker } 94*387f9dfdSAndroid Build Coastguard Worker u64 delta = (bpf_ktime_get_ns() - *tsp) / FACTOR; 95*387f9dfdSAndroid Build Coastguard Worker 96*387f9dfdSAndroid Build Coastguard Worker // store as histogram 97*387f9dfdSAndroid Build Coastguard Worker dist_key_t key = {.slot = bpf_log2l(delta)}; 98*387f9dfdSAndroid Build Coastguard Worker __builtin_memcpy(&key.op, op, sizeof(key.op)); 99*387f9dfdSAndroid Build Coastguard Worker dist.atomic_increment(key); 100*387f9dfdSAndroid Build Coastguard Worker 101*387f9dfdSAndroid Build Coastguard Worker start.delete(&tid); 102*387f9dfdSAndroid Build Coastguard Worker return 0; 103*387f9dfdSAndroid Build Coastguard Worker} 104*387f9dfdSAndroid Build Coastguard Worker 105*387f9dfdSAndroid Build Coastguard Workerint trace_read_return(struct pt_regs *ctx) 106*387f9dfdSAndroid Build Coastguard Worker{ 107*387f9dfdSAndroid Build Coastguard Worker char *op = "read"; 108*387f9dfdSAndroid Build Coastguard Worker return trace_return(ctx, op); 109*387f9dfdSAndroid Build Coastguard Worker} 110*387f9dfdSAndroid Build Coastguard Worker 111*387f9dfdSAndroid Build Coastguard Workerint trace_write_return(struct pt_regs *ctx) 112*387f9dfdSAndroid Build Coastguard Worker{ 113*387f9dfdSAndroid Build Coastguard Worker char *op = "write"; 114*387f9dfdSAndroid Build Coastguard Worker return trace_return(ctx, op); 115*387f9dfdSAndroid Build Coastguard Worker} 116*387f9dfdSAndroid Build Coastguard Worker 117*387f9dfdSAndroid Build Coastguard Workerint trace_open_return(struct pt_regs *ctx) 118*387f9dfdSAndroid Build Coastguard Worker{ 119*387f9dfdSAndroid Build Coastguard Worker char *op = "open"; 120*387f9dfdSAndroid Build Coastguard Worker return trace_return(ctx, op); 121*387f9dfdSAndroid Build Coastguard Worker} 122*387f9dfdSAndroid Build Coastguard Worker 123*387f9dfdSAndroid Build Coastguard Workerint trace_getattr_return(struct pt_regs *ctx) 124*387f9dfdSAndroid Build Coastguard Worker{ 125*387f9dfdSAndroid Build Coastguard Worker char *op = "getattr"; 126*387f9dfdSAndroid Build Coastguard Worker return trace_return(ctx, op); 127*387f9dfdSAndroid Build Coastguard Worker} 128*387f9dfdSAndroid Build Coastguard Worker""" 129*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('FACTOR', str(factor)) 130*387f9dfdSAndroid Build Coastguard Workerif args.pid: 131*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) 132*387f9dfdSAndroid Build Coastguard Workerelse: 133*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FILTER_PID', '0') 134*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf: 135*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 136*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 137*387f9dfdSAndroid Build Coastguard Worker exit() 138*387f9dfdSAndroid Build Coastguard Worker 139*387f9dfdSAndroid Build Coastguard Worker# load BPF program 140*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 141*387f9dfdSAndroid Build Coastguard Worker 142*387f9dfdSAndroid Build Coastguard Worker# common file functions 143*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="nfs_file_read", fn_name="trace_entry") 144*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="nfs_file_write", fn_name="trace_entry") 145*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="nfs_file_open", fn_name="trace_entry") 146*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="nfs_getattr", fn_name="trace_entry") 147*387f9dfdSAndroid Build Coastguard Worker 148*387f9dfdSAndroid Build Coastguard Workerb.attach_kretprobe(event="nfs_file_read", fn_name="trace_read_return") 149*387f9dfdSAndroid Build Coastguard Workerb.attach_kretprobe(event="nfs_file_write", fn_name="trace_write_return") 150*387f9dfdSAndroid Build Coastguard Workerb.attach_kretprobe(event="nfs_file_open", fn_name="trace_open_return") 151*387f9dfdSAndroid Build Coastguard Workerb.attach_kretprobe(event="nfs_getattr", fn_name="trace_getattr_return") 152*387f9dfdSAndroid Build Coastguard Worker 153*387f9dfdSAndroid Build Coastguard Workerif BPF.get_kprobe_functions(b'nfs4_file_open'): 154*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="nfs4_file_open", fn_name="trace_entry") 155*387f9dfdSAndroid Build Coastguard Worker b.attach_kretprobe(event="nfs4_file_open", fn_name="trace_open_return") 156*387f9dfdSAndroid Build Coastguard Workerelse: 157*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="nfs_file_open", fn_name="trace_entry") 158*387f9dfdSAndroid Build Coastguard Worker b.attach_kretprobe(event="nfs_file_open", fn_name="trace_open_return") 159*387f9dfdSAndroid Build Coastguard Worker 160*387f9dfdSAndroid Build Coastguard Workerprint("Tracing NFS operation latency... Hit Ctrl-C to end.") 161*387f9dfdSAndroid Build Coastguard Worker 162*387f9dfdSAndroid Build Coastguard Worker# output 163*387f9dfdSAndroid Build Coastguard Workerexiting = 0 164*387f9dfdSAndroid Build Coastguard Workerdist = b.get_table("dist") 165*387f9dfdSAndroid Build Coastguard Workerwhile (1): 166*387f9dfdSAndroid Build Coastguard Worker try: 167*387f9dfdSAndroid Build Coastguard Worker if args.interval: 168*387f9dfdSAndroid Build Coastguard Worker sleep(int(args.interval)) 169*387f9dfdSAndroid Build Coastguard Worker else: 170*387f9dfdSAndroid Build Coastguard Worker sleep(99999999) 171*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 172*387f9dfdSAndroid Build Coastguard Worker exiting = 1 173*387f9dfdSAndroid Build Coastguard Worker 174*387f9dfdSAndroid Build Coastguard Worker print() 175*387f9dfdSAndroid Build Coastguard Worker if args.interval and (not args.notimestamp): 176*387f9dfdSAndroid Build Coastguard Worker print(strftime("%H:%M:%S:")) 177*387f9dfdSAndroid Build Coastguard Worker 178*387f9dfdSAndroid Build Coastguard Worker dist.print_log2_hist(label, "operation", section_print_fn=bytes.decode) 179*387f9dfdSAndroid Build Coastguard Worker dist.clear() 180*387f9dfdSAndroid Build Coastguard Worker 181*387f9dfdSAndroid Build Coastguard Worker countdown -= 1 182*387f9dfdSAndroid Build Coastguard Worker if exiting or countdown == 0: 183*387f9dfdSAndroid Build Coastguard Worker exit() 184