#!/usr/bin/env python # @lint-avoid-python-3-compatibility-imports # # vfsstat Count some VFS calls. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of counting multiple events as a stat tool. # # USAGE: vfsstat [-h] [-p PID] [interval] [count] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 14-Aug-2015 Brendan Gregg Created this. # 12-Oct-2022 Rocky Xing Added PID filter support. from __future__ import print_function from bcc import BPF from ctypes import c_int from time import sleep, strftime from sys import argv import argparse # arguments examples = """examples: ./vfsstat # count some VFS calls per second ./vfsstat -p 185 # trace PID 185 only ./vfsstat 2 5 # print 2 second summaries, 5 times """ parser = argparse.ArgumentParser( description="Count some VFS calls.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", default=1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) debug = 0 # load BPF program bpf_text = """ #include enum stat_types { S_READ = 1, S_WRITE, S_FSYNC, S_OPEN, S_CREATE, S_MAXSTAT }; BPF_ARRAY(stats, u64, S_MAXSTAT); static void stats_try_increment(int key) { PID_FILTER stats.atomic_increment(key); } """ bpf_text_kprobe = """ void do_read(struct pt_regs *ctx) { stats_try_increment(S_READ); } void do_write(struct pt_regs *ctx) { stats_try_increment(S_WRITE); } void do_fsync(struct pt_regs *ctx) { stats_try_increment(S_FSYNC); } void do_open(struct pt_regs *ctx) { stats_try_increment(S_OPEN); } void do_create(struct pt_regs *ctx) { stats_try_increment(S_CREATE); } """ bpf_text_kfunc = """ KFUNC_PROBE(vfs_read) { stats_try_increment(S_READ); return 0; } KFUNC_PROBE(vfs_write) { stats_try_increment(S_WRITE); return 0; } KFUNC_PROBE(vfs_fsync_range) { stats_try_increment(S_FSYNC); return 0; } KFUNC_PROBE(vfs_open) { stats_try_increment(S_OPEN); return 0; } KFUNC_PROBE(vfs_create) { stats_try_increment(S_CREATE); return 0; } """ is_support_kfunc = BPF.support_kfunc() if is_support_kfunc: bpf_text += bpf_text_kfunc else: bpf_text += bpf_text_kprobe if args.pid: bpf_text = bpf_text.replace('PID_FILTER', """ u32 pid = bpf_get_current_pid_tgid() >> 32; if (pid != %s) { return; } """ % args.pid) else: bpf_text = bpf_text.replace('PID_FILTER', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() b = BPF(text=bpf_text) if not is_support_kfunc: b.attach_kprobe(event="vfs_read", fn_name="do_read") b.attach_kprobe(event="vfs_write", fn_name="do_write") b.attach_kprobe(event="vfs_fsync_range", fn_name="do_fsync") b.attach_kprobe(event="vfs_open", fn_name="do_open") b.attach_kprobe(event="vfs_create", fn_name="do_create") # stat column labels and indexes stat_types = { "READ": 1, "WRITE": 2, "FSYNC": 3, "OPEN": 4, "CREATE": 5 } # header print("%-8s " % "TIME", end="") for stype in stat_types.keys(): print(" %8s" % (stype + "/s"), end="") idx = stat_types[stype] print("") # output exiting = 0 if args.interval else 1 while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print("%-8s: " % strftime("%H:%M:%S"), end="") # print each statistic as a column for stype in stat_types.keys(): idx = stat_types[stype] try: val = b["stats"][c_int(idx)].value / int(args.interval) print(" %8d" % val, end="") except: print(" %8d" % 0, end="") b["stats"].clear() print("") countdown -= 1 if exiting or countdown == 0: exit()