使用 ftrace

ftrace 是一种调试工具,用于了解 Linux 内核中的情况。以下部分详细介绍了 ftrace 的基本功能、ftrace 与 atrace(捕获内核事件)如何配合使用,以及动态 ftrace。

有关 systrace 中没有的 ftrace 高级功能的详情,请参阅位于以下位置的 ftrace 文档:<kernel tree>/Documentation/trace/ftrace.txt

通过 atrace 捕获内核事件

atrace (frameworks/native/cmds/atrace) 使用 ftrace 来捕获内核事件。反之,systrace.py(或更高版本的 Catapult 中的 run_systrace.py)使用 adb 在设备上运行 atrace。atrace 执行以下操作:

  • 通过设置属性 (debug.atrace.tags.enableflags) 来设置用户模式跟踪。
  • 通过写入适当的 ftrace sysfs 节点来启用所需的 ftrace 功能。不过,由于 ftrace 支持的功能更多,您可以自行设置一些 sysfs 节点,然后使用 atrace。

除了启动时跟踪之外,还可以通过使用 atrace 将属性设置为适当的值。属性是一个位掩码,除了查找适当的标头(在不同的 Android 版本之间会有所变化),没有确定正确值更好的办法了。

启用 ftrace 事件

ftrace sysfs 节点位于 /d/tracing 中,trace 事件在 /d/tracing/events 中被划分为多个类别。

要按类别启用事件,请使用:

$ echo 1 > /d/tracing/events/irq/enable

要按事件启用事件,请使用:

$ echo 1 > /d/tracing/events/sched/sched_wakeup/enable

如果通过写入 sysfs 节点启用了额外的事件,这些事件将不会被 atrace 重置。Qualcomm 设备启动的常用模式是启用 kgsl (GPU) 和 mdss(显示管道)跟踪点,然后使用 atrace 或 systrace

$ adb shell "echo 1 > /d/tracing/events/mdss/enable"
$ adb shell "echo 1 > /d/tracing/events/kgsl/enable"
$ ./systrace.py sched freq idle am wm gfx view binder_driver irq workq ss sync -t 10 -b 96000 -o full_trace.html

您还可以单独使用 ftrace(不使用 atrace 或 systrace),这在您需要仅限内核的跟踪(或者您已经花时间手动写入用户模式跟踪属性)时很有帮助。如需只运行 ftrace,请执行以下操作:

  1. 将缓冲区大小设置为足以用于您跟踪的值:
    $ echo 96000 > /d/tracing/buffer_size_kb
  2. 启用跟踪:
    $ echo 1 > /d/tracing/tracing_on
  3. 运行您的测试,然后停用跟踪:
    $ echo 0 > /d/tracing/tracing_on
  4. 转储跟踪:
    $ cat /d/tracing/trace > /data/local/tmp/trace_output

trace_output 以文本形式提供跟踪记录。要使用 Catapult 将其可视化,请从 Github 获取 Catapult 存储区并运行 trace2html:

$ catapult/tracing/bin/trace2html ~/path/to/trace_file

默认情况下,此操作会将 trace_file.html 写入同一目录中。

相关事件

同时查看 Catapult 可视化内容和 ftrace 日志通常很有帮助,例如,某些 ftrace 事件(尤其是特定于供应商的事件)未经 Catapult 可视化。不过,Catapult 的时间戳与跟踪记录中的第一个事件或 atrace 所转储的特定时间戳相关,而原始的 ftrace 时间戳则基于 Linux 内核中的特别绝对时钟源。

要从 Catapult 事件中查找特定的 ftrace 事件,请执行以下操作:

  1. 打开原始的 ftrace 日志。最新版本的 systrace 中的跟踪记录默认经过压缩:
    • 如果您使用 --no-compress 捕获 systrace,则跟踪记录在 html 文件中以 BEGIN TRACE 开头的部分。
    • 如果没有,请从 Catapult 树 (tracing/bin/html2trace) 运行 html2trace 来解压缩跟踪记录。
  2. 在 Catapult 可视化中查找相对时间戳。
  3. 在跟踪记录的开头找到一行包含 tracing_mark_sync 的内容。该行应该与以下内容相似:
    <5134>-5134  (-----) [003] ...1    68.104349: tracing_mark_write: trace_event_clock_sync: parent_ts=68.104286

    如果此行不存在(或者您使用没有 atrace 的 ftrace),则计时将与 ftrace 日志中的第一个事件相关。
    1. 将相对时间戳(以毫秒为单位)添加到 parent_ts(以秒为单位)中的值。
    2. 搜索新时间戳。

按照这些步骤操作之后,您应该会找到(或至少非常接近于)要找的事件。

使用动态 ftrace

当 systrace 和标准 ftrace 不足时,还有最后一种可用资源:动态 ftrace。动态 ftrace 涉及在启动后重写内核代码,因此出于安全考虑,它在正式版内核中不可用。不过,2015 和 2016 版中的每个严重性能错误最终都使用动态 ftrace 找出了根本原因。它在调试不间断休眠方面的功能尤其强大,因为每次命中触发不间断休眠的函数时,您都会获得内核中的堆栈跟踪记录。您还可以调试中断和抢占被停用的部分,这对证明问题非常有帮助。

要开启动态 ftrace,请修改您内核的 defconfig:

  1. 移除 CONFIG_STRICT_MEMORY_RWX(如果存在)。如果您使用的是 3.18 或更新版本以及 arm64,则不存在。
  2. 添加以下内容:CONFIG_DYNAMIC_FTRACE=y、CONFIG_FUNCTION_TRACER=y、CONFIG_IRQSOFF_TRACER=y、CONFIG_FUNCTION_PROFILER=y 和 CONFIG_PREEMPT_TRACER=y
  3. 重建和启动新内核。
  4. 运行以下代码以检查可用的跟踪工具:
    $ cat /d/tracing/available_tracers
  5. 确认命令返回 functionirqsoffpreemptoffpreemptirqsoff
  6. 运行以下命令以确保动态 ftrace 正常运行:
    $ cat /d/tracing/available_filter_functions | grep <a function you care about>

完成这些步骤之后,动态 ftrace、函数分析器、irqsoff 分析器以及 preemptoff 分析器便处于可用状态。我们强烈建议您在使用之前阅读有关这些主题的 ftrace 文档,因为它们虽然功能强大,但比较复杂。irqsoff 和 preemptoff 主要用于确认驱动程序是否会使中断或抢占处于关闭状态的时间过长。

函数分析器是诊断性能问题的最佳选择,通常用于查找函数被调用的位置。


如果函数分析器的数据不够具体,您可以将 ftrace 跟踪点与函数分析器结合使用。此外,还可以像平常一样启用 ftrace 事件,这些事件会与您的跟踪记录交错。如果您要调试的特定函数中偶尔存在不间断的长时间休眠,这会非常有用:将 ftrace 过滤器设置为所需函数,启用跟踪点,然后进行跟踪。您可以使用 trace2html 解析得出的跟踪记录,查找所需的事件,然后获取原始跟踪记录中相邻的堆栈跟踪记录。

使用 lockstat

有时,只有 ftrace 是不够的,您必须调试内核锁争用。还有一种内核选项值得尝试:CONFIG_LOCK_STAT。这是最后一种方法,因为要在 Android 设备上应用这一方法非常困难,原因是它会使内核的大小超出大多数设备可以处理的范围。

不过,lockstat 使用调试锁基础设施,这对很多其他应用都有帮助。负责设备启动的所有人都应想出方法来使该选项适用于每台设备,因为您也许会想“如果我可以开启 LOCK_STAT,就可以在 5 分钟(而不是 5 天)内确认或反驳这一问题”。


如果您可以使用配置选项启动内核,则锁跟踪与 ftrace 类似:

  1. 启用跟踪:
    $ echo 1 > /proc/sys/kernel/lock_stat
  2. 运行您的测试。
  3. 停用跟踪:
    $ echo 0 > /proc/sys/kernel/lock_stat
  4. 转储您的跟踪记录:
    $ cat /proc/lock_stat > /data/local/tmp/lock_stat

有关解释所生成的输出结果的帮助内容,请参阅 lockstat 文档:<kernel>/Documentation/locking/lockstat.txt