Fprobe - 函数入口/出口探针¶
简介¶
Fprobe 是一个基于 ftrace 中的函数图跟踪特性的函数入口/出口探针。 如果你不想跟踪所有函数,而是想在特定函数的入口和出口处附加回调,类似于 kprobes 和 kretprobes,你可以使用 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 ftrace_regs *fregs, void *entry_data);
void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);
请注意,@entry_ip 保存在函数入口处并传递给退出处理程序。 如果进入回调函数返回 !0,则将取消相应的退出回调。
- @fp
这是与此处理程序相关的 fprobe 数据结构的地址。 你可以将 fprobe 嵌入到你的数据结构中,并通过 @fp 中的 container_of() 宏获取它。 @fp 不得为 NULL。
- @entry_ip
这是被跟踪函数的 ftrace 地址(包括进入和退出)。 请注意,这可能不是函数的实际入口地址,而是 ftrace 被检测的地址。
- @ret_ip
这是被跟踪函数将返回到的返回地址,位于调用者中的某个位置。 这可以在进入和退出时使用。
- @fregs
这是进入和退出时的 ftrace_regs 数据结构。 这包括函数参数或返回值。 因此,用户可以通过适当的 ftrace_regs_* API 访问这些值。
- @entry_data
这是一个本地存储,用于在进入和退出处理程序之间共享数据。 默认情况下,此存储为 NULL。 如果用户在注册 fprobe 时指定了 exit_handler 字段和 entry_data_size 字段,则会分配存储空间并将其传递给 entry_handler 和 exit_handler。
同一函数上的进入数据大小和退出处理程序¶
由于进入数据通过每个任务的堆栈传递,并且其大小有限,因此每个探针的进入数据大小限制为 15 * sizeof(long)。 你还需要注意不同的 fprobe 正在探测同一个函数,此限制会变小。 进入数据大小与 sizeof(long) 对齐,并且每个具有退出处理程序的 fprobe 在堆栈上使用 sizeof(long) 空间,你应该尽可能减少同一函数上的 fprobe 数量。
丢失计数器¶
fprobe 数据结构具有 fprobe::nmissed 计数器字段,与 kprobes 相同。 在以下情况下,此计数器会递增:
fprobe 无法获取 ftrace_recursion 锁。 这通常意味着其他 ftrace 用户跟踪的函数是从 entry_handler 调用的。
由于无法从每个任务的影子堆栈中分配数据缓冲区,fprobe 无法设置函数退出。
fprobe::nmissed 字段在这两种情况下都会递增。 因此,前者跳过进入和退出回调,后者跳过退出回调,但在两种情况下,计数器都将增加 1。
请注意,如果在注册 fprobe 时,你将 FTRACE_OPS_FL_RECURSION 和/或 FTRACE_OPS_FL_RCU 设置为 fprobe::ops::flags (ftrace_ops::flags),则此计数器可能无法正常工作,因为 ftrace 会跳过增加计数器的 fprobe 函数。
函数和结构¶
-
struct fprobe_hlist_node¶
基于地址的 fprobe 哈希列表节点。
定义:
struct fprobe_hlist_node {
struct hlist_node hlist;
unsigned long addr;
struct fprobe *fp;
};
成员
hlist
地址搜索哈希表的 hlist 节点。
addr
fp 的探测地址之一。
fp
拥有此节点的 fprobe。
-
struct fprobe_hlist¶
fprobe 的哈希列表节点。
定义:
struct fprobe_hlist {
struct hlist_node hlist;
struct rcu_head rcu;
struct fprobe *fp;
int size;
struct fprobe_hlist_node array[] ;
};
成员
hlist
用于存在性检查哈希表的 hlist 节点。
rcu
用于 RCU 延迟释放的 rcu_head。
fp
拥有此 fprobe_hlist 的 fprobe。
size
array 的大小。
array
每个要探测的地址的 fprobe_hlist_node。
-
struct fprobe¶
基于 ftrace 的探针。
定义:
struct fprobe {
unsigned long nmissed;
unsigned int flags;
size_t entry_data_size;
fprobe_entry_cb entry_handler;
fprobe_exit_cb exit_handler;
struct fprobe_hlist *hlist_array;
};
成员
nmissed
用于丢失事件的计数器。
flags
状态标志。
entry_data_size
私有数据存储大小。
entry_handler
函数入口的回调函数。
exit_handler
函数退出的回调函数。
hlist_array
用于从 IP 哈希表搜索 fprobe 的 fprobe_hlist。
参数
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
NOT 探测符号的通配符模式。
描述
将 fp 注册到 ftrace,以在与 filter 匹配的符号上启用探测。 如果 notfilter 不为 NULL,则不会探测与 notfilter 匹配的符号。
如果 fp 注册成功,则返回 0,否则返回 -errno。
参数
struct fprobe *fp
要注册的 fprobe 数据结构。
unsigned long *addrs
目标函数地址的数组。
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。