Thomas Gleixner | 09c434b | 2019-05-19 13:08:20 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 2 | /* |
| 3 | * kretprobe_example.c |
| 4 | * |
| 5 | * Here's a sample kernel module showing the use of return probes to |
| 6 | * report the return value and total time taken for probed function |
| 7 | * to run. |
| 8 | * |
| 9 | * usage: insmod kretprobe_example.ko func=<func_name> |
| 10 | * |
Christian Brauner | eea1128 | 2020-08-19 12:46:54 +0200 | [diff] [blame] | 11 | * If no func_name is specified, kernel_clone is instrumented |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 12 | * |
| 13 | * For more information on theory of operation of kretprobes, see |
Mauro Carvalho Chehab | 7f9a235 | 2020-09-09 16:10:37 +0200 | [diff] [blame] | 14 | * Documentation/trace/kprobes.rst |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 15 | * |
| 16 | * Build and insert the kernel module as done in the kprobe example. |
| 17 | * You will see the trace data in /var/log/messages and on the console |
| 18 | * whenever the probed function returns. (Some messages may be suppressed |
| 19 | * if syslogd is configured to eliminate duplicate messages.) |
| 20 | */ |
| 21 | |
| 22 | #include <linux/kernel.h> |
| 23 | #include <linux/module.h> |
| 24 | #include <linux/kprobes.h> |
| 25 | #include <linux/ktime.h> |
| 26 | #include <linux/limits.h> |
Alexey Dobriyan | 8abf919 | 2009-08-13 10:05:43 +0000 | [diff] [blame] | 27 | #include <linux/sched.h> |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 28 | |
Christian Brauner | eea1128 | 2020-08-19 12:46:54 +0200 | [diff] [blame] | 29 | static char func_name[NAME_MAX] = "kernel_clone"; |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 30 | module_param_string(func, func_name, NAME_MAX, S_IRUGO); |
| 31 | MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the" |
| 32 | " function's execution time"); |
| 33 | |
| 34 | /* per-instance private data */ |
| 35 | struct my_data { |
| 36 | ktime_t entry_stamp; |
| 37 | }; |
| 38 | |
| 39 | /* Here we use the entry_hanlder to timestamp function entry */ |
| 40 | static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) |
| 41 | { |
| 42 | struct my_data *data; |
| 43 | |
| 44 | if (!current->mm) |
| 45 | return 1; /* Skip kernel threads */ |
| 46 | |
| 47 | data = (struct my_data *)ri->data; |
| 48 | data->entry_stamp = ktime_get(); |
| 49 | return 0; |
| 50 | } |
Masami Hiramatsu | d85eaa9 | 2020-03-26 23:50:11 +0900 | [diff] [blame] | 51 | NOKPROBE_SYMBOL(entry_handler); |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 52 | |
| 53 | /* |
| 54 | * Return-probe handler: Log the return value and duration. Duration may turn |
| 55 | * out to be zero consistently, depending upon the granularity of time |
| 56 | * accounting on the platform. |
| 57 | */ |
| 58 | static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) |
| 59 | { |
Huang Shijie | 57c24b2 | 2016-08-03 13:46:12 -0700 | [diff] [blame] | 60 | unsigned long retval = regs_return_value(regs); |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 61 | struct my_data *data = (struct my_data *)ri->data; |
| 62 | s64 delta; |
| 63 | ktime_t now; |
| 64 | |
| 65 | now = ktime_get(); |
| 66 | delta = ktime_to_ns(ktime_sub(now, data->entry_stamp)); |
Huang Shijie | 57c24b2 | 2016-08-03 13:46:12 -0700 | [diff] [blame] | 67 | pr_info("%s returned %lu and took %lld ns to execute\n", |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 68 | func_name, retval, (long long)delta); |
| 69 | return 0; |
| 70 | } |
Masami Hiramatsu | d85eaa9 | 2020-03-26 23:50:11 +0900 | [diff] [blame] | 71 | NOKPROBE_SYMBOL(ret_handler); |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 72 | |
| 73 | static struct kretprobe my_kretprobe = { |
| 74 | .handler = ret_handler, |
| 75 | .entry_handler = entry_handler, |
| 76 | .data_size = sizeof(struct my_data), |
| 77 | /* Probe up to 20 instances concurrently. */ |
| 78 | .maxactive = 20, |
| 79 | }; |
| 80 | |
| 81 | static int __init kretprobe_init(void) |
| 82 | { |
| 83 | int ret; |
| 84 | |
| 85 | my_kretprobe.kp.symbol_name = func_name; |
| 86 | ret = register_kretprobe(&my_kretprobe); |
| 87 | if (ret < 0) { |
Huang Shijie | 6134131 | 2016-08-03 13:46:09 -0700 | [diff] [blame] | 88 | pr_err("register_kretprobe failed, returned %d\n", ret); |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 89 | return -1; |
| 90 | } |
Huang Shijie | 6134131 | 2016-08-03 13:46:09 -0700 | [diff] [blame] | 91 | pr_info("Planted return probe at %s: %p\n", |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 92 | my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr); |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | static void __exit kretprobe_exit(void) |
| 97 | { |
| 98 | unregister_kretprobe(&my_kretprobe); |
Huang Shijie | 6134131 | 2016-08-03 13:46:09 -0700 | [diff] [blame] | 99 | pr_info("kretprobe at %p unregistered\n", my_kretprobe.kp.addr); |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 100 | |
| 101 | /* nmissed > 0 suggests that maxactive was set too low. */ |
Huang Shijie | 6134131 | 2016-08-03 13:46:09 -0700 | [diff] [blame] | 102 | pr_info("Missed probing %d instances of %s\n", |
Ananth N Mavinakayanahalli | 804defe | 2008-03-04 14:28:38 -0800 | [diff] [blame] | 103 | my_kretprobe.nmissed, my_kretprobe.kp.symbol_name); |
| 104 | } |
| 105 | |
| 106 | module_init(kretprobe_init) |
| 107 | module_exit(kretprobe_exit) |
| 108 | MODULE_LICENSE("GPL"); |