152 lines
4.1 KiB
Python
Executable File
152 lines
4.1 KiB
Python
Executable File
#! /usr/bin/python3
|
|
# @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 <uapi/linux/ptrace.h>
|
|
|
|
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()
|