blob: 4c1f8e3489b5ba3e8bdb5dc9f435bac39c423944 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
samuelnairceff3e02017-09-12 13:13:10 +08002# @lint-avoid-python-3-compatibility-imports
3#
4# nfsdist Summarize NFS operation latency
5# for Linux, uses BCC and eBPF
6#
7# USAGE: nfsdist [-h] [-T] [-m] [-p PID] [interval] [count]
8#
9# 4-Sep-2017 Samuel Nair created this
10
11from __future__ import print_function
12from bcc import BPF
13from time import sleep, strftime
14import argparse
15
16# arguments
17examples = """examples:
18 ./nfsdist # show operation latency as a histogram
19 ./nfsdist -p 181 # trace PID 181 only
20 ./nfsdist 1 10 # print 1 second summaries, 10 times
21 ./nfsdist -m 5 # 5s summaries, milliseconds
22"""
23parser = argparse.ArgumentParser(
24 description="Summarize NFS operation latency",
25 formatter_class=argparse.RawDescriptionHelpFormatter,
26 epilog=examples)
27parser.add_argument("-T", "--notimestamp", action="store_true",
28 help="don't include timestamp on interval output")
29parser.add_argument("-m", "--milliseconds", action="store_true",
30 help="output in milliseconds")
31parser.add_argument("-p", "--pid",
32 help="trace this PID only")
33parser.add_argument("interval", nargs="?",
34 help="output interval, in seconds")
35parser.add_argument("count", nargs="?", default=99999999,
36 help="number of outputs")
Nathan Scottcf0792f2018-02-02 16:56:50 +110037parser.add_argument("--ebpf", action="store_true",
38 help=argparse.SUPPRESS)
samuelnairceff3e02017-09-12 13:13:10 +080039args = parser.parse_args()
40pid = args.pid
41countdown = int(args.count)
42if args.milliseconds:
43 factor = 1000000
44 label = "msecs"
45else:
46 factor = 1000
47 label = "usecs"
48 if args.interval and int(args.interval) == 0:
49 print("ERROR: interval 0. Exiting.")
50 exit()
51debug = 0
52
53# define BPF program
54bpf_text = """
55#include <uapi/linux/ptrace.h>
56#include <linux/fs.h>
57#include <linux/sched.h>
58
59#define OP_NAME_LEN 8
60typedef struct dist_key {
61 char op[OP_NAME_LEN];
62 u64 slot;
63} dist_key_t;
64
65BPF_HASH(start, u32);
66BPF_HISTOGRAM(dist, dist_key_t);
67
68// time operation
69int trace_entry(struct pt_regs *ctx)
70{
Hengqi Chen89c96a82021-05-13 21:46:16 +080071 u64 pid_tgid = bpf_get_current_pid_tgid();
72 u32 pid = pid_tgid >> 32;
73 u32 tid = (u32)pid_tgid;
74
samuelnairceff3e02017-09-12 13:13:10 +080075 if (FILTER_PID)
76 return 0;
77 u64 ts = bpf_ktime_get_ns();
Hengqi Chen89c96a82021-05-13 21:46:16 +080078 start.update(&tid, &ts);
samuelnairceff3e02017-09-12 13:13:10 +080079 return 0;
80}
81
82static int trace_return(struct pt_regs *ctx, const char *op)
83{
84 u64 *tsp;
Hengqi Chen89c96a82021-05-13 21:46:16 +080085 u64 pid_tgid = bpf_get_current_pid_tgid();
86 u32 pid = pid_tgid >> 32;
87 u32 tid = (u32)pid_tgid;
samuelnairceff3e02017-09-12 13:13:10 +080088
89 // fetch timestamp and calculate delta
Hengqi Chen89c96a82021-05-13 21:46:16 +080090 tsp = start.lookup(&tid);
samuelnairceff3e02017-09-12 13:13:10 +080091 if (tsp == 0) {
92 return 0; // missed start or filtered
93 }
94 u64 delta = (bpf_ktime_get_ns() - *tsp) / FACTOR;
95
96 // store as histogram
97 dist_key_t key = {.slot = bpf_log2l(delta)};
98 __builtin_memcpy(&key.op, op, sizeof(key.op));
zcy80242fb2021-07-02 00:12:32 +080099 dist.atomic_increment(key);
samuelnairceff3e02017-09-12 13:13:10 +0800100
Hengqi Chen89c96a82021-05-13 21:46:16 +0800101 start.delete(&tid);
samuelnairceff3e02017-09-12 13:13:10 +0800102 return 0;
103}
104
105int trace_read_return(struct pt_regs *ctx)
106{
107 char *op = "read";
108 return trace_return(ctx, op);
109}
110
111int trace_write_return(struct pt_regs *ctx)
112{
113 char *op = "write";
114 return trace_return(ctx, op);
115}
116
117int trace_open_return(struct pt_regs *ctx)
118{
119 char *op = "open";
120 return trace_return(ctx, op);
121}
122
123int trace_getattr_return(struct pt_regs *ctx)
124{
125 char *op = "getattr";
126 return trace_return(ctx, op);
127}
128"""
129bpf_text = bpf_text.replace('FACTOR', str(factor))
130if args.pid:
131 bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid)
132else:
133 bpf_text = bpf_text.replace('FILTER_PID', '0')
Nathan Scottcf0792f2018-02-02 16:56:50 +1100134if debug or args.ebpf:
samuelnairceff3e02017-09-12 13:13:10 +0800135 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100136 if args.ebpf:
137 exit()
samuelnairceff3e02017-09-12 13:13:10 +0800138
139# load BPF program
140b = BPF(text=bpf_text)
141
142# common file functions
143b.attach_kprobe(event="nfs_file_read", fn_name="trace_entry")
144b.attach_kprobe(event="nfs_file_write", fn_name="trace_entry")
samuelnairceff3e02017-09-12 13:13:10 +0800145b.attach_kprobe(event="nfs_file_open", fn_name="trace_entry")
146b.attach_kprobe(event="nfs_getattr", fn_name="trace_entry")
147
148b.attach_kretprobe(event="nfs_file_read", fn_name="trace_read_return")
149b.attach_kretprobe(event="nfs_file_write", fn_name="trace_write_return")
samuelnairceff3e02017-09-12 13:13:10 +0800150b.attach_kretprobe(event="nfs_file_open", fn_name="trace_open_return")
151b.attach_kretprobe(event="nfs_getattr", fn_name="trace_getattr_return")
152
sabbenea433ef92020-05-05 23:55:17 -0700153if BPF.get_kprobe_functions(b'nfs4_file_open'):
154 b.attach_kprobe(event="nfs4_file_open", fn_name="trace_entry")
155 b.attach_kretprobe(event="nfs4_file_open", fn_name="trace_open_return")
156else:
157 b.attach_kprobe(event="nfs_file_open", fn_name="trace_entry")
158 b.attach_kretprobe(event="nfs_file_open", fn_name="trace_open_return")
159
samuelnairceff3e02017-09-12 13:13:10 +0800160print("Tracing NFS operation latency... Hit Ctrl-C to end.")
161
162# output
163exiting = 0
164dist = b.get_table("dist")
165while (1):
166 try:
167 if args.interval:
168 sleep(int(args.interval))
169 else:
170 sleep(99999999)
171 except KeyboardInterrupt:
172 exiting = 1
173
174 print()
175 if args.interval and (not args.notimestamp):
176 print(strftime("%H:%M:%S:"))
177
178 dist.print_log2_hist(label, "operation", section_print_fn=bytes.decode)
179 dist.clear()
180
181 countdown -= 1
182 if exiting or countdown == 0:
183 exit()