Jayendra Parmar
Jayendra Parmar

Reputation: 774

Failed to load eBPF program

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

Answers (1)

pchaigno
pchaigno

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

Related Questions