使用 tracer 进行调试¶
版权所有 2024 Google LLC.
- 作者:
Steven Rostedt <rostedt@goodmis.org>
- 许可证:
GNU 自由文档许可证,版本 1.2(在 GPL v2 下双重许可)
编写于: 6.12
简介¶
跟踪基础设施对于调试 Linux 内核非常有用。本文档用于添加各种使用 tracer 进行调试的方法。
首先,请确保已挂载 tracefs 文件系统
$ sudo mount -t tracefs tracefs /sys/kernel/tracing
使用 trace_printk()¶
trace_printk()
是一个非常轻量级的实用程序,可以在内核中的任何上下文中使用,除了“noinstr”部分。 它可以在 normal、softirq、interrupt 甚至 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”标志禁用跟踪,并在启动后启用跟踪。 否则,来自最新启动的跟踪将与来自上一次启动的跟踪混合,这可能会使读取变得混乱。