用户空间调试建议¶
本文档简要概述了从用户空间调试 Linux 内核的常用工具。有关针对驱动程序开发者的调试建议,请转到此处。有关一般调试建议,请参阅一般建议文档。
以下部分向您展示了可用的工具。
动态调试¶
通过启用/禁用日志消息来过滤最终进入内核日志的内容的机制。
先决条件:CONFIG_DYNAMIC_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
事件和指标的可用性取决于您运行的系统。
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