Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | // SPDX-License-Identifier: GPL-2.0-only /* * Here's a sample kernel module showing the use of fprobe to dump a * stack trace and selected registers when kernel_clone() is called. * * For more information on theory of operation of kprobes, see * Documentation/trace/kprobes.rst * * You will see the trace data in /var/log/messages and on the console * whenever kernel_clone() is invoked to create a new process. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fprobe.h> #include <linux/sched/debug.h> #include <linux/slab.h> #define BACKTRACE_DEPTH 16 #define MAX_SYMBOL_LEN 4096 static struct fprobe sample_probe; static unsigned long nhit; static char symbol[MAX_SYMBOL_LEN] = "kernel_clone"; module_param_string(symbol, symbol, sizeof(symbol), 0644); MODULE_PARM_DESC(symbol, "Probed symbol(s), given by comma separated symbols or a wildcard pattern."); static char nosymbol[MAX_SYMBOL_LEN] = ""; module_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644); MODULE_PARM_DESC(nosymbol, "Not-probed symbols, given by a wildcard pattern."); static bool stackdump = true; module_param(stackdump, bool, 0644); MODULE_PARM_DESC(stackdump, "Enable stackdump."); static bool use_trace = false; module_param(use_trace, bool, 0644); MODULE_PARM_DESC(use_trace, "Use trace_printk instead of printk. This is only for debugging."); static void show_backtrace(void) { unsigned long stacks[BACKTRACE_DEPTH]; unsigned int len; len = stack_trace_save(stacks, BACKTRACE_DEPTH, 2); stack_trace_print(stacks, len, 24); } static int sample_entry_handler(struct fprobe *fp, unsigned long ip, unsigned long ret_ip, struct pt_regs *regs, void *data) { if (use_trace) /* * This is just an example, no kernel code should call * trace_printk() except when actively debugging. */ trace_printk("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); else pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); nhit++; if (stackdump) show_backtrace(); return 0; } static void sample_exit_handler(struct fprobe *fp, unsigned long ip, unsigned long ret_ip, struct pt_regs *regs, void *data) { unsigned long rip = ret_ip; if (use_trace) /* * This is just an example, no kernel code should call * trace_printk() except when actively debugging. */ trace_printk("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", (void *)ip, (void *)ip, (void *)rip, (void *)rip); else pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", (void *)ip, (void *)ip, (void *)rip, (void *)rip); nhit++; if (stackdump) show_backtrace(); } static int __init fprobe_init(void) { char *p, *symbuf = NULL; const char **syms; int ret, count, i; sample_probe.entry_handler = sample_entry_handler; sample_probe.exit_handler = sample_exit_handler; if (strchr(symbol, '*')) { /* filter based fprobe */ ret = register_fprobe(&sample_probe, symbol, nosymbol[0] == '\0' ? NULL : nosymbol); goto out; } else if (!strchr(symbol, ',')) { symbuf = symbol; ret = register_fprobe_syms(&sample_probe, (const char **)&symbuf, 1); goto out; } /* Comma separated symbols */ symbuf = kstrdup(symbol, GFP_KERNEL); if (!symbuf) return -ENOMEM; p = symbuf; count = 1; while ((p = strchr(++p, ',')) != NULL) count++; pr_info("%d symbols found\n", count); syms = kcalloc(count, sizeof(char *), GFP_KERNEL); if (!syms) { kfree(symbuf); return -ENOMEM; } p = symbuf; for (i = 0; i < count; i++) syms[i] = strsep(&p, ","); ret = register_fprobe_syms(&sample_probe, syms, count); kfree(syms); kfree(symbuf); out: if (ret < 0) pr_err("register_fprobe failed, returned %d\n", ret); else pr_info("Planted fprobe at %s\n", symbol); return ret; } static void __exit fprobe_exit(void) { unregister_fprobe(&sample_probe); pr_info("fprobe at %s unregistered. %ld times hit, %ld times missed\n", symbol, nhit, sample_probe.nmissed); } module_init(fprobe_init) module_exit(fprobe_exit) MODULE_LICENSE("GPL"); |