xref: /aosp_15_r20/external/bcc/tools/vfsstat.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1#!/usr/bin/env python
2# @lint-avoid-python-3-compatibility-imports
3#
4# vfsstat   Count some VFS calls.
5#           For Linux, uses BCC, eBPF. Embedded C.
6#
7# Written as a basic example of counting multiple events as a stat tool.
8#
9# USAGE: vfsstat [-h] [-p PID] [interval] [count]
10#
11# Copyright (c) 2015 Brendan Gregg.
12# Licensed under the Apache License, Version 2.0 (the "License")
13#
14# 14-Aug-2015   Brendan Gregg   Created this.
15# 12-Oct-2022   Rocky Xing      Added PID filter support.
16
17from __future__ import print_function
18from bcc import BPF
19from ctypes import c_int
20from time import sleep, strftime
21from sys import argv
22import argparse
23
24# arguments
25examples = """examples:
26    ./vfsstat             # count some VFS calls per second
27    ./vfsstat -p 185      # trace PID 185 only
28    ./vfsstat 2 5         # print 2 second summaries, 5 times
29"""
30parser = argparse.ArgumentParser(
31    description="Count some VFS calls.",
32    formatter_class=argparse.RawDescriptionHelpFormatter,
33    epilog=examples)
34parser.add_argument("-p", "--pid",
35    help="trace this PID only")
36parser.add_argument("interval", nargs="?", default=1,
37    help="output interval, in seconds")
38parser.add_argument("count", nargs="?", default=99999999,
39    help="number of outputs")
40parser.add_argument("--ebpf", action="store_true",
41    help=argparse.SUPPRESS)
42
43args = parser.parse_args()
44countdown = int(args.count)
45debug = 0
46
47# load BPF program
48bpf_text = """
49#include <uapi/linux/ptrace.h>
50
51enum stat_types {
52    S_READ = 1,
53    S_WRITE,
54    S_FSYNC,
55    S_OPEN,
56    S_CREATE,
57    S_MAXSTAT
58};
59
60BPF_ARRAY(stats, u64, S_MAXSTAT);
61
62static void stats_try_increment(int key) {
63    PID_FILTER
64    stats.atomic_increment(key);
65}
66"""
67
68bpf_text_kprobe = """
69void do_read(struct pt_regs *ctx) { stats_try_increment(S_READ); }
70void do_write(struct pt_regs *ctx) { stats_try_increment(S_WRITE); }
71void do_fsync(struct pt_regs *ctx) { stats_try_increment(S_FSYNC); }
72void do_open(struct pt_regs *ctx) { stats_try_increment(S_OPEN); }
73void do_create(struct pt_regs *ctx) { stats_try_increment(S_CREATE); }
74"""
75
76bpf_text_kfunc = """
77KFUNC_PROBE(vfs_read)         { stats_try_increment(S_READ); return 0; }
78KFUNC_PROBE(vfs_write)        { stats_try_increment(S_WRITE); return 0; }
79KFUNC_PROBE(vfs_fsync_range)  { stats_try_increment(S_FSYNC); return 0; }
80KFUNC_PROBE(vfs_open)         { stats_try_increment(S_OPEN); return 0; }
81KFUNC_PROBE(vfs_create)       { stats_try_increment(S_CREATE); return 0; }
82"""
83
84is_support_kfunc = BPF.support_kfunc()
85if is_support_kfunc:
86    bpf_text += bpf_text_kfunc
87else:
88    bpf_text += bpf_text_kprobe
89
90if args.pid:
91    bpf_text = bpf_text.replace('PID_FILTER', """
92    u32 pid = bpf_get_current_pid_tgid() >> 32;
93    if (pid != %s) {
94        return;
95    }
96    """ % args.pid)
97else:
98    bpf_text = bpf_text.replace('PID_FILTER', '')
99
100if debug or args.ebpf:
101    print(bpf_text)
102    if args.ebpf:
103        exit()
104
105b = BPF(text=bpf_text)
106if not is_support_kfunc:
107    b.attach_kprobe(event="vfs_read",         fn_name="do_read")
108    b.attach_kprobe(event="vfs_write",        fn_name="do_write")
109    b.attach_kprobe(event="vfs_fsync_range",  fn_name="do_fsync")
110    b.attach_kprobe(event="vfs_open",         fn_name="do_open")
111    b.attach_kprobe(event="vfs_create",       fn_name="do_create")
112
113# stat column labels and indexes
114stat_types = {
115    "READ": 1,
116    "WRITE": 2,
117    "FSYNC": 3,
118    "OPEN": 4,
119    "CREATE": 5
120}
121
122# header
123print("%-8s  " % "TIME", end="")
124for stype in stat_types.keys():
125    print(" %8s" % (stype + "/s"), end="")
126    idx = stat_types[stype]
127print("")
128
129# output
130exiting = 0 if args.interval else 1
131while (1):
132    try:
133        sleep(int(args.interval))
134    except KeyboardInterrupt:
135        exiting = 1
136
137    print("%-8s: " % strftime("%H:%M:%S"), end="")
138    # print each statistic as a column
139    for stype in stat_types.keys():
140        idx = stat_types[stype]
141        try:
142            val = b["stats"][c_int(idx)].value / int(args.interval)
143            print(" %8d" % val, end="")
144        except:
145            print(" %8d" % 0, end="")
146    b["stats"].clear()
147    print("")
148
149    countdown -= 1
150    if exiting or countdown == 0:
151        exit()
152