Tracing a function with jprobes

One problem with kprobes is that you can’t check validity of the arguments passed to the function you are monitoring. For that matter, jprobes comes in. It’s basically make a wrapper for the existing function and will be called instead without make any changes to the existing function. jprobes is an extention to the kprobes and it uses the following structure to install the handler.

struct jprobe {
        struct kprobe kp;
        void *entry;    /* probe handling code to jump to */
};

Here ‘entry’ is the wrapper that has the same arguments as the original function you are going to monitor. The other important parts are kp.addr and kp.symbol_name that you used in kprobes. To register/unregister hanlder, you can use below functions.

int register_jprobe(struct jprobe *p);
void unregister_jprobe(struct jprobe *p);

Here’s a simple example that monitors mod_timer() function call.

#include 
#include 
#include 
#include 
#include 
 
static int mod_count = 0;
static int my_mod_timer(struct timer_list *timer, 
            unsigned long expires) 
{
    mod_count++;
 
    printk("mod_timer call count=%dn", mod_count);
    printk("  during run process %s(%d)n", current->comm, current->pid);
 
    jprobe_return();
    return 0;
}
static struct jprobe my_jp;
 
static int __init my_init(void)
{
    int ret_code;
    my_jp.kp.addr = (kprobe_opcode_t *)mod_timer;
    my_jp.entry = (kprobe_opcode_t *)my_mod_timer;
 
    ret_code = register_jprobe(&my_jp);
    if (ret_code == 0)
        printk("JPROBE : my_mod_timer() replaced mod_timer()n");
 
    return ret_code;
}
static void __exit my_exit(void)
{
    unregister_jprobe(&my_jp);
    printk("JPROBE : mod_timer() restored.n");
}
 
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

It doesn’t do much in the handler except counting the internal variable, but you can use this to check the arguments. Here jprobe_return() does calling the original function. You can use below Makefile to compile it.

obj-m += jprobestest.o
 
export KROOT=/lib/modules/`uname -r`/build
 
allofit:   modules
 
modules:
        @$(MAKE) -C $(KROOT) M=$(PWD) modules
 
modules_install:
        @$(MAKE) -C $(KROOT) M=$(PWD) modules_install
 
clean:
        @rm -rf   *.o *.ko .*cmd *.mod.c .tmp_versions .*.d .*.tmp Module.symvers

You can see that it increaments the count continuously in the below test.

$ make
make[1]: Entering directory `/usr/src/kernels/2.6.32-358.6.1.el6.x86_64'
  CC [M]  /root/Study/jprobestest.o
  Building modules, stage 2.
  MODPOST 3 modules
  CC      /root/Study/jprobestest.mod.o
  LD [M]  /root/Study/jprobestest.ko.unsigned
  NO SIGN [M] /root/Study/jprobestest.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.32-358.6.1.el6.x86_64'
 
$ insmod ./jprobestest.ko
 
$ tail /var/log/messages 
May  5 21:08:45 localhost kernel: mod_timer call count=115
May  5 21:08:45 localhost kernel:  during run process vmmemctl(735)
May  5 21:08:45 localhost kernel: mod_timer call count=116
May  5 21:08:45 localhost kernel:  during run process events/0(7)
May  5 21:08:45 localhost kernel: mod_timer call count=117
May  5 21:08:45 localhost kernel:  during run process events/0(7)
May  5 21:08:45 localhost kernel: mod_timer call count=118
May  5 21:08:45 localhost kernel:  during run process gnome-terminal(7910)
May  5 21:08:45 localhost kernel: mod_timer call count=119
May  5 21:08:45 localhost kernel:  during run process bash(7942)
 
$ rmmod jprobestest

Leave a Comment

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.