目录
1.前言
本文主要是根据阅码场 《Linux内核tracers的实现原理与应用》视频课程在aarch64上的实践。通过观察钩子函数的创建过程以及替换过程,理解trace的原理。本文同样以blk_update_request函数为例进行说明kretprobe的工作原理,此处的kretprobe是基于trace event来实现,同时使用了ftrace的框架。
kernel版本:5.10
平台:arm64
2. kretprobe领域模型

同 trace系列3 - kretprobe学习笔记
kretprobe_instance : 记录了原始的返回地址,以及所属的kretprobe,作为kretprobe实例连入kretprobe的free_instancesl链表,当kretprobe_instance 被初始化后,它将从free_instancesl链表移除;重新连入全局kretprobe_inst_table链表
3. kretprobe创建
在执行如下指令时,会完成kretprobe的创建:
#echo 'r:blk_update blk_update_request $retval' > /sys/kernel/debug/tracing/kprobe_events
此过程主要通过调用create_or_delete_trace_kprobe,最主要的设置pre_handler为pre_handler_kretprobe,同时设置了打印格式,并完成trace_kprobe的注册。与kprobe创建时的主要区别在于:rp->kp.pre_handler初始化和kretprobe.handler初始化
3.1 rp->kp.pre_handler初始化
create_or_delete_trace_kprobe ->
trace_kprobe_create ->
register_trace_kprobe ->
__register_trace_kprobe
会调用register_kretprobe,它初始化了pre_handler为pre_handler_kretprobe
int register_kretprobe(struct kretprobe *rp)
{
int ret = 0;
struct kretprobe_instance *inst;
int i;
void *addr;
if (!kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset))
return -EINVAL;
if (kretprobe_blacklist_size) {
addr = kprobe_addr(&rp->kp);
if (IS_ERR(addr))
return PTR_ERR(addr);
for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
if (kretprobe_blacklist[i].addr == addr)
return -EINVAL;
}
}
//初始化pre_handler回调
rp->kp.pre_handler = pre_handler_kretprobe;
rp->kp.post_handler = NULL;
rp->kp.fault_handler = NULL;
/* Pre-allocate memory for max kretprobe instances */
if (rp->maxactive <= 0) {
#ifdef CONFIG_PREEMPTION
//此处为10
rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus());
#else
rp->maxactive = num_possible_cpus();
#endif
}
raw_spin_lock_init(&rp->lock);
INIT_HLIST_HEAD(&rp->free_instances);
//本例中rp->maxactive为10,循环创建10个kretprobe_instance,并连入kretprobe.free_instances链表
//此处可以看出一个kretprobe可以有多个kretprobe_instance实例
for (i = 0; i < rp->maxactive; i++) {
inst = kmalloc(sizeof(struct kretprobe_instance) +
rp->data_size, GFP_KERNEL);
if (inst == NULL) {
free_rp_inst(rp);
return -ENOMEM;
}
INIT_HLIST_NODE(&inst->hlist);
hlist_add_head(&inst->hlist, &rp->free_instances);
}
rp->nmissed = 0;
/* Establish function entry probe point */
ret = register_kprobe(&rp->kp);
if (ret != 0)
free_rp_inst(rp);
return ret;
}
3.2 kretprobe.handler初始化
create_or_delete_trace_kprobe ->
trace_kprobe_create ->
alloc_trace_kprobe
- alloc_trace_kprobe:为trace_kprobe分配空间,主要初始化了kreprobe的handler为kretprobe_dispatcher
4. kretprobe brk指令替换
先来看下未替换指令前blk_update_request的反汇编:
Dump of assembler code for function blk_update_request:
0xffff8000104ec1f0 <+0>: sub sp, sp, #0x60
0xffff8000104ec1f4 <+4>: stp x29, x30, [sp,#16]
0xffff8000104ec1f8 <+8>: add x29, sp, #0x10
0xffff8000104ec1fc <+12>: stp x19, x20, [sp,#32]
0xffff8000104ec200 <+16>: stp x21, x22, [sp,#48]
0xffff8000104ec204 <+20>: stp x23, x24, [sp,#64]
0xffff8000104ec208 <+24>: str x25, [sp,#80]
0xffff8000104ec20c <+28>: mov x22, x0
0xffff8000104ec210 <+32>

本文详细介绍了kretprobe的工作流程,包括kretprobe的注册、断点指令的插入及kretprobe回调的执行过程。通过具体示例blk_update_request函数,深入解析了kretprobe如何在函数返回时捕获并执行特定的回调。

3564

被折叠的 条评论
为什么被折叠?



