162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Here's a sample kernel module showing the use of fprobe to dump a
462306a36Sopenharmony_ci * stack trace and selected registers when kernel_clone() is called.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * For more information on theory of operation of kprobes, see
762306a36Sopenharmony_ci * Documentation/trace/kprobes.rst
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * You will see the trace data in /var/log/messages and on the console
1062306a36Sopenharmony_ci * whenever kernel_clone() is invoked to create a new process.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/fprobe.h>
1862306a36Sopenharmony_ci#include <linux/sched/debug.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define BACKTRACE_DEPTH 16
2262306a36Sopenharmony_ci#define MAX_SYMBOL_LEN 4096
2362306a36Sopenharmony_cistatic struct fprobe sample_probe;
2462306a36Sopenharmony_cistatic unsigned long nhit;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic char symbol[MAX_SYMBOL_LEN] = "kernel_clone";
2762306a36Sopenharmony_cimodule_param_string(symbol, symbol, sizeof(symbol), 0644);
2862306a36Sopenharmony_ciMODULE_PARM_DESC(symbol, "Probed symbol(s), given by comma separated symbols or a wildcard pattern.");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic char nosymbol[MAX_SYMBOL_LEN] = "";
3162306a36Sopenharmony_cimodule_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644);
3262306a36Sopenharmony_ciMODULE_PARM_DESC(nosymbol, "Not-probed symbols, given by a wildcard pattern.");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic bool stackdump = true;
3562306a36Sopenharmony_cimodule_param(stackdump, bool, 0644);
3662306a36Sopenharmony_ciMODULE_PARM_DESC(stackdump, "Enable stackdump.");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic bool use_trace = false;
3962306a36Sopenharmony_cimodule_param(use_trace, bool, 0644);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(use_trace, "Use trace_printk instead of printk. This is only for debugging.");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void show_backtrace(void)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	unsigned long stacks[BACKTRACE_DEPTH];
4562306a36Sopenharmony_ci	unsigned int len;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	len = stack_trace_save(stacks, BACKTRACE_DEPTH, 2);
4862306a36Sopenharmony_ci	stack_trace_print(stacks, len, 24);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int sample_entry_handler(struct fprobe *fp, unsigned long ip,
5262306a36Sopenharmony_ci				unsigned long ret_ip,
5362306a36Sopenharmony_ci				struct pt_regs *regs, void *data)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	if (use_trace)
5662306a36Sopenharmony_ci		/*
5762306a36Sopenharmony_ci		 * This is just an example, no kernel code should call
5862306a36Sopenharmony_ci		 * trace_printk() except when actively debugging.
5962306a36Sopenharmony_ci		 */
6062306a36Sopenharmony_ci		trace_printk("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip);
6162306a36Sopenharmony_ci	else
6262306a36Sopenharmony_ci		pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip);
6362306a36Sopenharmony_ci	nhit++;
6462306a36Sopenharmony_ci	if (stackdump)
6562306a36Sopenharmony_ci		show_backtrace();
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void sample_exit_handler(struct fprobe *fp, unsigned long ip,
7062306a36Sopenharmony_ci				unsigned long ret_ip, struct pt_regs *regs,
7162306a36Sopenharmony_ci				void *data)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	unsigned long rip = ret_ip;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (use_trace)
7662306a36Sopenharmony_ci		/*
7762306a36Sopenharmony_ci		 * This is just an example, no kernel code should call
7862306a36Sopenharmony_ci		 * trace_printk() except when actively debugging.
7962306a36Sopenharmony_ci		 */
8062306a36Sopenharmony_ci		trace_printk("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n",
8162306a36Sopenharmony_ci			(void *)ip, (void *)ip, (void *)rip, (void *)rip);
8262306a36Sopenharmony_ci	else
8362306a36Sopenharmony_ci		pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n",
8462306a36Sopenharmony_ci			(void *)ip, (void *)ip, (void *)rip, (void *)rip);
8562306a36Sopenharmony_ci	nhit++;
8662306a36Sopenharmony_ci	if (stackdump)
8762306a36Sopenharmony_ci		show_backtrace();
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int __init fprobe_init(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	char *p, *symbuf = NULL;
9362306a36Sopenharmony_ci	const char **syms;
9462306a36Sopenharmony_ci	int ret, count, i;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	sample_probe.entry_handler = sample_entry_handler;
9762306a36Sopenharmony_ci	sample_probe.exit_handler = sample_exit_handler;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (strchr(symbol, '*')) {
10062306a36Sopenharmony_ci		/* filter based fprobe */
10162306a36Sopenharmony_ci		ret = register_fprobe(&sample_probe, symbol,
10262306a36Sopenharmony_ci				      nosymbol[0] == '\0' ? NULL : nosymbol);
10362306a36Sopenharmony_ci		goto out;
10462306a36Sopenharmony_ci	} else if (!strchr(symbol, ',')) {
10562306a36Sopenharmony_ci		symbuf = symbol;
10662306a36Sopenharmony_ci		ret = register_fprobe_syms(&sample_probe, (const char **)&symbuf, 1);
10762306a36Sopenharmony_ci		goto out;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Comma separated symbols */
11162306a36Sopenharmony_ci	symbuf = kstrdup(symbol, GFP_KERNEL);
11262306a36Sopenharmony_ci	if (!symbuf)
11362306a36Sopenharmony_ci		return -ENOMEM;
11462306a36Sopenharmony_ci	p = symbuf;
11562306a36Sopenharmony_ci	count = 1;
11662306a36Sopenharmony_ci	while ((p = strchr(++p, ',')) != NULL)
11762306a36Sopenharmony_ci		count++;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	pr_info("%d symbols found\n", count);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	syms = kcalloc(count, sizeof(char *), GFP_KERNEL);
12262306a36Sopenharmony_ci	if (!syms) {
12362306a36Sopenharmony_ci		kfree(symbuf);
12462306a36Sopenharmony_ci		return -ENOMEM;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	p = symbuf;
12862306a36Sopenharmony_ci	for (i = 0; i < count; i++)
12962306a36Sopenharmony_ci		syms[i] = strsep(&p, ",");
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ret = register_fprobe_syms(&sample_probe, syms, count);
13262306a36Sopenharmony_ci	kfree(syms);
13362306a36Sopenharmony_ci	kfree(symbuf);
13462306a36Sopenharmony_ciout:
13562306a36Sopenharmony_ci	if (ret < 0)
13662306a36Sopenharmony_ci		pr_err("register_fprobe failed, returned %d\n", ret);
13762306a36Sopenharmony_ci	else
13862306a36Sopenharmony_ci		pr_info("Planted fprobe at %s\n", symbol);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return ret;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void __exit fprobe_exit(void)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	unregister_fprobe(&sample_probe);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	pr_info("fprobe at %s unregistered. %ld times hit, %ld times missed\n",
14862306a36Sopenharmony_ci		symbol, nhit, sample_probe.nmissed);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cimodule_init(fprobe_init)
15262306a36Sopenharmony_cimodule_exit(fprobe_exit)
15362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
154