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