diff options
Diffstat (limited to 'samples')
-rw-r--r-- | samples/Kconfig | 11 | ||||
-rw-r--r-- | samples/Makefile | 2 | ||||
-rw-r--r-- | samples/kprobes/Makefile | 5 | ||||
-rw-r--r-- | samples/kprobes/jprobe_example.c | 68 | ||||
-rw-r--r-- | samples/kprobes/kprobe_example.c | 91 | ||||
-rw-r--r-- | samples/kprobes/kretprobe_example.c | 106 |
6 files changed, 282 insertions, 1 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index 74d97cc..e1fb471 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -22,5 +22,16 @@ config SAMPLE_KOBJECT If in doubt, say "N" here. +config SAMPLE_KPROBES + tristate "Build kprobes examples -- loadable modules only" + depends on KPROBES && m + help + This build several kprobes example modules. + +config SAMPLE_KRETPROBES + tristate "Build kretprobes example -- loadable modules only" + default m + depends on SAMPLE_KPROBES && KRETPROBES + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 8652d0f..2e02575 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -1,3 +1,3 @@ # Makefile for Linux samples code -obj-$(CONFIG_SAMPLES) += markers/ kobject/ +obj-$(CONFIG_SAMPLES) += markers/ kobject/ kprobes/ diff --git a/samples/kprobes/Makefile b/samples/kprobes/Makefile new file mode 100644 index 0000000..68739bc --- /dev/null +++ b/samples/kprobes/Makefile @@ -0,0 +1,5 @@ +# builds the kprobes example kernel modules; +# then to use one (as root): insmod <module_name.ko> + +obj-$(CONFIG_SAMPLE_KPROBES) += kprobe_example.o jprobe_example.o +obj-$(CONFIG_SAMPLE_KRETPROBES) += kretprobe_example.o diff --git a/samples/kprobes/jprobe_example.c b/samples/kprobes/jprobe_example.c new file mode 100644 index 0000000..b754135 --- /dev/null +++ b/samples/kprobes/jprobe_example.c @@ -0,0 +1,68 @@ +/* + * Here's a sample kernel module showing the use of jprobes to dump + * the arguments of do_fork(). + * + * For more information on theory of operation of jprobes, see + * Documentation/kprobes.txt + * + * Build and insert the kernel module as done in the kprobe example. + * You will see the trace data in /var/log/messages and on the + * console whenever do_fork() is invoked to create a new process. + * (Some messages may be suppressed if syslogd is configured to + * eliminate duplicate messages.) + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kprobes.h> + +/* + * Jumper probe for do_fork. + * Mirror principle enables access to arguments of the probed routine + * from the probe handler. + */ + +/* Proxy routine having the same arguments as actual do_fork() routine */ +static long jdo_fork(unsigned long clone_flags, unsigned long stack_start, + struct pt_regs *regs, unsigned long stack_size, + int __user *parent_tidptr, int __user *child_tidptr) +{ + printk(KERN_INFO "jprobe: clone_flags = 0x%lx, stack_size = 0x%lx," + " regs = 0x%p\n", + clone_flags, stack_size, regs); + + /* Always end with a call to jprobe_return(). */ + jprobe_return(); + return 0; +} + +static struct jprobe my_jprobe = { + .entry = jdo_fork, + .kp = { + .symbol_name = "do_fork", + }, +}; + +static int __init jprobe_init(void) +{ + int ret; + + ret = register_jprobe(&my_jprobe); + if (ret < 0) { + printk(KERN_INFO "register_jprobe failed, returned %d\n", ret); + return -1; + } + printk(KERN_INFO "Planted jprobe at %p, handler addr %p\n", + my_jprobe.kp.addr, my_jprobe.entry); + return 0; +} + +static void __exit jprobe_exit(void) +{ + unregister_jprobe(&my_jprobe); + printk(KERN_INFO "jprobe at %p unregistered\n", my_jprobe.kp.addr); +} + +module_init(jprobe_init) +module_exit(jprobe_exit) +MODULE_LICENSE("GPL"); diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.c new file mode 100644 index 0000000..a681998 --- /dev/null +++ b/samples/kprobes/kprobe_example.c @@ -0,0 +1,91 @@ +/* + * NOTE: This example is works on x86 and powerpc. + * Here's a sample kernel module showing the use of kprobes to dump a + * stack trace and selected registers when do_fork() is called. + * + * For more information on theory of operation of kprobes, see + * Documentation/kprobes.txt + * + * You will see the trace data in /var/log/messages and on the console + * whenever do_fork() is invoked to create a new process. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kprobes.h> + +/* For each probe you need to allocate a kprobe structure */ +static struct kprobe kp = { + .symbol_name = "do_fork", +}; + +/* kprobe pre_handler: called just before the probed instruction is executed */ +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#ifdef CONFIG_X86 + printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx," + " flags = 0x%lx\n", + p->addr, regs->ip, regs->flags); +#endif +#ifdef CONFIG_PPC + printk(KERN_INFO "pre_handler: p->addr = 0x%p, nip = 0x%lx," + " msr = 0x%lx\n", + p->addr, regs->nip, regs->msr); +#endif + + /* A dump_stack() here will give a stack backtrace */ + return 0; +} + +/* kprobe post_handler: called after the probed instruction is executed */ +static void handler_post(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ +#ifdef CONFIG_X86 + printk(KERN_INFO "post_handler: p->addr = 0x%p, flags = 0x%lx\n", + p->addr, regs->flags); +#endif +#ifdef CONFIG_PPC + printk(KERN_INFO "post_handler: p->addr = 0x%p, msr = 0x%lx\n", + p->addr, regs->msr); +#endif +} + +/* + * fault_handler: this is called if an exception is generated for any + * instruction within the pre- or post-handler, or when Kprobes + * single-steps the probed instruction. + */ +static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) +{ + printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%dn", + p->addr, trapnr); + /* Return 0 because we don't handle the fault. */ + return 0; +} + +static int __init kprobe_init(void) +{ + int ret; + kp.pre_handler = handler_pre; + kp.post_handler = handler_post; + kp.fault_handler = handler_fault; + + ret = register_kprobe(&kp); + if (ret < 0) { + printk(KERN_INFO "register_kprobe failed, returned %d\n", ret); + return ret; + } + printk(KERN_INFO "Planted kprobe at %p\n", kp.addr); + return 0; +} + +static void __exit kprobe_exit(void) +{ + unregister_kprobe(&kp); + printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr); +} + +module_init(kprobe_init) +module_exit(kprobe_exit) +MODULE_LICENSE("GPL"); diff --git a/samples/kprobes/kretprobe_example.c b/samples/kprobes/kretprobe_example.c new file mode 100644 index 0000000..4e764b3 --- /dev/null +++ b/samples/kprobes/kretprobe_example.c @@ -0,0 +1,106 @@ +/* + * kretprobe_example.c + * + * Here's a sample kernel module showing the use of return probes to + * report the return value and total time taken for probed function + * to run. + * + * usage: insmod kretprobe_example.ko func=<func_name> + * + * If no func_name is specified, do_fork is instrumented + * + * For more information on theory of operation of kretprobes, see + * Documentation/kprobes.txt + * + * Build and insert the kernel module as done in the kprobe example. + * You will see the trace data in /var/log/messages and on the console + * whenever the probed function returns. (Some messages may be suppressed + * if syslogd is configured to eliminate duplicate messages.) + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/ktime.h> +#include <linux/limits.h> + +static char func_name[NAME_MAX] = "do_fork"; +module_param_string(func, func_name, NAME_MAX, S_IRUGO); +MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the" + " function's execution time"); + +/* per-instance private data */ +struct my_data { + ktime_t entry_stamp; +}; + +/* Here we use the entry_hanlder to timestamp function entry */ +static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + struct my_data *data; + + if (!current->mm) + return 1; /* Skip kernel threads */ + + data = (struct my_data *)ri->data; + data->entry_stamp = ktime_get(); + return 0; +} + +/* + * Return-probe handler: Log the return value and duration. Duration may turn + * out to be zero consistently, depending upon the granularity of time + * accounting on the platform. + */ +static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + int retval = regs_return_value(regs); + struct my_data *data = (struct my_data *)ri->data; + s64 delta; + ktime_t now; + + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, data->entry_stamp)); + printk(KERN_INFO "%s returned %d and took %lld ns to execute\n", + func_name, retval, (long long)delta); + return 0; +} + +static struct kretprobe my_kretprobe = { + .handler = ret_handler, + .entry_handler = entry_handler, + .data_size = sizeof(struct my_data), + /* Probe up to 20 instances concurrently. */ + .maxactive = 20, +}; + +static int __init kretprobe_init(void) +{ + int ret; + + my_kretprobe.kp.symbol_name = func_name; + ret = register_kretprobe(&my_kretprobe); + if (ret < 0) { + printk(KERN_INFO "register_kretprobe failed, returned %d\n", + ret); + return -1; + } + printk(KERN_INFO "Planted return probe at %s: %p\n", + my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr); + return 0; +} + +static void __exit kretprobe_exit(void) +{ + unregister_kretprobe(&my_kretprobe); + printk(KERN_INFO "kretprobe at %p unregistered\n", + my_kretprobe.kp.addr); + + /* nmissed > 0 suggests that maxactive was set too low. */ + printk(KERN_INFO "Missed probing %d instances of %s\n", + my_kretprobe.nmissed, my_kretprobe.kp.symbol_name); +} + +module_init(kretprobe_init) +module_exit(kretprobe_exit) +MODULE_LICENSE("GPL"); |