Reputation: 774
I am eBPF newbie, in order to learn the concepts trying to tweak existing filetop
from https://github.com/iovisor/bcc/blob/master/tools/filetop.py.
This is similar to htop
but for disk-io. For the filename column
the tool only prints the filename and not the absolute path. In order to print the absolute path instead of just filename I am using dentry_path_raw
function to get the full path from dentry object How to use dentry_path_raw().
The new ebpf
code looks like this and the use-space python code is as it is.
#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);
}
Actual code can be found here
The error for above code I am getting is here
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
Upvotes: 0
Views: 739
Reputation: 13133
You cannot call arbitrary kernel functions from your BPF programs. You could however solve this with the BPF helper bpf_d_path
:
long bpf_d_path(struct path *path, char *buf, u32 sz)
You can find usage examples in the kernel sources, but the basic idea is to call it as follows in your case:
ret = bpf_d_path(&file->f_path, info.name, 32);
if (ret < 0)
... handle error ...
Note that this helper requires Linux v5.10.
Upvotes: 0