xref: /aosp_15_r20/external/bcc/tools/filetop.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# filetop  file reads and writes by process.
5*387f9dfdSAndroid Build Coastguard Worker#          For Linux, uses BCC, eBPF.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# USAGE: filetop.py [-h] [-C] [-r MAXROWS] [interval] [count]
8*387f9dfdSAndroid Build Coastguard Worker#
9*387f9dfdSAndroid Build Coastguard Worker# This uses in-kernel eBPF maps to store per process summaries for efficiency.
10*387f9dfdSAndroid Build Coastguard Worker#
11*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc.
12*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
13*387f9dfdSAndroid Build Coastguard Worker#
14*387f9dfdSAndroid Build Coastguard Worker# 06-Feb-2016   Brendan Gregg   Created this.
15*387f9dfdSAndroid Build Coastguard Worker
16*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
17*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
18*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
19*387f9dfdSAndroid Build Coastguard Workerimport argparse
20*387f9dfdSAndroid Build Coastguard Workerfrom subprocess import call
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard Worker# arguments
23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
24*387f9dfdSAndroid Build Coastguard Worker    ./filetop            # file I/O top, 1 second refresh
25*387f9dfdSAndroid Build Coastguard Worker    ./filetop -C         # don't clear the screen
26*387f9dfdSAndroid Build Coastguard Worker    ./filetop -p 181     # PID 181 only
27*387f9dfdSAndroid Build Coastguard Worker    ./filetop 5          # 5 second summaries
28*387f9dfdSAndroid Build Coastguard Worker    ./filetop 5 10       # 5 second summaries, 10 times only
29*387f9dfdSAndroid Build Coastguard Worker"""
30*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
31*387f9dfdSAndroid Build Coastguard Worker    description="File reads and writes by process",
32*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
33*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
34*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-a", "--all-files", action="store_true",
35*387f9dfdSAndroid Build Coastguard Worker    help="include non-regular file types (sockets, FIFOs, etc)")
36*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--noclear", action="store_true",
37*387f9dfdSAndroid Build Coastguard Worker    help="don't clear the screen")
38*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-r", "--maxrows", default=20,
39*387f9dfdSAndroid Build Coastguard Worker    help="maximum rows to print, default 20")
40*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-s", "--sort", default="all",
41*387f9dfdSAndroid Build Coastguard Worker    choices=["all", "reads", "writes", "rbytes", "wbytes"],
42*387f9dfdSAndroid Build Coastguard Worker    help="sort column, default all")
43*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid",
44*387f9dfdSAndroid Build Coastguard Worker    help="trace this PID only")
45*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=1,
46*387f9dfdSAndroid Build Coastguard Worker    help="output interval, in seconds")
47*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999,
48*387f9dfdSAndroid Build Coastguard Worker    help="number of outputs")
49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
50*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
51*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
52*387f9dfdSAndroid Build Coastguard Workerinterval = int(args.interval)
53*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count)
54*387f9dfdSAndroid Build Coastguard Workermaxrows = int(args.maxrows)
55*387f9dfdSAndroid Build Coastguard Workerclear = not int(args.noclear)
56*387f9dfdSAndroid Build Coastguard Workerdebug = 0
57*387f9dfdSAndroid Build Coastguard Worker
58*387f9dfdSAndroid Build Coastguard Worker# linux stats
59*387f9dfdSAndroid Build Coastguard Workerloadavg = "/proc/loadavg"
60*387f9dfdSAndroid Build Coastguard Worker
61*387f9dfdSAndroid Build Coastguard Worker# define BPF program
62*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
63*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
64*387f9dfdSAndroid Build Coastguard Worker#include <linux/blkdev.h>
65*387f9dfdSAndroid Build Coastguard Worker
66*387f9dfdSAndroid Build Coastguard Worker// the key for the output summary
67*387f9dfdSAndroid Build Coastguard Workerstruct info_t {
68*387f9dfdSAndroid Build Coastguard Worker    unsigned long inode;
69*387f9dfdSAndroid Build Coastguard Worker    dev_t dev;
70*387f9dfdSAndroid Build Coastguard Worker    dev_t rdev;
71*387f9dfdSAndroid Build Coastguard Worker    u32 pid;
72*387f9dfdSAndroid Build Coastguard Worker    u32 name_len;
73*387f9dfdSAndroid Build Coastguard Worker    char comm[TASK_COMM_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 type;
77*387f9dfdSAndroid Build Coastguard Worker};
78*387f9dfdSAndroid Build Coastguard Worker
79*387f9dfdSAndroid Build Coastguard Worker// the value of the output summary
80*387f9dfdSAndroid Build Coastguard Workerstruct val_t {
81*387f9dfdSAndroid Build Coastguard Worker    u64 reads;
82*387f9dfdSAndroid Build Coastguard Worker    u64 writes;
83*387f9dfdSAndroid Build Coastguard Worker    u64 rbytes;
84*387f9dfdSAndroid Build Coastguard Worker    u64 wbytes;
85*387f9dfdSAndroid Build Coastguard Worker};
86*387f9dfdSAndroid Build Coastguard Worker
87*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(counts, struct info_t, struct val_t);
88*387f9dfdSAndroid Build Coastguard Worker
89*387f9dfdSAndroid Build Coastguard Workerstatic int do_entry(struct pt_regs *ctx, struct file *file,
90*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count, int is_read)
91*387f9dfdSAndroid Build Coastguard Worker{
92*387f9dfdSAndroid Build Coastguard Worker    u32 tgid = bpf_get_current_pid_tgid() >> 32;
93*387f9dfdSAndroid Build Coastguard Worker    if (TGID_FILTER)
94*387f9dfdSAndroid Build Coastguard Worker        return 0;
95*387f9dfdSAndroid Build Coastguard Worker
96*387f9dfdSAndroid Build Coastguard Worker    u32 pid = bpf_get_current_pid_tgid();
97*387f9dfdSAndroid Build Coastguard Worker
98*387f9dfdSAndroid Build Coastguard Worker    // skip I/O lacking a filename
99*387f9dfdSAndroid Build Coastguard Worker    struct dentry *de = file->f_path.dentry;
100*387f9dfdSAndroid Build Coastguard Worker    int mode = file->f_inode->i_mode;
101*387f9dfdSAndroid Build Coastguard Worker    struct qstr d_name = de->d_name;
102*387f9dfdSAndroid Build Coastguard Worker    if (d_name.len == 0 || TYPE_FILTER)
103*387f9dfdSAndroid Build Coastguard Worker        return 0;
104*387f9dfdSAndroid Build Coastguard Worker
105*387f9dfdSAndroid Build Coastguard Worker    // store counts and sizes by pid & file
106*387f9dfdSAndroid Build Coastguard Worker    struct info_t info = {
107*387f9dfdSAndroid Build Coastguard Worker        .pid = pid,
108*387f9dfdSAndroid Build Coastguard Worker        .inode = file->f_inode->i_ino,
109*387f9dfdSAndroid Build Coastguard Worker        .dev = file->f_inode->i_sb->s_dev,
110*387f9dfdSAndroid Build Coastguard Worker        .rdev = file->f_inode->i_rdev,
111*387f9dfdSAndroid Build Coastguard Worker    };
112*387f9dfdSAndroid Build Coastguard Worker    bpf_get_current_comm(&info.comm, sizeof(info.comm));
113*387f9dfdSAndroid Build Coastguard Worker    info.name_len = d_name.len;
114*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_kernel(&info.name, sizeof(info.name), d_name.name);
115*387f9dfdSAndroid Build Coastguard Worker    if (S_ISREG(mode)) {
116*387f9dfdSAndroid Build Coastguard Worker        info.type = 'R';
117*387f9dfdSAndroid Build Coastguard Worker    } else if (S_ISSOCK(mode)) {
118*387f9dfdSAndroid Build Coastguard Worker        info.type = 'S';
119*387f9dfdSAndroid Build Coastguard Worker    } else {
120*387f9dfdSAndroid Build Coastguard Worker        info.type = 'O';
121*387f9dfdSAndroid Build Coastguard Worker    }
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
124*387f9dfdSAndroid Build Coastguard Worker    valp = counts.lookup_or_try_init(&info, &zero);
125*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
126*387f9dfdSAndroid Build Coastguard Worker        if (is_read) {
127*387f9dfdSAndroid Build Coastguard Worker            valp->reads++;
128*387f9dfdSAndroid Build Coastguard Worker            valp->rbytes += count;
129*387f9dfdSAndroid Build Coastguard Worker        } else {
130*387f9dfdSAndroid Build Coastguard Worker            valp->writes++;
131*387f9dfdSAndroid Build Coastguard Worker            valp->wbytes += count;
132*387f9dfdSAndroid Build Coastguard Worker        }
133*387f9dfdSAndroid Build Coastguard Worker    }
134*387f9dfdSAndroid Build Coastguard Worker
135*387f9dfdSAndroid Build Coastguard Worker    return 0;
136*387f9dfdSAndroid Build Coastguard Worker}
137*387f9dfdSAndroid Build Coastguard Worker
138*387f9dfdSAndroid Build Coastguard Workerint trace_read_entry(struct pt_regs *ctx, struct file *file,
139*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count)
140*387f9dfdSAndroid Build Coastguard Worker{
141*387f9dfdSAndroid Build Coastguard Worker    return do_entry(ctx, file, buf, count, 1);
142*387f9dfdSAndroid Build Coastguard Worker}
143*387f9dfdSAndroid Build Coastguard Worker
144*387f9dfdSAndroid Build Coastguard Workerint trace_write_entry(struct pt_regs *ctx, struct file *file,
145*387f9dfdSAndroid Build Coastguard Worker    char __user *buf, size_t count)
146*387f9dfdSAndroid Build Coastguard Worker{
147*387f9dfdSAndroid Build Coastguard Worker    return do_entry(ctx, file, buf, count, 0);
148*387f9dfdSAndroid Build Coastguard Worker}
149*387f9dfdSAndroid Build Coastguard Worker
150*387f9dfdSAndroid Build Coastguard Worker"""
151*387f9dfdSAndroid Build Coastguard Workerif args.tgid:
152*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid)
153*387f9dfdSAndroid Build Coastguard Workerelse:
154*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TGID_FILTER', '0')
155*387f9dfdSAndroid Build Coastguard Workerif args.all_files:
156*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TYPE_FILTER', '0')
157*387f9dfdSAndroid Build Coastguard Workerelse:
158*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)')
159*387f9dfdSAndroid Build Coastguard Worker
160*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf:
161*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
162*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
163*387f9dfdSAndroid Build Coastguard Worker        exit()
164*387f9dfdSAndroid Build Coastguard Worker
165*387f9dfdSAndroid Build Coastguard Worker# initialize BPF
166*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
167*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
168*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="vfs_write", fn_name="trace_write_entry")
169*387f9dfdSAndroid Build Coastguard Worker
170*387f9dfdSAndroid Build Coastguard WorkerDNAME_INLINE_LEN = 32  # linux/dcache.h
171*387f9dfdSAndroid Build Coastguard Worker
172*387f9dfdSAndroid Build Coastguard Workerprint('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
173*387f9dfdSAndroid Build Coastguard Worker
174*387f9dfdSAndroid Build Coastguard Workerdef sort_fn(counts):
175*387f9dfdSAndroid Build Coastguard Worker    if args.sort == "all":
176*387f9dfdSAndroid Build Coastguard Worker        return (counts[1].rbytes + counts[1].wbytes + counts[1].reads + counts[1].writes)
177*387f9dfdSAndroid Build Coastguard Worker    else:
178*387f9dfdSAndroid Build Coastguard Worker        return getattr(counts[1], args.sort)
179*387f9dfdSAndroid Build Coastguard Worker
180*387f9dfdSAndroid Build Coastguard Worker# output
181*387f9dfdSAndroid Build Coastguard Workerexiting = 0
182*387f9dfdSAndroid Build Coastguard Workerwhile 1:
183*387f9dfdSAndroid Build Coastguard Worker    try:
184*387f9dfdSAndroid Build Coastguard Worker        sleep(interval)
185*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
186*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
187*387f9dfdSAndroid Build Coastguard Worker
188*387f9dfdSAndroid Build Coastguard Worker    # header
189*387f9dfdSAndroid Build Coastguard Worker    if clear:
190*387f9dfdSAndroid Build Coastguard Worker        call("clear")
191*387f9dfdSAndroid Build Coastguard Worker    else:
192*387f9dfdSAndroid Build Coastguard Worker        print()
193*387f9dfdSAndroid Build Coastguard Worker    with open(loadavg) as stats:
194*387f9dfdSAndroid Build Coastguard Worker        print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read()))
195*387f9dfdSAndroid Build Coastguard Worker    print("%-7s %-16s %-6s %-6s %-7s %-7s %1s %s" % ("TID", "COMM",
196*387f9dfdSAndroid Build Coastguard Worker        "READS", "WRITES", "R_Kb", "W_Kb", "T", "FILE"))
197*387f9dfdSAndroid Build Coastguard Worker
198*387f9dfdSAndroid Build Coastguard Worker    # by-TID output
199*387f9dfdSAndroid Build Coastguard Worker    counts = b.get_table("counts")
200*387f9dfdSAndroid Build Coastguard Worker    line = 0
201*387f9dfdSAndroid Build Coastguard Worker    for k, v in reversed(sorted(counts.items(),
202*387f9dfdSAndroid Build Coastguard Worker                                key=sort_fn)):
203*387f9dfdSAndroid Build Coastguard Worker        name = k.name.decode('utf-8', 'replace')
204*387f9dfdSAndroid Build Coastguard Worker        if k.name_len > DNAME_INLINE_LEN:
205*387f9dfdSAndroid Build Coastguard Worker            name = name[:-3] + "..."
206*387f9dfdSAndroid Build Coastguard Worker
207*387f9dfdSAndroid Build Coastguard Worker        # print line
208*387f9dfdSAndroid Build Coastguard Worker        print("%-7d %-16s %-6d %-6d %-7d %-7d %1s %s" % (k.pid,
209*387f9dfdSAndroid Build Coastguard Worker            k.comm.decode('utf-8', 'replace'), v.reads, v.writes,
210*387f9dfdSAndroid Build Coastguard Worker            v.rbytes / 1024, v.wbytes / 1024,
211*387f9dfdSAndroid Build Coastguard Worker            k.type.decode('utf-8', 'replace'), name))
212*387f9dfdSAndroid Build Coastguard Worker
213*387f9dfdSAndroid Build Coastguard Worker        line += 1
214*387f9dfdSAndroid Build Coastguard Worker        if line >= maxrows:
215*387f9dfdSAndroid Build Coastguard Worker            break
216*387f9dfdSAndroid Build Coastguard Worker    counts.clear()
217*387f9dfdSAndroid Build Coastguard Worker
218*387f9dfdSAndroid Build Coastguard Worker    countdown -= 1
219*387f9dfdSAndroid Build Coastguard Worker    if exiting or countdown == 0:
220*387f9dfdSAndroid Build Coastguard Worker        print("Detaching...")
221*387f9dfdSAndroid Build Coastguard Worker        exit()
222