xref: /aosp_15_r20/external/bcc/tools/fileslower.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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# fileslower  Trace slow synchronous file reads and writes.
5*387f9dfdSAndroid Build Coastguard Worker#             For Linux, uses BCC, eBPF.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# USAGE: fileslower [-h] [-p PID] [-a] [min_ms]
8*387f9dfdSAndroid Build Coastguard Worker#
9*387f9dfdSAndroid Build Coastguard Worker# This script uses kernel dynamic tracing of synchronous reads and writes
10*387f9dfdSAndroid Build Coastguard Worker# at the VFS interface, to identify slow file reads and writes for any file
11*387f9dfdSAndroid Build Coastguard Worker# system.
12*387f9dfdSAndroid Build Coastguard Worker#
13*387f9dfdSAndroid Build Coastguard Worker# This works by tracing __vfs_read() and __vfs_write(), and filtering for
14*387f9dfdSAndroid Build Coastguard Worker# synchronous I/O (the path to new_sync_read() and new_sync_write()), and
15*387f9dfdSAndroid Build Coastguard Worker# for I/O with filenames. This approach provides a view of just two file
16*387f9dfdSAndroid Build Coastguard Worker# system request types. There are typically many others: asynchronous I/O,
17*387f9dfdSAndroid Build Coastguard Worker# directory operations, file handle operations, etc, that this tool does not
18*387f9dfdSAndroid Build Coastguard Worker# instrument.
19*387f9dfdSAndroid Build Coastguard Worker#
20*387f9dfdSAndroid Build Coastguard Worker# WARNING: This traces VFS reads and writes, which can be extremely frequent,
21*387f9dfdSAndroid Build Coastguard Worker# and so the overhead of this tool can become severe depending on the
22*387f9dfdSAndroid Build Coastguard Worker# workload.
23*387f9dfdSAndroid Build Coastguard Worker#
24*387f9dfdSAndroid Build Coastguard Worker# By default, a minimum millisecond threshold of 10 is used.
25*387f9dfdSAndroid Build Coastguard Worker#
26*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc.
27*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
28*387f9dfdSAndroid Build Coastguard Worker#
29*387f9dfdSAndroid Build Coastguard Worker# 06-Feb-2016   Brendan Gregg   Created this.
30*387f9dfdSAndroid Build Coastguard Worker
31*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
32*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
33*387f9dfdSAndroid Build Coastguard Workerimport argparse
34*387f9dfdSAndroid Build Coastguard Workerimport time
35*387f9dfdSAndroid Build Coastguard Worker
36*387f9dfdSAndroid Build Coastguard Worker# arguments
37*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
38*387f9dfdSAndroid Build Coastguard Worker    ./fileslower             # trace sync file I/O slower than 10 ms (default)
39*387f9dfdSAndroid Build Coastguard Worker    ./fileslower 1           # trace sync file I/O slower than 1 ms
40*387f9dfdSAndroid Build Coastguard Worker    ./fileslower -p 185      # trace PID 185 only
41*387f9dfdSAndroid Build Coastguard Worker"""
42*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
43*387f9dfdSAndroid Build Coastguard Worker    description="Trace slow synchronous file reads and writes",
44*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
45*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
46*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid",
47*387f9dfdSAndroid Build Coastguard Worker    help="trace this PID only")
48*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-a", "--all-files", action="store_true",
49*387f9dfdSAndroid Build Coastguard Worker    help="include non-regular file types (sockets, FIFOs, etc)")
50*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("min_ms", nargs="?", default='10',
51*387f9dfdSAndroid Build Coastguard Worker    help="minimum I/O duration to trace, in ms (default 10)")
52*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
53*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
54*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
55*387f9dfdSAndroid Build Coastguard Workermin_ms = int(args.min_ms)
56*387f9dfdSAndroid Build Coastguard Workertgid = args.tgid
57*387f9dfdSAndroid Build Coastguard Workerdebug = 0
58*387f9dfdSAndroid Build Coastguard Worker
59*387f9dfdSAndroid Build Coastguard Worker# define BPF program
60*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
61*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
62*387f9dfdSAndroid Build Coastguard Worker#include <linux/fs.h>
63*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h>
64*387f9dfdSAndroid Build Coastguard Worker
65*387f9dfdSAndroid Build Coastguard Workerenum trace_mode {
66*387f9dfdSAndroid Build Coastguard Worker    MODE_READ,
67*387f9dfdSAndroid Build Coastguard Worker    MODE_WRITE
68*387f9dfdSAndroid Build Coastguard Worker};
69*387f9dfdSAndroid Build Coastguard Worker
70*387f9dfdSAndroid Build Coastguard Workerstruct val_t {
71*387f9dfdSAndroid Build Coastguard Worker    u32 sz;
72*387f9dfdSAndroid Build Coastguard Worker    u64 ts;
73*387f9dfdSAndroid Build Coastguard Worker    u32 name_len;
74*387f9dfdSAndroid Build Coastguard Worker    // de->d_name.name may point to de->d_iname so limit len accordingly
75*387f9dfdSAndroid Build Coastguard Worker    char name[DNAME_INLINE_LEN];
76*387f9dfdSAndroid Build Coastguard Worker    char comm[TASK_COMM_LEN];
77*387f9dfdSAndroid Build Coastguard Worker};
78*387f9dfdSAndroid Build Coastguard Worker
79*387f9dfdSAndroid Build Coastguard Workerstruct data_t {
80*387f9dfdSAndroid Build Coastguard Worker    enum trace_mode mode;
81*387f9dfdSAndroid Build Coastguard Worker    u32 pid;
82*387f9dfdSAndroid Build Coastguard Worker    u32 sz;
83*387f9dfdSAndroid Build Coastguard Worker    u64 delta_us;
84*387f9dfdSAndroid Build Coastguard Worker    u32 name_len;
85*387f9dfdSAndroid Build Coastguard Worker    char name[DNAME_INLINE_LEN];
86*387f9dfdSAndroid Build Coastguard Worker    char comm[TASK_COMM_LEN];
87*387f9dfdSAndroid Build Coastguard Worker};
88*387f9dfdSAndroid Build Coastguard Worker
89*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(entryinfo, pid_t, struct val_t);
90*387f9dfdSAndroid Build Coastguard WorkerBPF_PERF_OUTPUT(events);
91*387f9dfdSAndroid Build Coastguard Worker
92*387f9dfdSAndroid Build Coastguard Worker// store timestamp and size on entry
93*387f9dfdSAndroid Build Coastguard Workerstatic int trace_rw_entry(struct pt_regs *ctx, struct file *file,
94*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count)
95*387f9dfdSAndroid Build Coastguard Worker{
96*387f9dfdSAndroid Build Coastguard Worker    u32 tgid = bpf_get_current_pid_tgid() >> 32;
97*387f9dfdSAndroid Build Coastguard Worker    if (TGID_FILTER)
98*387f9dfdSAndroid Build Coastguard Worker        return 0;
99*387f9dfdSAndroid Build Coastguard Worker
100*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
101*387f9dfdSAndroid Build Coastguard Worker
102*387f9dfdSAndroid Build Coastguard Worker    // skip I/O lacking a filename
103*387f9dfdSAndroid Build Coastguard Worker    struct dentry *de = file->f_path.dentry;
104*387f9dfdSAndroid Build Coastguard Worker    int mode = file->f_inode->i_mode;
105*387f9dfdSAndroid Build Coastguard Worker    if (de->d_name.len == 0 || TYPE_FILTER)
106*387f9dfdSAndroid Build Coastguard Worker        return 0;
107*387f9dfdSAndroid Build Coastguard Worker
108*387f9dfdSAndroid Build Coastguard Worker    // store size and timestamp by pid
109*387f9dfdSAndroid Build Coastguard Worker    struct val_t val = {};
110*387f9dfdSAndroid Build Coastguard Worker    val.sz = count;
111*387f9dfdSAndroid Build Coastguard Worker    val.ts = bpf_ktime_get_ns();
112*387f9dfdSAndroid Build Coastguard Worker
113*387f9dfdSAndroid Build Coastguard Worker    struct qstr d_name = de->d_name;
114*387f9dfdSAndroid Build Coastguard Worker    val.name_len = d_name.len;
115*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_kernel(&val.name, sizeof(val.name), d_name.name);
116*387f9dfdSAndroid Build Coastguard Worker    bpf_get_current_comm(&val.comm, sizeof(val.comm));
117*387f9dfdSAndroid Build Coastguard Worker    entryinfo.update(&pid, &val);
118*387f9dfdSAndroid Build Coastguard Worker
119*387f9dfdSAndroid Build Coastguard Worker    return 0;
120*387f9dfdSAndroid Build Coastguard Worker}
121*387f9dfdSAndroid Build Coastguard Worker
122*387f9dfdSAndroid Build Coastguard Workerint trace_read_entry(struct pt_regs *ctx, struct file *file,
123*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count)
124*387f9dfdSAndroid Build Coastguard Worker{
125*387f9dfdSAndroid Build Coastguard Worker    // skip non-sync I/O; see kernel code for __vfs_read()
126*387f9dfdSAndroid Build Coastguard Worker    if (!(file->f_op->read_iter))
127*387f9dfdSAndroid Build Coastguard Worker        return 0;
128*387f9dfdSAndroid Build Coastguard Worker    return trace_rw_entry(ctx, file, buf, count);
129*387f9dfdSAndroid Build Coastguard Worker}
130*387f9dfdSAndroid Build Coastguard Worker
131*387f9dfdSAndroid Build Coastguard Workerint trace_write_entry(struct pt_regs *ctx, struct file *file,
132*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count)
133*387f9dfdSAndroid Build Coastguard Worker{
134*387f9dfdSAndroid Build Coastguard Worker    // skip non-sync I/O; see kernel code for __vfs_write()
135*387f9dfdSAndroid Build Coastguard Worker    if (!(file->f_op->write_iter))
136*387f9dfdSAndroid Build Coastguard Worker        return 0;
137*387f9dfdSAndroid Build Coastguard Worker    return trace_rw_entry(ctx, file, buf, count);
138*387f9dfdSAndroid Build Coastguard Worker}
139*387f9dfdSAndroid Build Coastguard Worker
140*387f9dfdSAndroid Build Coastguard Worker// output
141*387f9dfdSAndroid Build Coastguard Workerstatic int trace_rw_return(struct pt_regs *ctx, int type)
142*387f9dfdSAndroid Build Coastguard Worker{
143*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp;
144*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
145*387f9dfdSAndroid Build Coastguard Worker
146*387f9dfdSAndroid Build Coastguard Worker    valp = entryinfo.lookup(&pid);
147*387f9dfdSAndroid Build Coastguard Worker    if (valp == 0) {
148*387f9dfdSAndroid Build Coastguard Worker        // missed tracing issue or filtered
149*387f9dfdSAndroid Build Coastguard Worker        return 0;
150*387f9dfdSAndroid Build Coastguard Worker    }
151*387f9dfdSAndroid Build Coastguard Worker    u64 delta_us = (bpf_ktime_get_ns() - valp->ts) / 1000;
152*387f9dfdSAndroid Build Coastguard Worker    entryinfo.delete(&pid);
153*387f9dfdSAndroid Build Coastguard Worker    if (delta_us < MIN_US)
154*387f9dfdSAndroid Build Coastguard Worker        return 0;
155*387f9dfdSAndroid Build Coastguard Worker
156*387f9dfdSAndroid Build Coastguard Worker    struct data_t data = {};
157*387f9dfdSAndroid Build Coastguard Worker    data.mode = type;
158*387f9dfdSAndroid Build Coastguard Worker    data.pid = pid;
159*387f9dfdSAndroid Build Coastguard Worker    data.sz = valp->sz;
160*387f9dfdSAndroid Build Coastguard Worker    data.delta_us = delta_us;
161*387f9dfdSAndroid Build Coastguard Worker    data.name_len = valp->name_len;
162*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_kernel(&data.name, sizeof(data.name), valp->name);
163*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm);
164*387f9dfdSAndroid Build Coastguard Worker    events.perf_submit(ctx, &data, sizeof(data));
165*387f9dfdSAndroid Build Coastguard Worker
166*387f9dfdSAndroid Build Coastguard Worker    return 0;
167*387f9dfdSAndroid Build Coastguard Worker}
168*387f9dfdSAndroid Build Coastguard Worker
169*387f9dfdSAndroid Build Coastguard Workerint trace_read_return(struct pt_regs *ctx)
170*387f9dfdSAndroid Build Coastguard Worker{
171*387f9dfdSAndroid Build Coastguard Worker    return trace_rw_return(ctx, MODE_READ);
172*387f9dfdSAndroid Build Coastguard Worker}
173*387f9dfdSAndroid Build Coastguard Worker
174*387f9dfdSAndroid Build Coastguard Workerint trace_write_return(struct pt_regs *ctx)
175*387f9dfdSAndroid Build Coastguard Worker{
176*387f9dfdSAndroid Build Coastguard Worker    return trace_rw_return(ctx, MODE_WRITE);
177*387f9dfdSAndroid Build Coastguard Worker}
178*387f9dfdSAndroid Build Coastguard Worker
179*387f9dfdSAndroid Build Coastguard Worker"""
180*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('MIN_US', str(min_ms * 1000))
181*387f9dfdSAndroid Build Coastguard Workerif args.tgid:
182*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % tgid)
183*387f9dfdSAndroid Build Coastguard Workerelse:
184*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TGID_FILTER', '0')
185*387f9dfdSAndroid Build Coastguard Workerif args.all_files:
186*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TYPE_FILTER', '0')
187*387f9dfdSAndroid Build Coastguard Workerelse:
188*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)')
189*387f9dfdSAndroid Build Coastguard Worker
190*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf:
191*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
192*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
193*387f9dfdSAndroid Build Coastguard Worker        exit()
194*387f9dfdSAndroid Build Coastguard Worker
195*387f9dfdSAndroid Build Coastguard Worker# initialize BPF
196*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
197*387f9dfdSAndroid Build Coastguard Worker
198*387f9dfdSAndroid Build Coastguard Worker# I'd rather trace these via new_sync_read/new_sync_write (which used to be
199*387f9dfdSAndroid Build Coastguard Worker# do_sync_read/do_sync_write), but those became static. So trace these from
200*387f9dfdSAndroid Build Coastguard Worker# the parent functions, at the cost of more overhead, instead.
201*387f9dfdSAndroid Build Coastguard Worker# Ultimately, we should be using [V]FS tracepoints.
202*387f9dfdSAndroid Build Coastguard Workertry:
203*387f9dfdSAndroid Build Coastguard Worker    b.attach_kprobe(event="__vfs_read", fn_name="trace_read_entry")
204*387f9dfdSAndroid Build Coastguard Worker    b.attach_kretprobe(event="__vfs_read", fn_name="trace_read_return")
205*387f9dfdSAndroid Build Coastguard Workerexcept Exception:
206*387f9dfdSAndroid Build Coastguard Worker    print('Current kernel does not have __vfs_read, try vfs_read instead')
207*387f9dfdSAndroid Build Coastguard Worker    b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
208*387f9dfdSAndroid Build Coastguard Worker    b.attach_kretprobe(event="vfs_read", fn_name="trace_read_return")
209*387f9dfdSAndroid Build Coastguard Workertry:
210*387f9dfdSAndroid Build Coastguard Worker    b.attach_kprobe(event="__vfs_write", fn_name="trace_write_entry")
211*387f9dfdSAndroid Build Coastguard Worker    b.attach_kretprobe(event="__vfs_write", fn_name="trace_write_return")
212*387f9dfdSAndroid Build Coastguard Workerexcept Exception:
213*387f9dfdSAndroid Build Coastguard Worker    print('Current kernel does not have __vfs_write, try vfs_write instead')
214*387f9dfdSAndroid Build Coastguard Worker    b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry")
215*387f9dfdSAndroid Build Coastguard Worker    b.attach_kretprobe(event="vfs_write", fn_name="trace_write_return")
216*387f9dfdSAndroid Build Coastguard Worker
217*387f9dfdSAndroid Build Coastguard Workermode_s = {
218*387f9dfdSAndroid Build Coastguard Worker    0: 'R',
219*387f9dfdSAndroid Build Coastguard Worker    1: 'W',
220*387f9dfdSAndroid Build Coastguard Worker}
221*387f9dfdSAndroid Build Coastguard Worker
222*387f9dfdSAndroid Build Coastguard Worker# header
223*387f9dfdSAndroid Build Coastguard Workerprint("Tracing sync read/writes slower than %d ms" % min_ms)
224*387f9dfdSAndroid Build Coastguard Workerprint("%-8s %-14s %-6s %1s %-7s %7s %s" % ("TIME(s)", "COMM", "TID", "D",
225*387f9dfdSAndroid Build Coastguard Worker    "BYTES", "LAT(ms)", "FILENAME"))
226*387f9dfdSAndroid Build Coastguard Worker
227*387f9dfdSAndroid Build Coastguard Workerstart_ts = time.time()
228*387f9dfdSAndroid Build Coastguard WorkerDNAME_INLINE_LEN = 32
229*387f9dfdSAndroid Build Coastguard Workerdef print_event(cpu, data, size):
230*387f9dfdSAndroid Build Coastguard Worker    event = b["events"].event(data)
231*387f9dfdSAndroid Build Coastguard Worker
232*387f9dfdSAndroid Build Coastguard Worker    ms = float(event.delta_us) / 1000
233*387f9dfdSAndroid Build Coastguard Worker    name = event.name.decode('utf-8', 'replace')
234*387f9dfdSAndroid Build Coastguard Worker    if event.name_len > DNAME_INLINE_LEN:
235*387f9dfdSAndroid Build Coastguard Worker        name = name[:-3] + "..."
236*387f9dfdSAndroid Build Coastguard Worker
237*387f9dfdSAndroid Build Coastguard Worker    print("%-8.3f %-14.14s %-6s %1s %-7s %7.2f %s" % (
238*387f9dfdSAndroid Build Coastguard Worker        time.time() - start_ts, event.comm.decode('utf-8', 'replace'),
239*387f9dfdSAndroid Build Coastguard Worker        event.pid, mode_s[event.mode], event.sz, ms, name))
240*387f9dfdSAndroid Build Coastguard Worker
241*387f9dfdSAndroid Build Coastguard Workerb["events"].open_perf_buffer(print_event, page_cnt=64)
242*387f9dfdSAndroid Build Coastguard Workerwhile 1:
243*387f9dfdSAndroid Build Coastguard Worker    try:
244*387f9dfdSAndroid Build Coastguard Worker        b.perf_buffer_poll()
245*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
246*387f9dfdSAndroid Build Coastguard Worker        exit()
247