使用跟踪器进行调试¶
版权所有 2024 Google LLC。
- 作者:
Steven Rostedt <rostedt@goodmis.org>
- 许可证:
GNU 自由文档许可证,版本 1.2(在 GPL v2 下双重许可)
为以下版本编写:6.12
引言¶
跟踪基础设施对于调试 Linux 内核非常有用。本文档旨在添加各种使用跟踪器进行调试的方法。
首先,请确保已挂载 tracefs 文件系统
$ sudo mount -t tracefs tracefs /sys/kernel/tracing
使用 trace_printk()¶
trace_printk()
是一个非常轻量级的实用程序,可以在内核内部的任何上下文中使用,除了“noinstr”部分。它可以在正常、软中断、中断甚至 NMI 上下文中使用。跟踪数据以无锁方式写入跟踪环形缓冲区。为了使其更轻量级,在可能的情况下,它只会记录格式字符串的指针,并将原始参数保存到缓冲区中。当读取环形缓冲区时,将对格式和参数进行后处理。这样,trace_printk()
格式转换不会在记录跟踪的热路径期间完成。
trace_printk()
仅用于调试,绝不应添加到内核的子系统中。如果需要调试跟踪,请添加跟踪事件。如果在内核中发现 trace_printk()
,则以下内容将出现在 dmesg 中
**********************************************************
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
** **
** trace_printk() being used. Allocating extra memory. **
** **
** This means that this is a DEBUG kernel and it is **
** unsafe for production use. **
** **
** If you see this message and you are not debugging **
** the kernel, report this immediately to your vendor! **
** **
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
**********************************************************
调试内核崩溃¶
有多种方法可以在内核崩溃时获取系统的状态。这可能是来自 printk 中的 oops 消息,或者可以使用 kexec/kdump。但是,这些只显示崩溃时发生的情况。了解崩溃发生之前的情况可能非常有用。默认情况下,跟踪环形缓冲区是一个循环缓冲区,它将用较新的事件覆盖较旧的事件。当崩溃发生时,环形缓冲区的内容将是导致崩溃的所有事件。
可以使用几个内核命令行参数来帮助解决此问题。第一个是“ftrace_dump_on_oops”。当发生 oops 时,这将把跟踪环形缓冲区转储到控制台。如果控制台正在记录到某个地方,这将非常有用。如果使用串行控制台,则应确保环形缓冲区相对较小,否则转储环形缓冲区可能需要几分钟到几小时才能完成。以下是一个内核命令行示例
ftrace_dump_on_oops trace_buf_size=50K
注意,跟踪缓冲区由每个 CPU 缓冲区组成,其中每个缓冲区又分为默认大小为 PAGE_SIZE 的子缓冲区。上面的 trace_buf_size 选项将每个 CPU 缓冲区设置为 50K,因此,在具有 8 个 CPU 的计算机上,实际上总共为 400K。
跨启动的持久缓冲区¶
如果系统内存允许,可以在内存中的特定位置指定跟踪环形缓冲区。如果该位置在启动过程中相同并且内存没有被修改,则可以从以下启动中检索跟踪缓冲区。有两种方法可以为环形缓冲区的使用保留内存。
更可靠的方法(在 x86 上)是使用“memmap”内核命令行选项保留内存,然后将该内存用于 trace_instance。这需要了解系统物理内存布局的一些知识。使用此方法的优点是,环形缓冲区的内存将始终相同
memmap==12M$0x284500000 trace_instance=boot_map@0x284500000:12M
上面的 memmap 在物理内存位置 0x284500000 保留 12 兆字节的内存。然后,trace_instance 选项将在同一位置创建具有相同保留内存量的跟踪实例“boot_map”。由于环形缓冲区分为每个 CPU 缓冲区,因此 12 兆字节将在这些 CPU 之间平均分配。如果您有 8 个 CPU,则每个 CPU 环形缓冲区的大小为 1.5 兆字节。请注意,这还包括元数据,因此环形缓冲区实际使用的内存量将略小。
另一种更通用但不太可靠的方法是在启动时分配环形缓冲区映射,使用“reserve_mem”选项
reserve_mem=12M:4096:trace trace_instance=boot_map@trace
上面的 reserve_mem 选项将找到启动时可用的 12 兆字节,并将其按 4096 字节对齐。它会将此内存标记为 “trace”,供以后的命令行选项使用。
trace_instance 选项将创建 “boot_map” 实例,并将使用由 reserve_mem 保留并标记为 “trace” 的内存。此方法更通用,但可能不如可靠。由于 KASLR,reserve_mem 保留的内存可能不在同一位置。如果发生这种情况,则环形缓冲区将不是来自上一次启动,并且将被重置。
有时,通过使用更大的对齐方式,可以防止 KASLR 以会移动 reserve_mem 位置的方式移动东西。通过使用更大的对齐方式,您可能会发现缓冲区放置位置更一致
reserve_mem=12M:0x2000000:trace trace_instance=boot_map@trace
在启动时,将验证为环形缓冲区保留的内存。它将经过一系列测试,以确保环形缓冲区包含有效数据。如果有效,则将其设置为可从实例读取。如果任何测试失败,它将清除整个环形缓冲区并将其初始化为新的。
此映射内存的布局在内核之间可能不一致,因此只有相同的内核才能保证在保留映射的情况下正常工作。切换到不同的内核版本可能会找到不同的布局,并将缓冲区标记为无效。
在启动实例中使用 trace_printk()¶
默认情况下,trace_printk()
的内容会进入顶层跟踪实例。但是此实例永远不会在启动时保留。为了使 trace_printk()
内容以及一些其他内部跟踪进入保留的缓冲区(例如转储堆栈),请从内核命令行将实例设置为 trace_printk()
目标,或者在启动后通过 trace_printk_dest 选项进行设置。
启动后
echo 1 > /sys/kernel/tracing/instances/boot_map/options/trace_printk_dest
从内核命令行
reserve_mem=12M:4096:trace trace_instance=boot_map^traceprintk^traceoff@trace
如果从内核命令行进行设置,建议也使用 “traceoff” 标志禁用跟踪,并在启动后启用跟踪。否则,最近一次启动的跟踪将与上一次启动的跟踪混合在一起,这可能会导致读取时感到困惑。