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