forked from grate-driver/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fprobe: Add sample program for fprobe
Add a sample program for the fprobe. The sample_fprobe puts a fprobe on kernel_clone() by default. This dump stack and some called address info at the function entry and exit. The sample_fprobe.ko gets 2 parameters. - symbol: you can specify the comma separated symbols or wildcard symbol pattern (in this case you can not use comma) - stackdump: a bool value to enable or disable stack dump in the fprobe handler. Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org> Tested-by: Steven Rostedt (Google) <rostedt@goodmis.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/164735291987.1084943.4449670993752806840.stgit@devnote2
- Loading branch information
Showing
4 changed files
with
131 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
|
||
obj-$(CONFIG_SAMPLE_FPROBE) += fprobe_example.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// 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 | ||
struct fprobe sample_probe; | ||
|
||
static char symbol[MAX_SYMBOL_LEN] = "kernel_clone"; | ||
module_param_string(symbol, symbol, sizeof(symbol), 0644); | ||
static char nosymbol[MAX_SYMBOL_LEN] = ""; | ||
module_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644); | ||
static bool stackdump = true; | ||
module_param(stackdump, bool, 0644); | ||
|
||
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 void sample_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) | ||
{ | ||
pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); | ||
if (stackdump) | ||
show_backtrace(); | ||
} | ||
|
||
static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) | ||
{ | ||
unsigned long rip = instruction_pointer(regs); | ||
|
||
pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", | ||
(void *)ip, (void *)ip, (void *)rip, (void *)rip); | ||
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\n", symbol); | ||
} | ||
|
||
module_init(fprobe_init) | ||
module_exit(fprobe_exit) | ||
MODULE_LICENSE("GPL"); |