报告内核错误

跳转至: 导航, 搜索

该文章试图教会大家帮助 openSUSE 内核团队更轻松的诊断和修复内核错误,使得自己的错误报告更精确更有针对性,从而增加得到回应和修改的几率。

Linux 内核调试介绍

诊断一个内核错误是一项艰难的任务,但是我们知道一些简单的可能是内核错误的 bug 类别,包括:

  • 硬件驱动问题 - 缺失驱动,缺失功能,等等
  • 文件系统损坏 - 数据损坏,文件丢失,等等
  • 硬件时钟 - 系统锁死,或僵死见SysRq 键
  • 内核 OOPS - 打印在 dmesg 或 /var/log/messages
  • 性能问题或延迟

本文档用于:

  • 支持者和咨询者,这样他们就知道如何提供更有用的和更有价值的错误报告。
  • 想要了解 Linux 内核提供的调试设备的开发者。

内核错误分类

内核错误可以归类为一系列不同的情况 (例如,根据它们对安全的影响,对数据真实性的影响,等等)。由于本文档是关于调试的,我们这里将它们按复杂程度归类。

  • 未如预期工作
    有些内核错误可以简单的定位,甚至是重现,且并不影响系统其它部分的稳定性。例如,一个网卡可能在某些配置下不能工作,或者 NFS 奇怪的错误。

    这些通常是能非常简便的调试的。需要的只是精确描述发生了什么,和详细的描述如何复现该问题。当然啦,内核黑客也需要有子系统的良好知识 :)
  • 内核 OOPS
    如果内核进入了一个非预期的错误状态 (例如,当提领了一个无效的指针时),一个例外处理器将捕获此错误,中止当前进程,并试图记录一条细化的描述该例外的消息。通常,OOPS 消息是被记入系统日志的。也可以在文本控制台显示此消息,但是默认这是关的,需要手动开启。

    由于该日志消息以 "Kernel Oops" 开头,整个事件通常被叫做 "oopsing"。

    OOPS 通常是由于一些内核内的可怕但重要的工作的中间进程造成的,并保持一个或多个锁定。由于进程立即中止,这些锁定没有被释放,因此其他试图稍后声明这些锁定的进程将永久挂起。

    如果 OOPS 发生时用户正在 X 窗口会话中,计算机的表现可能是看起来完全卡死,因为 X 服务器卡在某些过时的内核锁定上而不再响应了。
  • 内核崩溃
    如果 OOPS 发生在中断处理器中,内核将试图投递 OOPS 消息,然后完全挂起,因为在中断处理器崩溃后没有合理的方法恢复。这叫做内核崩溃。

    在内核崩溃的情况下,OOPS 不能写入到系统日志,因为系统日志守护精灵不再能调度了。
  • 软锁定
    有些内核错误不触发 OOPS,只是卡死计算机。它们是由于死锁或活锁,还有其它东西造成的。在大多数情况下 (除非是某些愚蠢的错误导致中断处理器卡在锁定上),这些软锁定不会阻止中断的投递。

    如果依然能够投递中断,计算机就会响应 ping,键盘在文本控制台中也能工作。然而,进程就不能用了。

    计算机大多数时候依然是迟钝的,因为进程不能用了。

    测试这种错误的好方法是按下数字锁定键或者大小写切换键;如果键盘灯一开一关,证明您在某些地方遇到了死锁。

    2.4 版内核在僵死时也会开始闪烁数字锁定键的灯。2.6 版内核目前还没有此功能。
  • 硬锁定
    硬锁定也可能发生;通常是由于硬件问题 (或者是有些写的不好的驱动过度滥用硬件)。如果这种情况发生,您的问题就大了。您可以别看下去了,我们预祝您能调试它,外人帮不上忙的。

获取正确的信息

用户倾向于把软件问题归咎于对他们来说最复杂或最神秘的系统问题。大部分时候都是内核。

他们不见得就是错了。但这意味着他们的故障报告是不准确的,或者忽略了重要细节。

所以当您调试 Linux 内核错误 (或者某些人 认为 是 Linux 内核错误) 时,您应该先问这些有关症状的问题:

  • 僵死还是崩溃

您的计算机僵死了吗?崩溃了吗?还是它看起来像崩溃了? 即使有经验的用户也不会总能想起来去查看系统日志寻找 OOPS 消息的,尤其是当内核只是表现的"有点蹊跷"而没有崩溃和壮烈。如果您要求他们查看系统日志来寻找 OOPS 消息的话那么您能给自己少找不少麻烦。 如同上述,如果内核在用户使用 X 服务器时 OOPS,那该用户看着就像是计算机僵死了。 在 2.4 版本的内核中,会有一个提示会告诉您内核挂掉了,即使您是在 X 中:数字锁定键会开始闪烁。2.6 版的内核尚未实现该功能。 在这种情况下,切换到文本控制台再重现问题总会是有帮助的。如果计算机僵死得不是很过分,内核至少能接受键盘输入。在控制台中,也更容易抓取崩溃的额外信息。所以至少您应该告诉内核也在控制台显示 OOPS 消息(不然只会记录在日志中),使用下面命令:

# klogconsole -r0 -l8
If the oops isn't written to the syslog (e.g. when the oops occurs inside an interrupt), capturing the output with a digital camera may still help (but please make sure that any images you attach to a bug report don't exceed 512k).

Alternatively, one can try to capture the oops via a serial console.

In addition, you may want to enable the sysrq key and capture some sysrq information, as described in section "Capturing sysrq information" below.

  • Eliminate well-known problems
    There are certain classes of problems that are common as dirt. Be aware of these problem areas and try to get these out of the equation early on.
Item Number One on the list of annoying issues is probably ACPI:
Most of the time when a user reports a problem with a machine not booting properly, or hardware not getting set up correctly, this is caused by bad ACPI BIOS tables.
Try to boot with acpi=off to turn off ACPI entirely.
[list of all ACPI related kernel command line variables goes here]
Other Very Common Problems?
  • Eliminate non-essential variables
    User bug reports often describe fairly specific scenarios, such as "I am using a USB disk with reiserfs on it exported via NFS while listening to my mp3s and all of a sudden the machine crashes".

    This is a nice and accurate report involving almost every subsystem the kernel has (block device layer, VM, VFS, network, sound, ...)

    To help you narrow down the problem, here is a bunch of things you can try:
    • does the problem exist with older/newer kernel versions as well?
    • if you take component X out of the equation, can the bug still be reproduced?
    • if you exchange component X for another, equivalent component (e.g. replacing reiserfs with ext3), does the problem persist?
    • if the problem implicates memory corruption or random hardware failure: can the problem be reproduced on a different machine?
    • Especially on large machines, random memory corruption could be caused by hardware problems with bad RAM. To diagnose bad RAM, use the installation CD, select memtest86 and run it for 24 hours.

捕获 Oops 信息

如上所述,内核崩溃会导致写入系统日志的 OOPS 信息。OOPS 信息包含可以帮助您至少诊断问题的大量信息。

在相对良性的情况下,内核即使出错也能继续,系统至少稳定到能写入 OOPS 信息到系统日志。然而如果内核崩溃了,那能否记录 OOPS 信息就看运气了。

您最大的敌人是桌面,任何打印到文本控制台的内核消息在 X 服务器运行时都不会显示在屏幕上。X 服务器捕获消息的方法是打印到 /dev/console 然后显示给你,但如果错误坏到能阻止 syslogd 和 klogd 写入 OOPS 信息到系统日志,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 信息报告完了错误,您就可以禁用它们了。

注意:通过 YaST 配置暂时不支持 crashkernel,由于 bnc#773143

  • 您需要手动添加 "crashkernel=256M-:128M" 到您的 grub 配置,然后重启

现在尝试重复您出现的问题。您的系统应该会在它出现时会立即转到文本控制台重启。当您重启后,/var/log/dump 下应该有一个日志文件。用该日志文件去 bugzilla 报告错误。

使用 ksymoops

  • 任何 openSUSE 发行版应该都不需要它,但是这对自己编译内核的人来说或许有用。

内核 OOPS 通常包含一个当前处理器状态的转储,包括寄存器,指令指针,和函数调用的回溯。要使这些能够为内核开发者所用,还必须尽可能地将这些地址映射到函数和/或变量名。

目前 SuSE 的内核支持一个名为 "kallsyms" 的功能,运行中的内核会包含它自身的一个符号表格,该表格允许在打印 OOPS 消息时自动解决地址映射问题。

旧内核没有该功能,因此打印出的 OOPS 消息将只包括原始地址,需要使用一个用户空间应用程序将该地址转译为函数和/或变量名。

ksymoops 就是干这个的:您可以在标准输入中喂给 ksymoops 一个原始 OOPS 消息,假设有正确的符号信息的话,它将返回您一个映射了所有符号的料理过的版本一个十六进制指令的反汇编列表。当 kallsyms 功能开启时,应该需要 ksymoops(openSUSE 内核开启了该功能)。

问题的关键是为 ksymoops 提供正确的符号信息。通常可从 /boot 文件夹下的 vmlinux 映像和 System.map 文件中获得该信息,这些映像和文件必须精确地匹配生成 OOPS 消息的内核版本。因此,通常最好在发生崩溃的计算机上运行 ksymoops。

如果想要适当地解析模块符号,ksymoops 也需要内核模块和它们在内存中位置的列表。找到该内容的最好办法是在 OOPS 发生前后立即复制 /proc/modules 文件,并在 ksymoops 命令行使用 -l 选项指定该副本:

# ksymoops -l /tmp/proc-modules-copy < /tmp/my-oops

幸运地,这些工作在 syslogd 捕捉到 OOPS 消息时就已经自动完成大部分了,因为 syslogd 的功能就是为您进行所有的符号翻译。这有很大的优势,因为 syslogd 总是会使用正确的符号列表的。

当然了,通过串行控制台捕捉的 OOPS 消息的地址不会被 syslogd 管理,在这种情况下您才需要手工运行 ksymoops。

Using sysrq

sysrq means "system request". This is the name for a bunch of magic key combinations that will tell the kernel to display various types of internal information, sync the file system or kill a task. Since this is somewhat security sensitive (esp. the task killing part), the sysrq keyboard commands are disabled by default for security reasons.

One way to enable sysrq is to execute the following command at the shell prompt:

echo 1 > /proc/sys/kernel/sysrq

In addition, you may want to edit /etc/sysconfig/sysctl and change the variable ENABLE_SYSRQ to "yes". This will ensure that sysrq is enabled after reboot.

To use sysrq, you need to press a "magic" key combination plus a command key. This magic key combination depends on the hardware platform, but on most platforms it's usually ALT-SysRQ (on some keyboards, the SysRQ key is labelled "PrtScr" or "Print", it's usually located to the right of the function keys).

Most sysrq keys will cause the kernel to report status information to the serial console. In the default configuration, a SUSE system has all kernel generated output redirected to tty10, so you need to switch to console 10 or redirect the kernel console to a different tty using klogconsole.

The most helpful command key is "h", which displays a short help text:

SysRq : HELP : loglevel0-8 reBoot tErm kIll saK showMem powerOff showPc
		unRaw Sync showTasks Unmount

Here's a description of the most important commands:

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.

There are a number of other sysrq keys; a complete list is available from Documentation/sysrq.txt in the kernel source.

It is also possible to trigger sysrq commands from the command line, which is very useful if you do not have keyboard access (e.g. when debugging a problem remotely). In this case, simply echo the letter to /proc/sysrq-trigger and read back the information from dmesg or the syslog files:

# echo t > /proc/sysrq-trigger

使用 lkcd

原作者待写

使用 nmi_watchdog

原作者待写

使用串行控制台

Some kernel oops (especially during boot-up) might occur when the system console unusable. To get a reliable dump report a serial console helps in this case. You'll need a second machine and a null-modem cable (ie a serial cable with two identical connectors); additionally both machines have to have a serial port on them. This leaves some modern machines out of the equation, I'm afraid; you'll have to try to use netconsole on them.

Once you've hooked the cable up you should add 'console=ttyS0,115200 console=tty0' to the command-line of the debuggee. This causes all console message to be sent to ttyS0 as well as well as the standard console. The last console= parameter determines where the console input should be handled from; so if you want to use the serial console to accept input also you'll have to exchange those parameters.

On the receiving machine you just fire up (as root) screen with the command:

# screen -T vt100 /dev/ttyS0 115200

(Assuming that the serial connection is hooked onto the first serial port). The do a reboot and watch.

To capture any oops it's easiest to enable logging from screen, see the screen manual on how to do that.

作者

Olaf Kirch <okir@suse.de>
Hannes Reinecke <hare@suse.de>
Jeff Mahoney <jeffm@suse.com>

我应该对内核 OOPS 做什么?

见这份单独的 OOPS 阅读文档。

我怎么调试内核问题?

见这份单独的内核调试指南文档。