16ee64cc3SMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0-only
26ee64cc3SMasami Hiramatsu /*
36ee64cc3SMasami Hiramatsu * Here's a sample kernel module showing the use of fprobe to dump a
46ee64cc3SMasami Hiramatsu * stack trace and selected registers when kernel_clone() is called.
56ee64cc3SMasami Hiramatsu *
66ee64cc3SMasami Hiramatsu * For more information on theory of operation of kprobes, see
76ee64cc3SMasami Hiramatsu * Documentation/trace/kprobes.rst
86ee64cc3SMasami Hiramatsu *
96ee64cc3SMasami Hiramatsu * You will see the trace data in /var/log/messages and on the console
106ee64cc3SMasami Hiramatsu * whenever kernel_clone() is invoked to create a new process.
116ee64cc3SMasami Hiramatsu */
126ee64cc3SMasami Hiramatsu
136ee64cc3SMasami Hiramatsu #define pr_fmt(fmt) "%s: " fmt, __func__
146ee64cc3SMasami Hiramatsu
156ee64cc3SMasami Hiramatsu #include <linux/kernel.h>
166ee64cc3SMasami Hiramatsu #include <linux/module.h>
176ee64cc3SMasami Hiramatsu #include <linux/fprobe.h>
186ee64cc3SMasami Hiramatsu #include <linux/sched/debug.h>
196ee64cc3SMasami Hiramatsu #include <linux/slab.h>
206ee64cc3SMasami Hiramatsu
216ee64cc3SMasami Hiramatsu #define BACKTRACE_DEPTH 16
226ee64cc3SMasami Hiramatsu #define MAX_SYMBOL_LEN 4096
23e3655dfaSsunliming static struct fprobe sample_probe;
24c88dbbcdSMasami Hiramatsu (Google) static unsigned long nhit;
256ee64cc3SMasami Hiramatsu
266ee64cc3SMasami Hiramatsu static char symbol[MAX_SYMBOL_LEN] = "kernel_clone";
276ee64cc3SMasami Hiramatsu module_param_string(symbol, symbol, sizeof(symbol), 0644);
28179a93f7SMasami Hiramatsu (Google) MODULE_PARM_DESC(symbol, "Probed symbol(s), given by comma separated symbols or a wildcard pattern.");
29179a93f7SMasami Hiramatsu (Google)
306ee64cc3SMasami Hiramatsu static char nosymbol[MAX_SYMBOL_LEN] = "";
316ee64cc3SMasami Hiramatsu module_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644);
32179a93f7SMasami Hiramatsu (Google) MODULE_PARM_DESC(nosymbol, "Not-probed symbols, given by a wildcard pattern.");
33179a93f7SMasami Hiramatsu (Google)
346ee64cc3SMasami Hiramatsu static bool stackdump = true;
356ee64cc3SMasami Hiramatsu module_param(stackdump, bool, 0644);
36179a93f7SMasami Hiramatsu (Google) MODULE_PARM_DESC(stackdump, "Enable stackdump.");
37179a93f7SMasami Hiramatsu (Google)
38c88dbbcdSMasami Hiramatsu (Google) static bool use_trace = false;
39c88dbbcdSMasami Hiramatsu (Google) module_param(use_trace, bool, 0644);
40179a93f7SMasami Hiramatsu (Google) MODULE_PARM_DESC(use_trace, "Use trace_printk instead of printk. This is only for debugging.");
416ee64cc3SMasami Hiramatsu
show_backtrace(void)426ee64cc3SMasami Hiramatsu static void show_backtrace(void)
436ee64cc3SMasami Hiramatsu {
446ee64cc3SMasami Hiramatsu unsigned long stacks[BACKTRACE_DEPTH];
456ee64cc3SMasami Hiramatsu unsigned int len;
466ee64cc3SMasami Hiramatsu
476ee64cc3SMasami Hiramatsu len = stack_trace_save(stacks, BACKTRACE_DEPTH, 2);
486ee64cc3SMasami Hiramatsu stack_trace_print(stacks, len, 24);
496ee64cc3SMasami Hiramatsu }
506ee64cc3SMasami Hiramatsu
sample_entry_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct ftrace_regs * fregs,void * data)5139d95420SMasami Hiramatsu (Google) static int sample_entry_handler(struct fprobe *fp, unsigned long ip,
52cb16330dSMasami Hiramatsu (Google) unsigned long ret_ip,
5346bc0823SMasami Hiramatsu (Google) struct ftrace_regs *fregs, void *data)
546ee64cc3SMasami Hiramatsu {
55c88dbbcdSMasami Hiramatsu (Google) if (use_trace)
56c88dbbcdSMasami Hiramatsu (Google) /*
57c88dbbcdSMasami Hiramatsu (Google) * This is just an example, no kernel code should call
58c88dbbcdSMasami Hiramatsu (Google) * trace_printk() except when actively debugging.
59c88dbbcdSMasami Hiramatsu (Google) */
60c88dbbcdSMasami Hiramatsu (Google) trace_printk("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip);
61c88dbbcdSMasami Hiramatsu (Google) else
626ee64cc3SMasami Hiramatsu pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip);
63c88dbbcdSMasami Hiramatsu (Google) nhit++;
646ee64cc3SMasami Hiramatsu if (stackdump)
656ee64cc3SMasami Hiramatsu show_backtrace();
6639d95420SMasami Hiramatsu (Google) return 0;
676ee64cc3SMasami Hiramatsu }
686ee64cc3SMasami Hiramatsu
sample_exit_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct ftrace_regs * regs,void * data)69cb16330dSMasami Hiramatsu (Google) static void sample_exit_handler(struct fprobe *fp, unsigned long ip,
70*762abbc0SMasami Hiramatsu (Google) unsigned long ret_ip, struct ftrace_regs *regs,
7176d0de57SMasami Hiramatsu (Google) void *data)
726ee64cc3SMasami Hiramatsu {
73cb16330dSMasami Hiramatsu (Google) unsigned long rip = ret_ip;
746ee64cc3SMasami Hiramatsu
75c88dbbcdSMasami Hiramatsu (Google) if (use_trace)
76c88dbbcdSMasami Hiramatsu (Google) /*
77c88dbbcdSMasami Hiramatsu (Google) * This is just an example, no kernel code should call
78c88dbbcdSMasami Hiramatsu (Google) * trace_printk() except when actively debugging.
79c88dbbcdSMasami Hiramatsu (Google) */
80c88dbbcdSMasami Hiramatsu (Google) trace_printk("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n",
81c88dbbcdSMasami Hiramatsu (Google) (void *)ip, (void *)ip, (void *)rip, (void *)rip);
82c88dbbcdSMasami Hiramatsu (Google) else
836ee64cc3SMasami Hiramatsu pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n",
846ee64cc3SMasami Hiramatsu (void *)ip, (void *)ip, (void *)rip, (void *)rip);
85c88dbbcdSMasami Hiramatsu (Google) nhit++;
866ee64cc3SMasami Hiramatsu if (stackdump)
876ee64cc3SMasami Hiramatsu show_backtrace();
886ee64cc3SMasami Hiramatsu }
896ee64cc3SMasami Hiramatsu
fprobe_init(void)906ee64cc3SMasami Hiramatsu static int __init fprobe_init(void)
916ee64cc3SMasami Hiramatsu {
926ee64cc3SMasami Hiramatsu char *p, *symbuf = NULL;
936ee64cc3SMasami Hiramatsu const char **syms;
946ee64cc3SMasami Hiramatsu int ret, count, i;
956ee64cc3SMasami Hiramatsu
966ee64cc3SMasami Hiramatsu sample_probe.entry_handler = sample_entry_handler;
976ee64cc3SMasami Hiramatsu sample_probe.exit_handler = sample_exit_handler;
986ee64cc3SMasami Hiramatsu
996ee64cc3SMasami Hiramatsu if (strchr(symbol, '*')) {
1006ee64cc3SMasami Hiramatsu /* filter based fprobe */
1016ee64cc3SMasami Hiramatsu ret = register_fprobe(&sample_probe, symbol,
1026ee64cc3SMasami Hiramatsu nosymbol[0] == '\0' ? NULL : nosymbol);
1036ee64cc3SMasami Hiramatsu goto out;
1046ee64cc3SMasami Hiramatsu } else if (!strchr(symbol, ',')) {
1056ee64cc3SMasami Hiramatsu symbuf = symbol;
1066ee64cc3SMasami Hiramatsu ret = register_fprobe_syms(&sample_probe, (const char **)&symbuf, 1);
1076ee64cc3SMasami Hiramatsu goto out;
1086ee64cc3SMasami Hiramatsu }
1096ee64cc3SMasami Hiramatsu
1106ee64cc3SMasami Hiramatsu /* Comma separated symbols */
1116ee64cc3SMasami Hiramatsu symbuf = kstrdup(symbol, GFP_KERNEL);
1126ee64cc3SMasami Hiramatsu if (!symbuf)
1136ee64cc3SMasami Hiramatsu return -ENOMEM;
1146ee64cc3SMasami Hiramatsu p = symbuf;
1156ee64cc3SMasami Hiramatsu count = 1;
1166ee64cc3SMasami Hiramatsu while ((p = strchr(++p, ',')) != NULL)
1176ee64cc3SMasami Hiramatsu count++;
1186ee64cc3SMasami Hiramatsu
1196ee64cc3SMasami Hiramatsu pr_info("%d symbols found\n", count);
1206ee64cc3SMasami Hiramatsu
1216ee64cc3SMasami Hiramatsu syms = kcalloc(count, sizeof(char *), GFP_KERNEL);
1226ee64cc3SMasami Hiramatsu if (!syms) {
1236ee64cc3SMasami Hiramatsu kfree(symbuf);
1246ee64cc3SMasami Hiramatsu return -ENOMEM;
1256ee64cc3SMasami Hiramatsu }
1266ee64cc3SMasami Hiramatsu
1276ee64cc3SMasami Hiramatsu p = symbuf;
1286ee64cc3SMasami Hiramatsu for (i = 0; i < count; i++)
1296ee64cc3SMasami Hiramatsu syms[i] = strsep(&p, ",");
1306ee64cc3SMasami Hiramatsu
1316ee64cc3SMasami Hiramatsu ret = register_fprobe_syms(&sample_probe, syms, count);
1326ee64cc3SMasami Hiramatsu kfree(syms);
1336ee64cc3SMasami Hiramatsu kfree(symbuf);
1346ee64cc3SMasami Hiramatsu out:
1356ee64cc3SMasami Hiramatsu if (ret < 0)
1366ee64cc3SMasami Hiramatsu pr_err("register_fprobe failed, returned %d\n", ret);
1376ee64cc3SMasami Hiramatsu else
1386ee64cc3SMasami Hiramatsu pr_info("Planted fprobe at %s\n", symbol);
1396ee64cc3SMasami Hiramatsu
1406ee64cc3SMasami Hiramatsu return ret;
1416ee64cc3SMasami Hiramatsu }
1426ee64cc3SMasami Hiramatsu
fprobe_exit(void)1436ee64cc3SMasami Hiramatsu static void __exit fprobe_exit(void)
1446ee64cc3SMasami Hiramatsu {
1456ee64cc3SMasami Hiramatsu unregister_fprobe(&sample_probe);
1466ee64cc3SMasami Hiramatsu
147c88dbbcdSMasami Hiramatsu (Google) pr_info("fprobe at %s unregistered. %ld times hit, %ld times missed\n",
148c88dbbcdSMasami Hiramatsu (Google) symbol, nhit, sample_probe.nmissed);
1496ee64cc3SMasami Hiramatsu }
1506ee64cc3SMasami Hiramatsu
1516ee64cc3SMasami Hiramatsu module_init(fprobe_init)
1526ee64cc3SMasami Hiramatsu module_exit(fprobe_exit)
153df216f57SJeff Johnson MODULE_DESCRIPTION("sample kernel module showing the use of fprobe");
1546ee64cc3SMasami Hiramatsu MODULE_LICENSE("GPL");
155