Kprobes 使用与原理介绍
.
简介
Kprobes
(kprobe and kretprobe)是一种Linux的调试机制,用于监控系统内部事件。你可以使用 Kprobes
来解决性能瓶颈、记录特定事件、跟踪问题等。
使用方法
kprobes 接口
内核源码samples目录下 kprobes/kprobe_example.c 提供了 Kprobes
测试用例。代码非常简单,主要配置了 struct kprobe
如下几个字段:
- symbol_name
- offset
- pre_handler
- post_handler
最后调用 register_kprobe()
注册定义的kprobe。
1 |
|
1 |
|
kprobe trace
使用方法和基于tracepoint的events类似. 不同的是Kprobes可以probe几乎所有函数(除了标注__kprobes/nokprobe_inline 和被标记为 NOKPROBE_SYMBOL的函数),且在系统运行时能够动态添加/删除。使用方法详见内核文档 kprobetrace。主要是使用如下接口:
1 |
|
bpftrace
bpftrace使用很简单,详见 bpftrace guide。例如,使用 bpftrace
通过 kretprobe
获取 ktime_get
返回的时间:
1 |
|
Kprobes在arm64上的实现
kprobe 和 kretprobe 原理相似,因此本文只分析kprobe流程。
register_kprobe流程
arch_prepare_kprobe() → arch_prepare_ss_slot()
, 拷贝probe点的指令(接下来需要替换probe位置的指令),调用get_insn_slot()
获取一个slot,按照如下顺序排放:- 拷贝的probe点的指令
brk 0x6
arm_kprobe() → arch_arm_kprobe()
,probe位置的指令替换为brk 0x4
BRK #<imm>
,立即数imm
会保存会在ESR_ELx.ISS
。
异常处理流程
1、执行到probe点后,由于已经替换成了 brk 0x4
,执行后陷入el1 debug异常。 call_break_hook
根据 ESR.ISS
(0x4)调用 kprobe_breakpoint_handler
,然后再根据probe点的 addr
在kprobes的哈希链表找到注册的kprobe结构体,再调用结构体里的 pre_handler()
就完成了。调用栈如下:
1 |
|
2、执行完 pre_handler()
后,跳转到之前保存的slot执行,第一条指令就是原来probe点的指令,接下来执行下一条指令 brk 0x6
后再次陷入异常。流程和上文相似,只不过 ESR.ISS
变成了 0x6, 因此 call_break_hook
调用 kprobe_breakpoint_ss_handler
,最后执行完 handler_post()
并退出异常,继续正常的代码流程。调用栈如下:
1 |
|
Jump Optimization
ToDo