使用 ftrace

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

如需详细了解 systrace 中未提供的高级 ftrace 功能,请参阅 <kernel tree>/Documentation/trace/ftrace.txt 中的 ftrace 文档。

使用 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 节点位于 /sys/kernel/tracing 中,跟踪事件在 /sys/kernel/tracing/events 中分为多个类别。

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

echo 1 > /sys/kernel/tracing/events/irq/enable

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

echo 1 > /sys/kernel/tracing/events/sched/sched_wakeup/enable

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

adb shell "echo 1 > /sys/kernel/tracing/events/mdss/enable"
adb shell "echo 1 > /sys/kernel/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

您也可以在不使用 atrace 或 systrace 的情况下使用 ftrace,这在您想要仅限内核的跟踪时(或者如果您花时间手动编写用户模式跟踪属性)非常有用。要仅运行 ftrace

  1. 将缓冲区大小设置为足够大的值以进行跟踪
    echo 96000 > /sys/kernel/tracing/buffer_size_kb
    
  2. 启用跟踪
    echo 1 > /sys/kernel/tracing/tracing_on
    
  3. 运行测试,然后禁用跟踪
    echo 0 > /sys/kernel/tracing/tracing_on
    
  4. 转储跟踪
    cat /sys/kernel/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 /sys/kernel/tracing/available_tracers
    
  5. 确认命令返回 functionirqsoffpreemptoffpreemptirqsoff
  6. 运行以下命令以确保动态 ftrace 正常工作
    cat /sys/kernel/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,我可以在五分钟内确认或驳斥这个问题,而不是五天。”


如果您可以使用配置选项启动内核,则锁跟踪类似于 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

使用供应商跟踪点

首先使用上游跟踪点,但有时您需要使用供应商跟踪点

  { "gfx",        "Graphics",         ATRACE_TAG_GRAPHICS, {
        { OPT,      "events/mdss/enable" },
        { OPT,      "events/sde/enable" },
        { OPT,      "events/mali_systrace/enable" },
    } },

HAL 服务可扩展跟踪点,允许您添加设备特定的跟踪点/类别。跟踪点与 perfetto、atrace/systrace 和设备上的系统跟踪应用程序集成。

用于实现跟踪点/类别的 API 是

  • listCategories() 生成 (vec<TracingCategory> categories);
  • enableCategories(vec<string> categories) 生成 (Status status);
  • disableAllCategories() 生成 (Status status);
有关更多信息,请参阅 AOSP 中的 HAL 定义和默认实现