boycechen
boycechen

Reputation: 41

how to use bpftrace probe local variable in a function

I want to know how to use bpftrace tool to probe the local variable in a kernel function, like:

int fun1(arg0, arg1, arg2)
{     
    ....  
    ret1 = arg0->param1; 
    var1 = xxxx; 
    .... ;
}

bpftrace can probe the data of arg0, arg1 and arg2

but how to probe the variable data of var1.

Upvotes: 3

Views: 659

Answers (1)

Asmadeus
Asmadeus

Reputation: 350

I was just looking at the same thing, so for posterity... Bpftrace by itself cannot do it yet.

There's just not enough information in BTF for this, but if you have debug informations you can use perf or bpftrace based on what perf found. It could be possible to have bpftrace use DWARF directly if support is added but it's not there yet.
This answer is based on this comment: https://github.com/bpftrace/bpftrace/issues/1901#issuecomment-881855742

To give a concrete example, taking an arbitrary kernel function (for modules perf requires -m module), we can get newattrs.ia_valid used for truncate like this (that's the first function I found):

  1. use perf to show what lines are probable, get variables available for such line. listing lines has color in terminal and is more readable there, but lines with line numbers are hookable.
[root@c9 ~]# perf probe -L do_truncate
<do_truncate@/usr/src/debug/kernel-5.14.0-284.30.1.el9_2/linux-5.14.0-284.30.1.el9_2.x86_64/fs/open.c:0>
      0  int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
                        loff_t length, unsigned int time_attrs, struct file *filp)
         {
      3         int ret;
                struct iattr newattrs;
         
                /* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
                if (length < 0)
                        return -EINVAL;
         
     10         newattrs.ia_size = length;
                newattrs.ia_valid = ATTR_SIZE | time_attrs;
     12         if (filp) {
     13                 newattrs.ia_file = filp;
     14                 newattrs.ia_valid |= ATTR_FILE;
                }
         
                /* Remove suid, sgid, and file capabilities on truncate too */
     18         ret = dentry_needs_remove_privs(dentry);
     19         if (ret < 0)
                        return ret;
     21         if (ret)
     22                 newattrs.ia_valid |= ret | ATTR_FORCE;
         
     24         inode_lock(dentry->d_inode);
                /* Note any delegations or leases have already been broken: */
     26         ret = notify_change(mnt_userns, dentry, &newattrs, NULL);
     27         inode_unlock(dentry->d_inode);
     28         return ret;
         }
         
         long vfs_truncate(const struct path *path, loff_t length)
[root@c9 ~]# perf probe -V do_truncate:26
Available variables at do_truncate:26
        @<do_truncate+109>
                struct dentry*  dentry
                struct iattr    newattrs
                struct user_namespace*  mnt_userns
  1. If just using perf:
[root@c9 ~]# > dummy
[root@c9 ~]# perf probe do_truncate:26 newattrs.ia_valid
[root@c9 ~]# perf record -e probe:do_truncate_L26 sh -c '> dummy'
[ perf record: Woken up 1 times to write data ]
perf record: Captured and wrote 0.014 MB perf.data (1 samples) ]
[root@c9 ~]# perf script
              sh 48158 [003] 41990.971339: probe:do_truncate_L26: (ffffffffae5c250d) ia_valid=0xa068
  1. From bpftrace, we need to look at what perf does a bit more and hook at the instruction listed in its debug output with the variable found from regs there (truncate call itself done in another shell):
[root@c9 ~]# perf probe --dry-run -v do_truncate:26 newattrs.ia_valid
...
Probe point found: do_truncate+109
...
Writing event: p:probe/do_truncate_L26 _text+3941645 ia_valid=+0(%sp):x32
# a 32 bits hex value (x32) at offset 0 above %sp

[root@c9 ~]# bpftrace -e 'kprobe:do_truncate+109 {
    printf("Got %x\n", *(int32*)reg("sp"));
}'
Attaching 1 probe...
Got a068

Or dereferencing the struct in bpftrace (thanks BTF! the address is the same because ia_valid is the first value of the struct but I'm looking up newattrs here in pref instead of newattrs.ia_valid)

[root@c9 ~]# perf probe --dry-run -v do_truncate:26 newattrs
Writing event: p:probe/do_truncate_L26 _text+3941645 newattrs=+0(%sp):x64

[root@c9 ~]# bpftrace -e 'kprobe:do_truncate+109 {
    printf("Got %x\n", ((struct iattr *)reg("sp"))->ia_valid);
}' 
Attaching 1 probe...
Got a068

Upvotes: 1

Related Questions