blob: 07484e0315564d44e8c3f1b2c6741c61ffa654eb [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Brendan Gregga32fbaf2016-02-07 12:14:37 -08002# @lint-avoid-python-3-compatibility-imports
3#
Brendan Gregg75d3e9d2016-02-07 18:48:20 -08004# fileslower Trace slow synchronous file reads and writes.
5# For Linux, uses BCC, eBPF.
Brendan Gregga32fbaf2016-02-07 12:14:37 -08006#
Mark Drayton74347312016-08-25 20:46:35 +01007# USAGE: fileslower [-h] [-p PID] [-a] [min_ms]
Brendan Gregga32fbaf2016-02-07 12:14:37 -08008#
9# This script uses kernel dynamic tracing of synchronous reads and writes
Brendan Gregg75d3e9d2016-02-07 18:48:20 -080010# at the VFS interface, to identify slow file reads and writes for any file
11# system.
Brendan Gregga32fbaf2016-02-07 12:14:37 -080012#
13# This works by tracing __vfs_read() and __vfs_write(), and filtering for
14# synchronous I/O (the path to new_sync_read() and new_sync_write()), and
15# for I/O with filenames. This approach provides a view of just two file
16# system request types. There are typically many others: asynchronous I/O,
17# directory operations, file handle operations, etc, that this tool does not
Brendan Gregg75d3e9d2016-02-07 18:48:20 -080018# instrument.
Brendan Gregga32fbaf2016-02-07 12:14:37 -080019#
20# WARNING: This traces VFS reads and writes, which can be extremely frequent,
21# and so the overhead of this tool can become severe depending on the
22# workload.
23#
24# By default, a minimum millisecond threshold of 10 is used.
25#
26# Copyright 2016 Netflix, Inc.
27# Licensed under the Apache License, Version 2.0 (the "License")
28#
29# 06-Feb-2016 Brendan Gregg Created this.
30
31from __future__ import print_function
32from bcc import BPF
33import argparse
Mark Drayton266d6f62016-05-24 07:01:01 -070034import time
Brendan Gregga32fbaf2016-02-07 12:14:37 -080035
36# arguments
37examples = """examples:
Brendan Gregg75d3e9d2016-02-07 18:48:20 -080038 ./fileslower # trace sync file I/O slower than 10 ms (default)
39 ./fileslower 1 # trace sync file I/O slower than 1 ms
40 ./fileslower -p 185 # trace PID 185 only
Brendan Gregga32fbaf2016-02-07 12:14:37 -080041"""
42parser = argparse.ArgumentParser(
Brendan Gregg75d3e9d2016-02-07 18:48:20 -080043 description="Trace slow synchronous file reads and writes",
Brendan Gregga32fbaf2016-02-07 12:14:37 -080044 formatter_class=argparse.RawDescriptionHelpFormatter,
45 epilog=examples)
Mark Drayton74347312016-08-25 20:46:35 +010046parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid",
Brendan Gregga32fbaf2016-02-07 12:14:37 -080047 help="trace this PID only")
Mark Drayton74347312016-08-25 20:46:35 +010048parser.add_argument("-a", "--all-files", action="store_true",
49 help="include non-regular file types (sockets, FIFOs, etc)")
Brendan Gregga32fbaf2016-02-07 12:14:37 -080050parser.add_argument("min_ms", nargs="?", default='10',
51 help="minimum I/O duration to trace, in ms (default 10)")
Nathan Scottcf0792f2018-02-02 16:56:50 +110052parser.add_argument("--ebpf", action="store_true",
53 help=argparse.SUPPRESS)
Brendan Gregga32fbaf2016-02-07 12:14:37 -080054args = parser.parse_args()
55min_ms = int(args.min_ms)
Mark Drayton74347312016-08-25 20:46:35 +010056tgid = args.tgid
Brendan Gregga32fbaf2016-02-07 12:14:37 -080057debug = 0
58
Brendan Gregga32fbaf2016-02-07 12:14:37 -080059# define BPF program
60bpf_text = """
61#include <uapi/linux/ptrace.h>
62#include <linux/fs.h>
Mark Drayton266d6f62016-05-24 07:01:01 -070063#include <linux/sched.h>
Brendan Gregga32fbaf2016-02-07 12:14:37 -080064
Mark Drayton266d6f62016-05-24 07:01:01 -070065enum trace_mode {
66 MODE_READ,
67 MODE_WRITE
68};
Brendan Gregga32fbaf2016-02-07 12:14:37 -080069
70struct val_t {
71 u32 sz;
72 u64 ts;
Mark Drayton74347312016-08-25 20:46:35 +010073 u32 name_len;
74 // de->d_name.name may point to de->d_iname so limit len accordingly
Brendan Gregga32fbaf2016-02-07 12:14:37 -080075 char name[DNAME_INLINE_LEN];
Mark Drayton266d6f62016-05-24 07:01:01 -070076 char comm[TASK_COMM_LEN];
77};
78
79struct data_t {
80 enum trace_mode mode;
81 u32 pid;
82 u32 sz;
83 u64 delta_us;
Mark Drayton74347312016-08-25 20:46:35 +010084 u32 name_len;
Mark Drayton266d6f62016-05-24 07:01:01 -070085 char name[DNAME_INLINE_LEN];
86 char comm[TASK_COMM_LEN];
Brendan Gregga32fbaf2016-02-07 12:14:37 -080087};
88
89BPF_HASH(entryinfo, pid_t, struct val_t);
Mark Drayton266d6f62016-05-24 07:01:01 -070090BPF_PERF_OUTPUT(events);
Brendan Gregga32fbaf2016-02-07 12:14:37 -080091
92// store timestamp and size on entry
93static int trace_rw_entry(struct pt_regs *ctx, struct file *file,
94 char __user *buf, size_t count)
95{
Mark Drayton74347312016-08-25 20:46:35 +010096 u32 tgid = bpf_get_current_pid_tgid() >> 32;
97 if (TGID_FILTER)
Brendan Gregga32fbaf2016-02-07 12:14:37 -080098 return 0;
99
Mark Drayton74347312016-08-25 20:46:35 +0100100 u32 pid = bpf_get_current_pid_tgid();
101
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800102 // skip I/O lacking a filename
103 struct dentry *de = file->f_path.dentry;
Mark Drayton74347312016-08-25 20:46:35 +0100104 int mode = file->f_inode->i_mode;
105 if (de->d_name.len == 0 || TYPE_FILTER)
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800106 return 0;
107
108 // store size and timestamp by pid
109 struct val_t val = {};
110 val.sz = count;
111 val.ts = bpf_ktime_get_ns();
Mark Drayton74347312016-08-25 20:46:35 +0100112
Paul Chaignonf86f7e82018-06-14 02:20:03 +0200113 struct qstr d_name = de->d_name;
114 val.name_len = d_name.len;
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500115 bpf_probe_read_kernel(&val.name, sizeof(val.name), d_name.name);
Mark Drayton266d6f62016-05-24 07:01:01 -0700116 bpf_get_current_comm(&val.comm, sizeof(val.comm));
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800117 entryinfo.update(&pid, &val);
118
119 return 0;
120}
121
122int trace_read_entry(struct pt_regs *ctx, struct file *file,
123 char __user *buf, size_t count)
124{
125 // skip non-sync I/O; see kernel code for __vfs_read()
126 if (!(file->f_op->read_iter))
127 return 0;
128 return trace_rw_entry(ctx, file, buf, count);
129}
130
131int trace_write_entry(struct pt_regs *ctx, struct file *file,
132 char __user *buf, size_t count)
133{
134 // skip non-sync I/O; see kernel code for __vfs_write()
135 if (!(file->f_op->write_iter))
136 return 0;
137 return trace_rw_entry(ctx, file, buf, count);
138}
139
140// output
141static int trace_rw_return(struct pt_regs *ctx, int type)
142{
143 struct val_t *valp;
144 u32 pid = bpf_get_current_pid_tgid();
145
146 valp = entryinfo.lookup(&pid);
147 if (valp == 0) {
148 // missed tracing issue or filtered
149 return 0;
150 }
151 u64 delta_us = (bpf_ktime_get_ns() - valp->ts) / 1000;
152 entryinfo.delete(&pid);
153 if (delta_us < MIN_US)
154 return 0;
155
Mark Drayton266d6f62016-05-24 07:01:01 -0700156 struct data_t data = {};
157 data.mode = type;
158 data.pid = pid;
159 data.sz = valp->sz;
160 data.delta_us = delta_us;
Mark Drayton74347312016-08-25 20:46:35 +0100161 data.name_len = valp->name_len;
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500162 bpf_probe_read_kernel(&data.name, sizeof(data.name), valp->name);
163 bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm);
Mark Drayton266d6f62016-05-24 07:01:01 -0700164 events.perf_submit(ctx, &data, sizeof(data));
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800165
166 return 0;
167}
168
169int trace_read_return(struct pt_regs *ctx)
170{
Mark Drayton266d6f62016-05-24 07:01:01 -0700171 return trace_rw_return(ctx, MODE_READ);
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800172}
173
174int trace_write_return(struct pt_regs *ctx)
175{
Mark Drayton266d6f62016-05-24 07:01:01 -0700176 return trace_rw_return(ctx, MODE_WRITE);
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800177}
178
179"""
180bpf_text = bpf_text.replace('MIN_US', str(min_ms * 1000))
Mark Drayton74347312016-08-25 20:46:35 +0100181if args.tgid:
182 bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % tgid)
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800183else:
Mark Drayton74347312016-08-25 20:46:35 +0100184 bpf_text = bpf_text.replace('TGID_FILTER', '0')
185if args.all_files:
186 bpf_text = bpf_text.replace('TYPE_FILTER', '0')
187else:
188 bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)')
189
Nathan Scottcf0792f2018-02-02 16:56:50 +1100190if debug or args.ebpf:
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800191 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100192 if args.ebpf:
193 exit()
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800194
195# initialize BPF
Nathan Scottcf0792f2018-02-02 16:56:50 +1100196b = BPF(text=bpf_text)
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800197
198# I'd rather trace these via new_sync_read/new_sync_write (which used to be
199# do_sync_read/do_sync_write), but those became static. So trace these from
200# the parent functions, at the cost of more overhead, instead.
201# Ultimately, we should be using [V]FS tracepoints.
He Zheb61e65f2020-08-12 13:04:35 +0800202try:
203 b.attach_kprobe(event="__vfs_read", fn_name="trace_read_entry")
204 b.attach_kretprobe(event="__vfs_read", fn_name="trace_read_return")
205except Exception:
206 print('Current kernel does not have __vfs_read, try vfs_read instead')
207 b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
208 b.attach_kretprobe(event="vfs_read", fn_name="trace_read_return")
Mark Drayton32a4fd32016-07-13 18:24:56 +0100209try:
210 b.attach_kprobe(event="__vfs_write", fn_name="trace_write_entry")
211 b.attach_kretprobe(event="__vfs_write", fn_name="trace_write_return")
Teng Qinaaca9762019-01-11 11:18:45 -0800212except Exception:
He Zheb61e65f2020-08-12 13:04:35 +0800213 print('Current kernel does not have __vfs_write, try vfs_write instead')
Mark Drayton32a4fd32016-07-13 18:24:56 +0100214 b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry")
215 b.attach_kretprobe(event="vfs_write", fn_name="trace_write_return")
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800216
Mark Drayton266d6f62016-05-24 07:01:01 -0700217mode_s = {
218 0: 'R',
219 1: 'W',
220}
221
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800222# header
223print("Tracing sync read/writes slower than %d ms" % min_ms)
Mark Drayton74347312016-08-25 20:46:35 +0100224print("%-8s %-14s %-6s %1s %-7s %7s %s" % ("TIME(s)", "COMM", "TID", "D",
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800225 "BYTES", "LAT(ms)", "FILENAME"))
226
Mark Drayton266d6f62016-05-24 07:01:01 -0700227start_ts = time.time()
Allan McAleavy4f690c22019-02-27 18:03:42 +0000228DNAME_INLINE_LEN = 32
Mark Drayton266d6f62016-05-24 07:01:01 -0700229def print_event(cpu, data, size):
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800230 event = b["events"].event(data)
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800231
Mark Drayton266d6f62016-05-24 07:01:01 -0700232 ms = float(event.delta_us) / 1000
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200233 name = event.name.decode('utf-8', 'replace')
Mark Drayton74347312016-08-25 20:46:35 +0100234 if event.name_len > DNAME_INLINE_LEN:
235 name = name[:-3] + "..."
Brendan Gregga32fbaf2016-02-07 12:14:37 -0800236
237 print("%-8.3f %-14.14s %-6s %1s %-7s %7.2f %s" % (
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200238 time.time() - start_ts, event.comm.decode('utf-8', 'replace'),
239 event.pid, mode_s[event.mode], event.sz, ms, name))
Mark Drayton266d6f62016-05-24 07:01:01 -0700240
Mark Drayton5f5687e2017-02-20 18:13:03 +0000241b["events"].open_perf_buffer(print_event, page_cnt=64)
Mark Drayton266d6f62016-05-24 07:01:01 -0700242while 1:
Jerome Marchand51671272018-12-19 01:57:24 +0100243 try:
244 b.perf_buffer_poll()
245 except KeyboardInterrupt:
246 exit()