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