用户空间调试建议

本文档简要概述了从用户空间调试 Linux 内核的常用工具。有关针对驱动程序开发者的调试建议,请转到此处。有关一般调试建议,请参阅一般建议文档

以下部分向您展示了可用的工具。

动态调试

通过启用/禁用日志消息来过滤最终进入内核日志的内容的机制。

先决条件:CONFIG_DYNAMIC_DEBUG

动态调试只能针对

  • pr_debug()

  • dev_dbg()

  • print_hex_dump_debug()

  • print_hex_dump_bytes()

因此,该工具的可用性目前非常有限,因为没有统一的规则将调试打印添加到代码库中,导致这些打印的实现方式多种多样。

另外,请注意,大多数调试语句都以 dprintk() 的变体实现,必须通过相应模块中的参数激活,动态调试无法为您完成该步骤。

这是一个示例,它启用文件中所有可用的pr_debug()

$ alias ddcmd='echo $* > /proc/dynamic_debug/control'
$ ddcmd '-p; file v4l2-h264.c +p'
$ grep =p /proc/dynamic_debug/control
 drivers/media/v4l2-core/v4l2-h264.c:372 [v4l2_h264]print_ref_list_b =p
 "ref_pic_list_b%u (cur_poc %u%c) %s"
 drivers/media/v4l2-core/v4l2-h264.c:333 [v4l2_h264]print_ref_list_p =p
 "ref_pic_list_p (cur_poc %u%c) %s\n"

何时应该使用它而不是 Ftrace?

  • 当代码包含有效的打印语句之一(参见上文),或者当您在开发过程中添加了多个pr_debug()语句时

  • 当时间不是问题时,这意味着代码中的多个pr_debug()语句不会导致延迟

  • 当您更关心接收特定的日志消息,而不是跟踪函数的调用模式时

有关完整文档,请参阅动态调试

Ftrace

先决条件:CONFIG_DYNAMIC_FTRACE

此工具使用 tracefs 文件系统作为控制文件和输出文件。该文件系统将挂载为 tracing 目录,该目录可以在 /sys/kernel//sys/debug/kernel/ 中找到。

调试时最重要的一些操作是

  • 您可以通过将函数名称添加到 set_ftrace_filter 文件(它接受在 available_filter_functions 文件中找到的任何函数名称)来执行函数跟踪,或者您可以通过将它们的名称添加到 set_ftrace_notrace 文件来专门禁用某些函数(更多信息请参见:动态 ftrace)。

  • 为了找出调用的来源,您可以激活 options/func_stack_trace 下的 func_stack_trace 选项。

  • 通过将所需的函数添加到 set_graph_function 文件(需要配置 FUNCTION_GRAPH_RETVAL)可以跟踪函数调用的子函数并显示返回值;更多信息请参见带有函数图示跟踪器的动态 ftrace

有关完整的 Ftrace 文档,请参阅ftrace - 函数跟踪器

或者您也可以通过使用事件跟踪来跟踪特定事件,该事件可以定义如下:创建自定义 Ftrace 跟踪点

有关完整的 Ftrace 事件跟踪文档,请参阅事件跟踪

读取 ftrace 日志

可以像读取任何其他文件一样读取 trace 文件(cat, tail, head, vim 等),文件的大小受 buffer_size_kb 的限制(echo 1000 > buffer_size_kb)。trace_pipe 的行为类似于 trace 文件,但是每当您从文件中读取内容时,内容就会被消耗。

Kernelshark

一个 GUI 界面,用于将来自 trace-cmd 应用程序输出的跟踪可视化为图形和列表视图。

有关完整的文档,请参阅https://kernelshark.org/Documentation.html

Perf & 替代方案

上面提到的工具提供了检查内核代码、结果、变量值等的方法。有时您必须先找出要查找的位置,对于这些情况,一组性能跟踪工具可以帮助您确定问题范围。

为什么要做性能分析?

在性能分析是一个很好的第一步,尤其是当

  • 您无法定义问题

  • 您不知道问题发生在何处

  • 运行中的系统不应中断,或者它是远程系统,您无法安装新的模块/内核

如何使用 Linux 工具进行简单的分析?

对于性能分析的开始,您可以从常用工具开始,例如

  • top / htop / atop获取系统负载的概览,查看特定进程的峰值

  • mpstat -P ALL查看 CPU 之间的负载分布

  • iostat -x观察输入和输出设备的利用率和性能

  • vmstat系统上内存使用情况的概览

  • pidstat类似于 vmstat ,但按进程划分,以缩小到目标

  • strace -tp $PID一旦您知道进程,您就可以弄清楚它如何与内核通信

这些应该有助于充分缩小要查看的区域。

使用 perf 深入研究

perf 工具提供了一系列指标和事件,以进一步缩小问题范围。

先决条件:在您的系统上构建或安装 perf

收集统计数据以查找 /usr 中所有以 gcc 开头的文件

# perf stat -d find /usr -name 'gcc*' | wc -l

 Performance counter stats for 'find /usr -name gcc*':

   1277.81 msec    task-clock             #    0.997 CPUs utilized
   9               context-switches       #    7.043 /sec
   1               cpu-migrations         #    0.783 /sec
   704             page-faults            #  550.943 /sec
   766548897       cycles                 #    0.600 GHz                         (97.15%)
   798285467       instructions           #    1.04  insn per cycle              (97.15%)
   57582731        branches               #   45.064 M/sec                       (2.85%)
   3842573         branch-misses          #    6.67% of all branches             (97.15%)
   281616097       L1-dcache-loads        #  220.390 M/sec                       (97.15%)
   4220975         L1-dcache-load-misses  #    1.50% of all L1-dcache accesses   (97.15%)
   <not supported> LLC-loads
   <not supported> LLC-load-misses

 1.281746009 seconds time elapsed

 0.508796000 seconds user
 0.773209000 seconds sys


52

事件和指标的可用性取决于您运行的系统。

有关完整的文档,请参阅https://perf.wiki.kernel.org/index.php/Main_Page

Perfetto

一套用于测量和分析应用程序和系统性能的工具。您可以使用它来

  • 识别瓶颈

  • 优化代码

  • 使软件运行得更快、更高效。

perfetto 和 perf 之间有什么区别?

  • perf 是 Linux 内核的一部分并专门用于 Linux 内核的工具,并且具有 CLI 用户界面。

  • perfetto 跨平台性能分析堆栈,已将功能扩展到用户空间,并提供 WEB 用户界面。

有关完整的文档,请参阅https://perfetto.dev/docs/

内核崩溃分析工具

要捕获崩溃转储,请使用 Kdump & Kexec。下面您可以找到一些分析数据的建议。

有关完整的文档,请参阅Kdump 文档 - 基于 kexec 的崩溃转储解决方案

为了找到代码中的相应行,您可以使用faddr2line;请注意,您需要启用 CONFIG_DEBUG_INFO 才能使其工作。

使用 faddr2line 的替代方法是使用 objdump(及其用于不同平台的衍生版本,如 aarch64-linux-gnu-objdump)。以此行作为示例

[  +0.000240]  rkvdec_device_run+0x50/0x138 [rockchip_vdec].

我们可以通过执行找到代码的相应行

aarch64-linux-gnu-objdump -dS drivers/staging/media/rkvdec/rockchip-vdec.ko | grep rkvdec_device_run\>: -A 40
0000000000000ac8 <rkvdec_device_run>:
 ac8:       d503201f        nop
 acc:       d503201f        nop
{
 ad0:       d503233f        paciasp
 ad4:       a9bd7bfd        stp     x29, x30, [sp, #-48]!
 ad8:       910003fd        mov     x29, sp
 adc:       a90153f3        stp     x19, x20, [sp, #16]
 ae0:       a9025bf5        stp     x21, x22, [sp, #32]
    const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
 ae4:       f9411814        ldr     x20, [x0, #560]
    struct rkvdec_dev *rkvdec = ctx->dev;
 ae8:       f9418015        ldr     x21, [x0, #768]
    if (WARN_ON(!desc))
 aec:       b4000654        cbz     x20, bb4 <rkvdec_device_run+0xec>
    ret = pm_runtime_resume_and_get(rkvdec->dev);
 af0:       f943d2b6        ldr     x22, [x21, #1952]
    ret = __pm_runtime_resume(dev, RPM_GET_PUT);
 af4:       aa0003f3        mov     x19, x0
 af8:       52800081        mov     w1, #0x4                        // #4
 afc:       aa1603e0        mov     x0, x22
 b00:       94000000        bl      0 <__pm_runtime_resume>
    if (ret < 0) {
 b04:       37f80340        tbnz    w0, #31, b6c <rkvdec_device_run+0xa4>
    dev_warn(rkvdec->dev, "Not good\n");
 b08:       f943d2a0        ldr     x0, [x21, #1952]
 b0c:       90000001        adrp    x1, 0 <rkvdec_try_ctrl-0x8>
 b10:       91000021        add     x1, x1, #0x0
 b14:       94000000        bl      0 <_dev_warn>
    *bad = 1;
 b18:       d2800001        mov     x1, #0x0                        // #0
 ...

这意味着,在崩溃转储的这一行中

[  +0.000240]  rkvdec_device_run+0x50/0x138 [rockchip_vdec]

我可以将 0x50 作为偏移量,我必须将其添加到相应函数的基本地址,该地址可以在这一行中找到

0000000000000ac8 <rkvdec_device_run>:

0xac8 + 0x50 = 0xb18 的结果是,当我在函数中搜索该地址时,我会得到以下行

*bad = 1;
b18:      d2800001        mov     x1, #0x0

版权所有 ©2024 : Collabora