lcc/pylcc/pytool/fileslower.py (58 lines of code) (raw):

# -*- coding: utf-8 -*- # cython:language_level=2 """ ------------------------------------------------- File Name: fileslower Description : Author : liaozhaoyan date: 2021/11/6 ------------------------------------------------- Change Activity: 2021/11/6: ------------------------------------------------- """ __author__ = 'liaozhaoyan' import argparse import time from pylcc.lbcBase import ClbcBase, CeventThread bpfProg = r""" #include "lbc.h" #define S_IFMT 00170000 #define S_IFREG 0100000 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define TASK_COMM_LEN 16 #define DNAME_INLINE_LEN 32 enum trace_mode { MODE_READ, MODE_WRITE }; struct val_t { u32 sz; u64 ts; u32 name_len; // de->d_name.name may point to de->d_iname so limit len accordingly char name[DNAME_INLINE_LEN]; char comm[TASK_COMM_LEN]; }; struct data_t { int mode; u32 pid; u32 sz; u64 delta_us; u32 name_len; char name[DNAME_INLINE_LEN]; char comm[TASK_COMM_LEN]; }; LBC_HASH(entryinfo, u32, struct val_t, 1024); LBC_PERF_OUTPUT(events, struct data_t, 128); // store timestamp and size on entry static int trace_rw_entry(struct pt_regs *ctx, struct file *file, size_t count) { u32 tgid = bpf_get_current_pid_tgid() >> 32; if (TGID_FILTER) return 0; u32 pid = bpf_get_current_pid_tgid(); // skip I/O lacking a filename struct dentry *de = BPF_CORE_READ(file, f_path.dentry); //file->f_path.dentry; int mode = BPF_CORE_READ(file, f_inode, i_mode); //file->f_inode->i_mode; if (BPF_CORE_READ(de, d_name.len) == 0 || TYPE_FILTER) //(de->d_name.len == 0 || TYPE_FILTER) return 0; // store size and timestamp by pid struct val_t val = {}; val.sz = count; val.ts = bpf_ktime_get_ns(); struct qstr d_name = BPF_CORE_READ(de, d_name); //de->d_name; val.name_len = d_name.len; bpf_probe_read_kernel(&val.name, sizeof(val.name), d_name.name); bpf_get_current_comm(&val.comm, sizeof(val.comm)); bpf_map_update_elem(&entryinfo, &pid, &val, BPF_ANY); //entryinfo.update(&pid, &val); return 0; } SEC("kprobe/__vfs_read") int j__vfs_read(struct pt_regs *ctx) { // skip non-sync I/O; see kernel code for __vfs_read() struct file *file = (struct file *)PT_REGS_PARM1(ctx); size_t count = PT_REGS_PARM3(ctx); if (!BPF_CORE_READ(file, f_op, read_iter))// (!(file->f_op->read_iter)) return 0; return trace_rw_entry(ctx, file, count); } SEC("kprobe/__vfs_write") int j__vfs_write(struct pt_regs *ctx) { // skip non-sync I/O; see kernel code for __vfs_write() struct file *file = (struct file *)PT_REGS_PARM1(ctx); size_t count = PT_REGS_PARM3(ctx); if (!BPF_CORE_READ(file, f_op, read_iter))// (!(file->f_op->read_iter)) return 0; return trace_rw_entry(ctx, file, count); } // output static int trace_rw_return(struct pt_regs *ctx, int type) { struct val_t *valp; u32 pid = bpf_get_current_pid_tgid(); valp = bpf_map_lookup_elem(&entryinfo, &pid); // entryinfo.lookup(&pid); if (valp == 0) { // missed tracing issue or filtered return 0; } u64 delta_us = (bpf_ktime_get_ns() - valp->ts) / 1000; bpf_map_delete_elem(&entryinfo, &pid); // entryinfo.delete(&pid); if (delta_us < MIN_US) return 0; struct data_t data = {}; data.mode = type; data.pid = pid; data.sz = valp->sz; data.delta_us = delta_us; data.name_len = valp->name_len; bpf_probe_read_kernel(&data.name, sizeof(data.name), valp->name); bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); // events.perf_submit(ctx, &data, sizeof(data)); return 0; } SEC("kretprobe/__vfs_read") int r__vfs_read(struct pt_regs *ctx) { return trace_rw_return(ctx, MODE_READ); } SEC("kretprobe/__vfs_write") int r__vfs_write(struct pt_regs *ctx) { return trace_rw_return(ctx, MODE_WRITE); } char _license[] SEC("license") = "GPL"; """ mode_s = { 0: 'R', 1: 'W', } start_ts = time.time() class CfileSlower(ClbcBase): def __init__(self): super(CfileSlower, self).__init__("fileslower", bpf_str=bpfProg) print("Tracing sync read/writes slower than %d ms" % min_ms) print("%-8s %-14s %-6s %1s %-7s %7s %s" % ("TIME(s)", "COMM", "TID", "D", "BYTES", "LAT(ms)", "FILENAME")) def _cb(self, cpu, e): ms = float(e.delta_us) / 1000 name = e.name print("%-8.3f %-14.14s %-6s %1s %-7s %7.2f %s" % ( time.time() - start_ts, e.comm, e.pid, mode_s[e.mode], e.sz, ms, name)) def loop(self): CeventThread(self, 'events', self._cb) self.waitInterrupt() if __name__ == "__main__": # arguments examples = """examples: ./fileslower # trace sync file I/O slower than 10 ms (default) ./fileslower 1 # trace sync file I/O slower than 1 ms ./fileslower -p 185 # trace PID 185 only """ parser = argparse.ArgumentParser( description="Trace slow synchronous file reads and writes", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid", help="trace this PID only") parser.add_argument("-a", "--all-files", action="store_true", help="include non-regular file types (sockets, FIFOs, etc)") parser.add_argument("min_ms", nargs="?", default='10', help="minimum I/O duration to trace, in ms (default 10)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() min_ms = int(args.min_ms) tgid = args.tgid debug = 1 bpfProg = bpfProg.replace('MIN_US', str(min_ms * 1000)) if args.tgid: bpfProg = bpfProg.replace('TGID_FILTER', 'tgid != %d' % tgid) else: bpfProg = bpfProg.replace('TGID_FILTER', '0') if args.all_files: bpfProg = bpfProg.replace('TYPE_FILTER', '0') else: bpfProg = bpfProg.replace('TYPE_FILTER', '!S_ISREG(mode)') if debug or args.ebpf: if args.ebpf: exit() f = CfileSlower() f.loop() pass