openSUSE:内核错误报告
Linux 内核调试简介
诊断内核错误是一项困难的任务,但一些简单的错误类别可能包括内核错误:
- 硬件驱动问题 - 缺少驱动程序、缺少功能等
- 文件系统损坏 - 数据损坏、缺少文件等
- 硬锁 - 系统死锁或冻结,请参阅 SysRq 键
- 内核 oops - 打印在 dmesg 或 /var/log/messages 中
- 性能问题或延迟
本文适用于
- 支持者和顾问,以便他们了解如何提供有用且有价值的错误报告。
- 开发人员,他们需要了解 Linux 内核提供的调试工具
内核错误的分类
内核错误可以根据多种不同的方案进行分类(例如,根据它们对安全性、数据完整性的影响等)。由于本文是关于调试的,让我们尝试按复杂程度对其进行分类。
- 未按预期工作
一些内核错误相当容易定位甚至重现,并且不会影响系统的其余部分的稳定性。例如,在某些配置下,网卡可能无法工作,或者 NFS 会产生奇怪的错误。
这些通常很容易调试。所有需要的都是对发生情况的精确描述,以及详细描述如何重现问题。当然,还需要一位对子系统有良好理解的内核黑客:) - 内核 Oops
如果内核遇到意外的错误条件(例如,当引用无效指针时),异常处理程序将捕获此错误,中止当前进程,并尝试记录描述该异常的详细信息的消息。通常,oops 消息会记录到系统日志中。也可以在文本控制台上显示消息,但这默认情况下已关闭,需要手动启用。
由于此日志消息以“Kernel Ooops”开头,因此整个事件通常被称为“oopsing”。
当发生 oops 时,导致它的进程通常在内核内部执行一些非常重要的操作,并持有一个或多个锁。由于该进程立即终止,因此这些锁都不会释放,因此以后尝试获取这些锁的其他进程将永远挂起。
如果用户在使用 X Windows 会话时发生 oops,则机器可能会完全挂起,因为 X 服务器卡在过时的内核锁上并且不再响应。 - 内核崩溃
如果在中断处理程序中发生 oops,内核将尝试传递 oops 消息,然后完全停止,因为在中断处理程序崩溃后没有合理的恢复方法。这称为内核崩溃。
在内核崩溃的情况下,oops 不会写入 syslog,因为 syslog 守护程序将不再被调度。 - 软死锁
一些内核错误不会触发 oops,而只是冻结机器。它们可能是由死锁或活锁等引起的。在大多数情况下(除非愚蠢的错误导致中断处理程序在锁上自旋),这些软死锁不会阻止传递中断。
如果仍然可以传递中断,机器将对 ping 做出反应,并且键盘输入将在文本控制台上回显。但是,进程将不再取得任何进展。
尽管如此,机器将大部分时间无响应,因为进程不再取得任何进展。
一个很好的测试方法是按下 Num Lock 或 Caps Lock 键;如果键盘 LED 开启和关闭,则某个地方存在死锁。
此外,如果内核卡住,内核将自行闪烁 Num Lock 或 Caps-Lock LED。 - 硬死锁
也可能发生硬死锁;这些通常是由于硬件问题(或某些编写不佳的驱动程序对硬件的过度滥用)引起的。如果发生这种情况,你将陷入困境。现在可以停止阅读,祝你好运调试此问题。
获取正确的信息
用户倾向于将软件问题归咎于系统中看似最复杂或神秘的部分。在大多数情况下是内核。
这并不意味着他们一定是错的。但通常这意味着他们的错误报告不准确,或者省略了重要细节。
因此,当你被要求调试 Linux 内核错误(或有人认为是 Linux 内核错误)时,你应该首先询问症状的一些问题
- 挂起与崩溃
机器是否挂起?它是否崩溃?它只是看起来崩溃了吗?
即使是经验丰富的用户也不会总是记得检查 syslog 中的 oops 消息,尤其是在内核只是“略微奇怪”地行为而没有戏剧性地崩溃和燃烧时。如果你要求他们检查 syslog 中的 oops 消息,你可以节省很多工作。
如上所述,如果内核在用户使用 X 时 oops,它可能看起来像是机器挂起。
一个指示内核崩溃的迹象是,即使你在 X 中,Num Lock LED 也会开始闪烁。
在这种情况下,在切换到文本控制台后重现问题始终有帮助。如果机器没有硬挂,内核至少可以接受键盘输入。在控制台上,你还可以捕获有关崩溃的更多信息。至少,你应该告诉内核在控制台上显示 oops 消息,使用
# klogconsole -r0 -l8
- 如果 oops 未写入 syslog(例如,当 oops 发生在中断中时),使用数码相机捕获输出仍然可能有所帮助(但请确保你附加到错误报告的任何图像不超过 512k)。
或者,可以尝试通过串口控制台捕获 oops。
此外,你可能需要启用 sysrq 键并捕获一些 sysrq 信息,如“捕获 sysrq 信息”部分所述。
- 消除已知问题
存在一些常见的问题类别。请注意这些问题领域,并尽早将其排除。
- 列表中的第一项可能是 ACPI
- 大多数时候,当用户报告机器无法正确启动或硬件无法正确设置的问题时,这是由错误的 ACPI BIOS 表引起的。
- 尝试使用 acpi=off 完全关闭 ACPI。
- [所有 ACPI 相关的内核命令行变量列表在此]
- 其他常见问题?
- 消除不必要的变量
用户错误报告通常描述非常具体的场景,例如“我正在使用带有 reiserfs 的 USB 磁盘,通过 NFS 导出,同时收听 mp3,突然机器崩溃了”。
这是一份不错的报告,涉及几乎所有内核子系统(块设备层、VM、VFS、网络、声音……)
为了帮助你缩小问题范围,这里有一些你可以尝试的事情- 问题是否也存在于较旧/较新的内核版本中?
- 如果你移除了组件 X,问题是否仍然可以重现?
- 如果你用另一个等效的组件(例如,将 reiserfs 替换为 ext3)替换了组件 X,问题是否仍然存在?
- 如果问题涉及内存损坏或随机硬件故障:问题是否可以在另一台机器上重现?
- 尤其是在大型机器上,随机内存损坏可能是由坏 RAM 引起的硬件问题。要诊断坏 RAM,请使用安装 CD,选择 memtest86 并运行 24 小时。
捕获 Oops 信息
如前所述,内核崩溃将导致写入系统日志的 Oops。oops 消息包含大量可以帮助你至少诊断问题的信息。
在相对良性的情况下,内核即使在发生错误条件时也能继续运行,并且系统足够稳定,可以至少将 oops 写入系统日志。如果内核崩溃,记录 oops 并不容易。
你的敌人是桌面。在 X 服务器运行时打印到控制台的任何内核消息都不会显示在屏幕上。X 服务器有一种捕获打印到 /dev/console 的消息并将其显示给你的方法,但如果错误严重到无法让 syslogd 和 klogd 将 oops 写入 syslog,那么 X 实际上能够显示任何有用的信息的机会非常非常小。
因此,如果你能够以某种方式重现问题,你应该做的第一件事是切换到文本控制台并增加控制台日志级别
klogconsole -r0 -l8
这将使内核的控制台日志级别显示它发送到 syslogd 的任何内容,包括任何内核 oops;如果你现在触发内核错误,你至少会得到一屏幕的 oops 信息。
注意:如果你经常这样做,请参阅下面的串口控制台部分 - 这确实是首选方法,但作为第一次尝试,能够读取 oops 是一大胜利。要了解如何读取 oops,请参阅文件 oops-reading。
Kdump
从 openSUSE 12.2 预发布版开始,kdump 能够快速转储并仅捕获 oops。YaST 最终应该更新为自动配置此功能,但现在应使用以下说明。
- 安装 yast2-kdump 和 kdump 包。
- 使用 yast2-kdump 启用 kdump
- 编辑 /etc/sysconfig/kdump 以将 $KDUMP_DUMPFORMAT 更改为“none”——这将禁用转储,并仅保存 Oops。
- 重新启动以在内核中启用 crashkernel 区域。你的系统将正常运行,直到你重新启动,但不会捕获 Oops。
- 请注意,启用转储会使用 128M 内存,除非在大型系统(超过 512GB 内存)上,在这种情况下,它必须使用更多内存以适应更大的页表。在报告带有生成的 Oops 的问题后,你可能需要再次禁用它。
现在尝试重现您的问题。当它在切换到文本控制台后发生时,您的系统应该几乎立即重启。重启后,/var/log/dump/ 目录下应该有一个日志文件。
使用 ksymoops
- 这对于任何 openSUSE 版本来说都不应该需要,但对于自建内核可能有用。
内核 oops 通常包括当前处理器状态的转储,包括寄存器、指令指针和函数调用回溯。为了使内核开发者能够使用这些信息,这些地址必须映射到函数和/或变量名称(如果可能的话)。
当前的 SUSE 内核支持一项名为“kallsyms”的功能,在该功能中,正在运行的内核包含其自身的符号表,这使其能够在打印 oops 时自动解析地址。
较旧的内核没有此功能,因此打印出的 oops 将仅包含原始地址,需要由用户空间应用程序进行转换。
这就是 ksymoops 的用途:您可以将原始 oops 馈送到 ksymoops 的标准输入,并提供正确的符号信息,它将为您提供一个映射所有符号和十六进制指令的反汇编列表的“已处理” oops。当 kallsyms 开启时(这对于 opensuse 内核是成立的),不应该使用它。
问题的关键是向 ksymoops 提供正确的符号信息。此信息通常从 vmlinux 镜像和 System.map 文件中获取/boot,这些文件必须完全匹配生成 oops 的内核版本。因此,通常在发生崩溃的机器上运行 ksymoops 是一个好主意。
如果需要正确解析模块符号,ksymoops 还需要内核模块列表及其在内存中的位置。提供此信息的好方法是在 oops 发生之前或之后复制文件/proc/modules,并在 ksymoops 命令行中使用此副本指定-l选项
# ksymoops -l /tmp/proc-modules-copy < /tmp/my-oops
幸运的是,当 oops 被 syslogd 捕获时,这项工作中的大部分已经自动完成,因为 syslogd 会为您完成所有符号转换。这具有很大的优势,即它始终使用正确的符号列表。
当然,通过串行控制台捕获的 oops 将不会被 syslogd 修改地址,因此在这种情况下您需要手动运行 ksymoops。
使用 sysrq
sysrq 表示“系统请求”。这是许多神奇键组合的名称,这些组合将告诉内核显示各种类型的内部信息、同步文件系统或杀死任务。由于这在某种程度上涉及安全性(尤其是杀死任务的部分),因此出于安全原因,默认情况下禁用 sysrq 键盘命令。
启用 sysrq 的一种方法是在 shell 提示符下执行以下命令
echo 1 > /proc/sys/kernel/sysrq
此外,您可能需要编辑 /etc/sysconfig/sysctl 并将变量 ENABLE_SYSRQ 更改为“yes”。这将确保在重新启动后启用 sysrq。
要使用 sysrq,您需要按下“神奇”键组合加上命令键。此神奇键组合取决于硬件平台,但在大多数平台上通常是 ALT-SysRQ(在某些键盘上,SysRQ 键标记为“PrtScr”或“Print”,通常位于功能键的右侧)。
大多数 sysrq 键将导致内核将状态信息报告到串行控制台。在默认配置中,SUSE 系统会将所有内核生成的输出重定向到 tty10,因此您需要切换到控制台 10 或使用 klogconsole 将内核控制台重定向到不同的 tty。
最有用的命令键是“h”,它显示简短的帮助文本
SysRq : HELP : loglevel0-8 reBoot tErm kIll saK showMem powerOff showPc unRaw Sync showTasks Unmount
以下是描述最重要的命令
0-8 These keys change the console log level to the indicated
level. 8 will display everything on the console, 1 will be
critical messages only, and 0 turns console logging off entirely.
M Display current memory statistics
P Display current processor registers, instruction counter, call
trace and list of loaded modules. This is essentially the process
related information that would get printed as part of an oops.
T Shows a listing of all tasks, including the back trace of their
current kernel stack. Beware, this list can be very long.
U Try to re-mount all currently mounted file systems read-only.
E Send a TERM signal to all processes except init.
I Send a KILL signal to all processes except init.
还有许多其他的 sysrq 键;完整的列表可在内核源代码中的 Documentation/sysrq.txt 中找到。
也可以从命令行触发 sysrq 命令,这在您没有键盘访问权限时非常有用(例如,在远程调试问题时)。在这种情况下,只需将字母回显到 /proc/sysrq-trigger 并从 dmesg 或 syslog 文件中读取信息
# echo t > /proc/sysrq-trigger
使用 lkcd
[待定]
使用 nmi_watchdog
[待定]
使用串行控制台
一些内核 oops(尤其是在启动期间)可能发生在系统控制台不可用的情况下。为了获得可靠的转储报告,在这种情况下,串行控制台会有所帮助。您需要一台第二台机器和一根空置电缆(即具有两个相同连接器的串行电缆);此外,两台机器都必须在其上具有串行端口。恐怕这让一些现代机器无法使用;您将不得不尝试在它们上使用 netconsole。
连接好电缆后,您应该将 'console=ttyS0,115200 console=tty0' 添加到 debuggee 的命令行。这将导致所有控制台消息都发送到 ttyS0 以及标准控制台。最后一个 console= 参数确定应从哪里处理控制台输入;因此,如果您也想使用串行控制台来接受输入,则需要交换这些参数。
在接收机器上,只需以 root 用户身份启动 screen 并使用以下命令
# screen -T vt100 /dev/ttyS0 115200
(假设串行连接已连接到第一个串行端口)。然后重新启动并观看。
要捕获任何 oops,最简单的方法是启用 screen 中的日志记录,请参阅 screen 手册了解如何操作。
作者
<okir@suse.de>
<hare@suse.de>
<jeffm@suse.com>
我应该如何处理内核 OOPS?
请参阅单独的 OOPS 阅读 文档。
我如何调试内核问题?
请参阅单独的 内核调试介绍 文档。
在哪里报告您的调试结果?
如果您足够幸运并找到了补丁,请善意地通过 Bugzilla 向我们报告缺陷。请记住选择组件 Kernel,以便在 SUSE 中优化您的错误报告的路由。
即使您无法为该错误生成修复程序,也请至少报告您收集到的信息。这将帮助我们进一步处理它。