用户空间调试建议¶
本文档简要概述了从用户空间调试 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应用程序输出的跟踪。
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