-10 +

linux ftrace

ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析。 最早 ftrace 是一个 function tracer,仅能够记录内核的函数调用流程。 如今 ftrace 已经成为一个 framework,采用 plugin 的方式支持开发人员添加更多种类的 trace 功能。

我们可以通过以下命令来查看本机支持的 tracer:

  1. [root@docker221 tracing]# cat /sys/kernel/debug/tracing/available_tracers
  2. blk function_graph wakeup_rt wakeup function nop
  3. [root@docker221 tracing]#

ftrace 的整体架构如下图:

Ftrace 有两大组成部分,一是 framework,另外就是一系列的 tracer 。每个 tracer 完成不同的功能, 它们统一由 framework 管理。 ftrace 的 trace 信息保存在 ring buffer 中,由 framework 负责管理。 Framework 利用 debugfs 系统在 /debugfs 下建立 tracing 目录,并提供了一系列的控制文件。

Ftrace 采用 GCC 的 profile 特性在所有内核函数的开始部分加入一段 stub 代码,ftrace 重载这段代码来实现 trace 功能。

gcc 的 -pg 选项将在每个函数入口处加入对 mcount 的调用代码。比如下面的 C 代码。

  1. //test.c
  2. void foo(void)
  3. {
  4. printf(" foo ");
  5. }

用 gcc 编译:

  1. gcc S test.c

反汇编如下:

  1. _foo:
  2. pushl %ebp
  3. movl %esp, %ebp
  4. subl $8, %esp
  5. movl $LC0, (%esp)
  6. call _printf
  7. leave
  8. ret

再加入 -gp 选项编译:

  1. gcc pg S test.c

得到的汇编如下:

  1. _foo:
  2. pushl %ebp
  3. movl %esp, %ebp
  4. subl $8, %esp
  5. LP3:
  6. movl $LP3,%edx
  7. call _mcount
  8. movl $LC0, (%esp)
  9. call _printf
  10. leave
  11. ret

增加 pg 选项后,gcc 在函数 foo 的入口处加入了对 mcount 的调用:call _mcount 。原本 mcount 由 libc 实现, 但是我们知道内核不会连接 libc 库,因此 ftrace 编写了自己的 mcount stub 函数,并借此实现 trace 功能。

在每个内核函数入口加入 trace 代码,必然会影响内核的性能,为了减小对内核性能的影响,ftrace 支持动态 trace 功能。

当 CONFIG_DYNAMIC_FTRACE 被选中后,内核编译时会调用一个 perl 脚本:recordmcount.pl 将每个函数的地址写入一个特殊的段:__mcount_loc

在内核初始化的初期,ftrace 查询 __mcount_loc 段,得到每个函数的入口地址,并将 mcount 替换为 nop 指令。 这样在默认情况下,ftrace 不会对内核性能产生影响。

当用户打开 ftrace 功能时,ftrace 将这些 nop 指令动态替换为 ftrace_caller,该函数将调用用户注册的 trace 函数。

关于我

85 后程序员, 比较熟悉 Java,JVM,Golang 相关技术栈, 关注 Liunx kernel,目前痴迷于分布式系统的设计和实践。 研究包括但不限于 Docker Kubernetes eBPF 等相关技术。

Blog

Code

Life

Archive

0 comments
Anonymous
Markdown is supported

Be the first person to leave a comment!