我是 eBPF 新手,为了学习尝试调整现有
filetop
的概念,来自 https://github.com/iovisor/bcc/blob/master/tools/filetop.py。
这与
htop
类似,但适用于磁盘 io。对于文件名列
该工具仅打印文件名,而不打印绝对路径。为了打印绝对路径而不仅仅是文件名,我使用 dentry_path_raw
函数从 dentry 对象获取完整路径如何使用 dentry_path_raw()。
新的
ebpf
代码看起来像这样,而使用空间Python代码也是如此。
#include <uapi/linux/ptrace.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
// the key for the output summary
struct info_t {
unsigned long inode;
dev_t dev;
dev_t rdev;
u32 pid;
u32 name_len;
char comm[TASK_COMM_LEN];
// de->d_name.name may point to de->d_iname so limit len accordingly
char* name;
char type;
};
// the value of the output summary
struct val_t {
u64 reads;
u64 writes;
u64 rbytes;
u64 wbytes;
};
BPF_HASH(counts, struct info_t, struct val_t);
static int do_entry(struct pt_regs *ctx, struct file *file,
char __user *buf, size_t count, int is_read)
{
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 = file->f_path.dentry;
int mode = file->f_inode->i_mode;
struct qstr d_name = de->d_name;
if (d_name.len == 0 || TYPE_FILTER)
return 0;
// store counts and sizes by pid & file
struct info_t info = {
.pid = pid,
.inode = file->f_inode->i_ino,
.dev = file->f_inode->i_sb->s_dev,
.rdev = file->f_inode->i_rdev,
};
bpf_get_current_comm(&info.comm, sizeof(info.comm));
info.name_len = d_name.len;
//bpf_probe_read_kernel(&info.name, sizeof(info.name), d_name.name);
dentry_path_raw(de, info.name, 32);
if (S_ISREG(mode)) {
info.type = 'R';
} else if (S_ISSOCK(mode)) {
info.type = 'S';
} else {
info.type = 'O';
}
struct val_t *valp, zero = {};
valp = counts.lookup_or_try_init(&info, &zero);
if (valp) {
if (is_read) {
valp->reads++;
valp->rbytes += count;
} else {
valp->writes++;
valp->wbytes += count;
}
}
return 0;
}
int trace_read_entry(struct pt_regs *ctx, struct file *file,
char __user *buf, size_t count)
{
return do_entry(ctx, file, buf, count, 1);
}
int trace_write_entry(struct pt_regs *ctx, struct file *file,
char __user *buf, size_t count)
{
return do_entry(ctx, file, buf, count, 0);
}
实际代码可以在这里找到
我收到的上述代码的错误在这里
bpf: Failed to load program: Invalid argument
jump out of range from insn 38 to 148
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
Traceback (most recent call last):
File "/home/jayendra/data/ebpf/listen.py", line 170, in <module>
b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
File "/usr/lib/python3/dist-packages/bcc/__init__.py", line 851, in attach_kprobe
fn = self.load_func(fn_name, BPF.KPROBE)
File "/usr/lib/python3/dist-packages/bcc/__init__.py", line 526, in load_func
raise Exception("Failed to load BPF program %s: %s" %
Exception: Failed to load BPF program b'trace_read_entry': Invalid argument
您不能从 BPF 程序中调用任意内核函数。不过,您可以使用 BPF 助手
bpf_d_path
来解决这个问题:
long bpf_d_path(struct path *path, char *buf, u32 sz)
您可以在内核源代码中找到使用示例,但基本思想是在您的情况下按如下方式调用它:
ret = bpf_d_path(&file->f_path, info.name, 32);
if (ret < 0)
... handle error ...
请注意,此帮助程序需要 Linux v5.10。