function tracer 和 function graph tracer的实现原理
1、function tracer 和 function graph tracer 实现原理1 - 编译阶段
(Linux 5.12-rc3)
function tracer 和 function graph tracer的实现是依赖 gcc 来辅助的,在编译内核的过程中,会加上一下gcc 编译选项,如下:
gcc的-pg编译选项,在每个函数的开始增加一个stub,这样在需要的时候可以控制函数跳转到指定的代码中去执行。-mfentry 会在编译的时候,在每一个可以被trace的函数头部加入一个 __fenrty__ 的函数,这个函数是内核实现的 。
比如以 函数 blk_update_request() 为例, objdump -d 和 -r 命令反汇编后查看,在编译时 需要在 blk_update_request 函数的入口地址0x2830之后 ,也就是 0x2831地址,重定位加入 __fentry__
objdump -r 显示文件的重定位入口
objdump -d 对包含机器指令的段进行反汇编
--mrecord-mcount 参数,以上图为例,会在编译程序时,创建一个 __mcount_loc 的 数据段重定位段,会将 函数的入口地址,也就是函数在 blk-core.c 中的偏移量 存到 0x1c8这个地方,0x1c0 存储的 2770是其它函数的偏移量。
注意:
- 有的gcc版本不支持 -mrecord-mcount, 这个时候就会使用 scripts/recordmcout.pl 脚本,这个脚本实现的功能是一样的。
- inline 函数是无法被 trace的,因为 被定义了 notrace 属性。如果函数被定义了以下属性,即便加了 -pg -mfentry -mrecord-mcount 也不可以被trace
#define inline notrace #define notrace __attribute__((no_instrument_function))
2、function tracer 和 function graph tracer 链接
在 x6 架构下,使用 gdb vmlinux 反汇编 查看 __fentry__ ,可以看到直接就返回了,这说明内核并不想用 __fentry__来做回调。
前面讲到 编译的时候 加上 -mrecord-mcount 参数后,会在编译的时候生成一个 __mcount_loc 的数据段,
我们可以在 include/asm-generic/vmlinux.lds.h 这个连接脚本中 查看 这个段的起始和结束地址。