Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FD to pathname #237

Open
brendangregg opened this Issue Sep 20, 2015 · 7 comments

Comments

Projects
None yet
2 participants
@brendangregg
Copy link
Member

commented Sep 20, 2015

Would like a macro or function for mapping a file descriptor to a pathname. I was trying something like (this may be wrong):

#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fdtable.h>

int kprobe__vfs_fstat(struct pt_regs *ctx, unsigned int fd)
{
        struct file *file = (struct file *)&current->files->fdt[fd];
        bpf_trace_printk("fstat file %d %s\\n", fd, file->f_path.dentry->d_iname);
        return 0;
}

and got:

error: couldn't allocate output register for constraint 'r' at line 2148418561
@4ast

This comment has been minimized.

Copy link
Member

commented Sep 21, 2015

accessing 'current' like this is the problem. One really ugly hack would be to cat /proc/kallsyms |grep current_task and bpf_probe_read that binary address to get 'current', but it's too ugly... may be easier to remember fd->name association in bpf program attached to sys_open, then fstat can read it from the map ? I guess we need a helper for current anyway.

@brendangregg

This comment has been minimized.

Copy link
Member Author

commented Oct 6, 2015

David Smith has put some work into this problem for SystemTap: https://sourceware.org/bugzilla/show_bug.cgi?id=17920

@4ast

This comment has been minimized.

Copy link
Member

commented Oct 7, 2015

it's pretty much doing
current->files->fdt[fd]->f_path.dentry->d_iname
with few useless spin_locks and refcnts.
Few helper functions for pieces of this sequence could have been used to avoid exposing current() as a single helper, but current may be necessary for other cases, so yes, bpf_get_current() is pretty high on my todo list. Just need to get big ticket items resolved first.

@4ast

This comment has been minimized.

Copy link
Member

commented Oct 7, 2016

though bpf_get_current_task() is now available, fd to pathname may be too complicated to do via probe_reads? Do we still need a separate helper for it ?

@brendangregg

This comment has been minimized.

Copy link
Member Author

commented Oct 7, 2016

Yes, I tried using bpf_get_current_task() but it gets pretty horrible. I got as far as this and it still wasn't working (no file output; not populated on entry to stat()?):

#!/usr/bin/env python

from bcc import BPF

# define BPF program
prog = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fdtable.h>

int kprobe__vfs_fstat(struct pt_regs *ctx, unsigned int fd)
{
    struct files_struct *files = NULL;
    struct fdtable *fdt = NULL;
    struct file *f = NULL;
    struct dentry *de = NULL;
    struct qstr dn = {};
    struct task_struct *curr = (struct task_struct *)bpf_get_current_task();
    bpf_probe_read(&files, sizeof(files), &curr->files);
    bpf_probe_read(&fdt, sizeof(fdt), &files->fdt);
    bpf_probe_read(&f, sizeof(f), &fdt[fd]);
    bpf_probe_read(&de, sizeof(de), &f->f_path.dentry);
    bpf_probe_read(&dn, sizeof(dn), &de->d_name);
    bpf_trace_printk("fstat fd=%d file=%s\\n", fd, dn.name);
    return 0;
}
"""

# load BPF program
b = BPF(text=prog)

# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))

# format output
while 1:
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
    except ValueError:
        continue
    print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))

If we can make it work, then at least we can see what the current state is...

@4ast

This comment has been minimized.

Copy link
Member

commented Oct 13, 2016

managed to make it work:

bpf_probe_read(&f, sizeof(f), &fdt[fd]);

should be

struct file **_fd = NULL;
...
bpf_probe_read(&_fd, sizeof(_fd), &fdt->fd);
bpf_probe_read(&f, sizeof(f), &_fd[fd]);
...
bpf_trace_printk("fstat name1=%s\\n", de->d_iname);
bpf_trace_printk("fstat name2=%s\\n", dn.name);

tried few tests...
looks like both short and full name are populated,
so dn.name is probably good enough always.

@brendangregg

This comment has been minimized.

Copy link
Member Author

commented Oct 13, 2016

Awesome, thanks! So I'm starting to believe that d_iname isn't reliable:

7538432.717158000  lsb_release      15415  fstat fd=3     dn.name=dist-packages
7538432.717160000  lsb_release      15415  fstat fd=3 de->d_iname=dist-packages
7538432.717340000  lsb_release      15415  fstat fd=3     dn.name=apport_python_hook.cpython-35.pyc
7538432.717342000  lsb_release      15415  fstat fd=3 de->d_iname=p"??????t
7538432.717354000  lsb_release      15415  fstat fd=3     dn.name=apport_python_hook.cpython-35.pyc
7538432.717356000  lsb_release      15415  fstat fd=3 de->d_iname=p"??????t

So I'm going to have to fix some of the exsting *slower tools to go use dn.name instead.

Maybe I'll write a tool that uses this code (statsnoop?), and put the bpf_probe_read()s in an fd2path() static function. I suppose we could eventually move it to somewhere like src/cc/export/helpers.h, and provide this functionality in bcc, at least to start with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.