Fprobe - 函数进入/退出探针¶
简介¶
Fprobe 是一种基于 ftrace 的函数进入/退出探针机制。如果您只想在函数进入和退出时附加回调,类似于 kprobes 和 kretprobes,而不是使用 ftrace 的完整功能,则可以使用 fprobe。与 kprobes 和 kretprobes 相比,fprobe 可以使用单个处理程序更快地检测多个函数。本文档描述了如何使用 fprobe。
fprobe 的用法¶
fprobe 是 ftrace(+ 类似 kretprobe 的返回回调)的包装器,用于将回调附加到多个函数的进入和退出。用户需要设置 struct fprobe
并将其传递给 register_fprobe()
。
通常,fprobe 数据结构使用 entry_handler 和/或 exit_handler 初始化,如下所示。
struct fprobe fp = {
.entry_handler = my_entry_callback,
.exit_handler = my_exit_callback,
};
要启用 fprobe,请调用 register_fprobe()
、register_fprobe_ips()
和 register_fprobe_syms()
之一。这些函数使用不同类型的参数注册 fprobe。
register_fprobe()
通过函数名过滤器启用 fprobe。例如,这会在 “func*()” 函数上启用 @fp,除了 “func2()”。
register_fprobe(&fp, "func*", "func2");
register_fprobe_ips()
通过 ftrace 位置地址启用 fprobe。例如:
unsigned long ips[] = { 0x.... };
register_fprobe_ips(&fp, ips, ARRAY_SIZE(ips));
并且 register_fprobe_syms()
通过符号名启用 fprobe。例如:
char syms[] = {"func1", "func2", "func3"};
register_fprobe_syms(&fp, syms, ARRAY_SIZE(syms));
要禁用(从函数中删除)此 fprobe,请调用
unregister_fprobe(&fp);
您可以通过以下方式临时(软)禁用 fprobe
disable_fprobe(&fp);
并通过以下方式恢复
enable_fprobe(&fp);
以上定义通过包含头文件
#include <linux/fprobe.h>
与 ftrace 相同,注册的回调将在调用 register_fprobe()
之后和返回之前开始被调用。请参阅 Documentation/trace/ftrace.rst
。
此外,unregister_fprobe()
将保证在 unregister_fprobe()
返回后,函数不再调用进入和退出处理程序,与 unregister_ftrace_function() 相同。
fprobe 进入/退出处理程序¶
进入/退出回调函数的原型如下所示
int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data);
void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data);
请注意,@entry_ip 在函数入口处保存并传递给退出处理程序。如果进入回调函数返回 !0,则会取消相应的退出回调。
- @fp
这是与此处理程序相关的 fprobe 数据结构的地址。您可以将 fprobe 嵌入到您的数据结构中,并通过 container_of() 宏从 @fp 获取它。@fp 不得为 NULL。
- @entry_ip
这是被跟踪函数的 ftrace 地址(包括进入和退出)。请注意,这可能不是函数的实际入口地址,而是 ftrace 检测的地址。
- @ret_ip
这是被跟踪函数将返回到的返回地址,位于调用方的某个位置。这可以在入口和出口处使用。
- @regs
这是入口和出口处的 pt_regs 数据结构。请注意,@regs 的指令指针在 entry_handler 中可能与 @entry_ip 不同。如果您需要跟踪的指令指针,则需要使用 @entry_ip。另一方面,在 exit_handler 中,@regs 的指令指针设置为当前返回地址。
- @entry_data
这是在进入和退出处理程序之间共享数据的本地存储。默认情况下,此存储为 NULL。如果用户在注册 fprobe 时指定了 exit_handler 字段和 entry_data_size 字段,则会分配存储空间并将其传递给 entry_handler 和 exit_handler。
未命中计数器¶
fprobe 数据结构具有与 kprobes 相同的 fprobe::nmissed 计数器字段。当以下情况发生时,此计数器会增加:
fprobe 无法获取 ftrace_recursion 锁。这通常意味着由其他 ftrace 用户跟踪的函数从 entry_handler 中调用。
由于缺少 rethook(用于挂钩函数返回的影子堆栈),fprobe 无法设置函数退出。
fprobe::nmissed 字段在这两种情况下都会增加。因此,前者会跳过入口和出口回调,而后者会跳过出口回调,但在这两种情况下,计数器都会增加 1。
请注意,如果在注册 fprobe 时将 FTRACE_OPS_FL_RECURSION 和/或 FTRACE_OPS_FL_RCU 设置为 fprobe::ops::flags (ftrace_ops::flags),则此计数器可能无法正常工作,因为 ftrace 会跳过增加计数器的 fprobe 函数。
函数和结构体¶
-
struct fprobe¶
基于 ftrace 的探针。
定义:
struct fprobe {
#ifdef CONFIG_FUNCTION_TRACER;
struct ftrace_ops ops;
#endif;
unsigned long nmissed;
unsigned int flags;
struct rethook *rethook;
size_t entry_data_size;
int nr_maxactive;
fprobe_entry_cb entry_handler;
fprobe_exit_cb exit_handler;
};
成员
ops
ftrace_ops。
nmissed
缺少事件的计数器。
flags
状态标志。
rethook
rethook 数据结构。(内部数据)
entry_data_size
私有数据存储大小。
nr_maxactive
活动函数的最大数量。
entry_handler
用于函数进入的回调函数。
exit_handler
用于函数退出的回调函数。
参数
struct fprobe *fp
要禁用的 fprobe。
描述
这将软禁用 fp。请注意,这不会从函数入口中删除 ftrace 挂钩。
参数
struct fprobe *fp
要启用的 fprobe。
描述
这将软启用 fp。
-
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)¶
通过模式将 fprobe 注册到 ftrace。
参数
struct fprobe *fp
要注册的 fprobe 数据结构。
const char *filter
被探测符号的通配符模式。
const char *notfilter
不被探测符号的通配符模式。
描述
将 fp 注册到 ftrace,以便在与 filter 匹配的符号上启用探测。如果 notfilter 不为 NULL,则不探测与 notfilter 匹配的符号。
如果 fp 成功注册,则返回 0,否则返回 -errno。
参数
struct fprobe *fp
要注册的 fprobe 数据结构。
unsigned long *addrs
目标 ftrace 位置地址的数组。
int num
addrs 的条目数。
描述
将 fp 注册到 ftrace,以便在 addrs 给定的地址上启用探测。addrs 必须是 ftrace 位置地址的地址,它可能是符号地址 + 架构相关的偏移量。如果您不确定这意味着什么,请使用其他注册函数。
如果 fp 成功注册,则返回 0,否则返回 -errno。
参数
struct fprobe *fp
要注册的 fprobe 数据结构。
const char **syms
目标符号的数组。
int num
syms 的条目数。
描述
将 fp 注册到 syms 数组给定的符号。如果您确定符号存在于内核中,这将非常有用。
如果 fp 成功注册,则返回 0,否则返回 -errno。
参数
struct fprobe *fp
要注销的 fprobe 数据结构。
描述
注销 fprobe(并从函数入口中删除 ftrace 钩子)。
如果 fp 成功注销,则返回 0,否则返回 -errno。