http://www.ibm.com/developerworks/cn/aix/library/au-kdbsteps/
简介
对于基本的转储分析,本文主要解释转储映像。我们将介绍如何从 snap 包中提取相应的文件,接着介绍一种检查转储的系统方法,然后探查引起系统崩溃的根本原因。转储文件和 UNIX ® 文件位于 snap 包的 dump 子目录中。
虽然我们考察的重点是转储映像,但是适当地使用 snap 选项还可以获得有用的信息,这点很重要。更多信息请阅读本文的 General 和 Kernel 小节。
General
general 目录包括有关系统运行时环境的信息,例如:
Kernel
kernel 子目录包含了有用的内核信息(进程和内存数据)。
回页首
提取 snap 包
使用 pax 命令从 snap 包中提取文件。
# zcat snap.pax.Z | pax -v |
# zcat snap.pax.Z | pax -r |
#uncompress snap.pax.Z #zcat snap.pax.Z | pax -r ./dump ./general ./kernel |
回页首
kdb 是什么?
kdb 实用工具检查当前运行系统的操作系统映像,并且与 IBM® AIX® 内核紧密耦合。这是因为它要求使用内核的结构信息来准确地格式化系统转储映像中包含的信息。kdb 为查看和格式化数据结构提供了大量子命令。
回页首
KDB - 内核调试器
kdb 是一个交互式内核调试器。kdb 允许用户控制内核代码的执行(包括内核扩展和设备驱动器),并观察和修改变量和寄存器。它必须由一个特殊的启动映像调用。
kdb 是一个分析系统转储的工具/命令。它用于对系统转储进行事后分析,或用于监视运行中的内核。
调用 kdb
kdb 在检查系统转储时通过两个参数调用。第一个参数指定系统映像,而第二个参数指定转储期间系统运行的内核的 UNIX 文件。UNIX 文件必须匹配转储映像(比如,发生崩溃时正在运行的映像)。如果不匹配的话,kdb 将显示一个错误消息并退出。
# kdb [dump] [unix] |
如果没有使用参数调用,kdb 将检查当前正在运行的系统的映像。
# kdb |
获取转储状态
对转储映像调用 kdb 之后,首先使用 stat 子命令检索基本转储状态:
kdb 的 stat 子命令提供了正在查看的转储的信息。除了包含日期、时间、版本和发布信息外,还包含转储原因代码。
转储原因代码
如果由于系统检测到问题而发起转储,那么 stat 子命令的输出将包含标题为 CRASH INFORMATION 的部分。
error code for LEDs: 30000000 |
这意味着转储原因代码为 300,即 DSI(DATA STORAGE INTERRUPT)。
转储原因代码表示造成崩溃的基本原因。大多数崩溃通常显示的原因代码为 300、400 和 700。
原因代码 | 说明 |
---|---|
2nn | 机器检查 |
3nn | 数据存储中断 |
400 | 指令存储中断 |
5nn | 外部中断 |
700 | 程序中断 |
800 | 浮点数据不可用 |
机器检查原因代码通常表示硬件问题(比如,内存损坏)。
如果对当前不在物理内存中加载的虚拟地址进行引用(这是一个页面错误),则会出现 DSI 错误。页面错误是经常发生的错误。通常由 VMM 处理,并且不会导致系统崩溃。然而,如果页面错误不能够在内核模式下解决,或者在禁用中断情况下出现页面错误,那么 DIS 将造成系统崩溃。这个问题通常由软件引起,但是偶尔也和硬件有关。
指令存储中断通常是指在取出指令时发生页面错误。这种错误通常通过 VMM 解决,并且一般不会造成系统崩溃。如果不能够通过 VMM 解决,或者在禁用中断的情况下发生页面错误,那么将造成系统崩溃。
引起崩溃的原因是外部设备出现中断,比如 I/O 总线控制器。
这种类型的中断是由内核例程调用了 trap 指令引起的。这通常是调用 panic 内核服务或断言失败造成的。内核例程在遇到无法解决的问题时就会调用 panic 服务。这种问题通常和软件有关,但也可能和硬件也有关。
如果在 MSR(机器状态寄存器)中可用的浮点位被禁用,将尝试执行一个浮点指令。
回页首
初始 CPU 上下文
kdb 永远在运行崩溃线程的 CPU 上下文中启动。当调用 kdb 时,显示以下提示:(n)>>
,其中的 n 表示当前的 CPU 上下文。
然而,要切换到另一个 CPU 上下文,可以使用 cpu 子命令:
(0)>> cpu 1 (1)>> |
MST (MACHINE STATE SAVE AREA)
Machine State save area(MST)包含已保存的 CPU 进程上下文的映像。进程上下文包含通用寄存器、浮点寄存器、特殊用途的寄存器和其他有关重启线程所需的信息。CPU 上的每个处理器都拥有自己的 CSA(当前保存区)指针,指向当线程或中断处理程序由于上下文切换而被中断或交换时使用的 MST。虽然线程是活动的,但是运行线程的处理器的 CSA 将指向当前活动线程的 MST。
mst [ thread_slot | thread_table_address ] |
为指定的线程提供一个格式化的 MST 显示。如果没有指定任何地址,将显示当前处理器的 MST 区域。
mst 子命令输出中的数据字段
包含错误指令的位置(错误的 MST)。这是在崩溃期间执行的当前指令。
包含函数的返回地址。
提供有关崩溃特征的信息。
表示处理器是否在中断禁用的情况下运行。
MST 表示处理器中运行的基本级别的线程。如果值为非 0 的值,则处理器正在处理中断。prev 字段指向链中的下一个 MST 结构。
prev 和 intpri 字段中的值帮助判断处理器是在运行线程还是在运行中断处理程序。
如果 CPU 正在运行一个线程,该线程可以位于处理上下文或中断上下文中。中断处理程序始终在中断上下文中运行。
处理环境与中断环境之间的主要差别是:当处于中断环境中时,不允许出现任何页面错误。
Machine State Register (MSR)
每个 CPU 都有自己的 MSR,它表示处理器的状态。
dr msr /* dr cmd 用于转储任何寄存器 */ 的内容
(0)> dr msr msr : 0000F0B2 bit set : EE PR FP ME IR DR RI |
分析 MSR 数据
在讨论系统转储时,我们只关注在 CPU 中运行的引起系统崩溃的东西。
如果在 MSR 中设置了 PR 位(问题状态),那么 CPU 在用户模式下运行,并且不可能引起系统崩溃。因为用户模式指令没有引起系统崩溃的足够权限。
如果转储原因代码为 300 或 400,我们必须对异常结构进行分析。
1 到 127 之间的值是错误编码(在 /usr/include/sys/errno.h 中显示),而 128 到 512 之间的值属于异常(在 /usr/include/sys/m_except.h 中显示)。
我们必须对 mst 输出中的值进行分析。特别要检查 IAR 引用的指令,证实它引起的崩溃类型是 stat 子命令报告的类型。
VMM 错误日志
在 VMM 提供崩溃原因时,vmlog 子命令提供额外的信息。代码 300 转储中显示的 Error ID 是 DSI_PROC,而在代码 400 转储中为 ISI_PROC。Exception DSISR/ISISR,srval,virt addr 包含异常结构中显示的信息。Exception 值是一个内部 VMM 错误代码。
f [threadtableslot | threadtableaddress] |
它显示线程的内核模式栈跟踪。
如果没有提供任何参数,则显示当前 CPU 上下文的栈跟踪。
上图显示了 stat 命令的输出。输出显示错误代码为 300,表明这是一个 DSI 崩溃。
上图显示的栈跟踪描述了当 CPU 0 上的某个线程运行 function __memmove 时出现的问题。
mst 子命令的输出表示线程(表示为 prev =0)在启用了所有中断的情况下运行(表示为 intpri =0B)。换句话说,线程正在进程环境中运行。
异常结构的 dsisr 字段由两项组成,DSISR_PROT 和 DSISR_ST。DSISR_PROT 字段表示出现保护违反(protection violation)。DSISR_ST 表示问题与存储操作有关。引起页面错误的数据地址为 0x00(dar 0000000000000000)。
异常结构 dsisr 字段的 DSISR_ST 表示存储操作出了问题。我们希望 IAR 引用某种形式的存储指令。
0)> dc @iar ___memmove64+000058 std r7,8(r3) |
r7 的当前内容将被存储到内存地址中(向 r3 的当前值加 0x08 得出该地址)。从 mst 子命令的输出可以看到,r3 的当前值是 FFFFFFFFFFFFFFF8,因此向 FFFFFFFFFFFFFFF8 加 0x08 将得到:
(0)> hcal FFFFFFFFFFFFFFF8+0x08 Value hexa: 00000000 Value decimal: 0 |
这意味着 r7 的当前内容将被保存到地址 0X00。它匹配异常结构中 dar 字段显示的值。
内核地址空间中的第一页虚拟内存可通过内核代码访问,但是被标记为只读。任何对该内存范围执行写操作的尝试将导致保护违反。
回页首
进程和线程
proc 子命令
在使用 asterix (*) 调用 proc 子命令时,它将以一行摘要显示进程表中的所有活动进程。
当使用进程表地址或 slot 编号调用 proc 子命令时,它将详细显示指定地址或 slot 的格式化进程信息(如果在未使用参数的情况下调用 proc 子命令,它将详细显示当前进程的格式化进程信息)。
thread 子命令
当使用 asterix (*) 调用 thread 子命令时,它将以一行摘要显示线程表中的所有活动线程。
当使用线程表地址或 slot 编号调用 thread 子命令时,它将详细显示指定线程的格式化线程信息(如果在未使用参数的情况下调用 thread 子命令,它将详细显示当前线程的格式化线程信息)。
当前线程和进程
status 子命令列出了所有 CPU 的当前线程和进程的信息。
图 18. status 命令输出
回页首
其他 kdb 子命令
pvproc+000000 在这里使用 Symbol+偏移量格式。
使用 ns 或设置 no_symbol,我们可以修改以上的格式:
(0)> set loglevel 2 |
日志级别可设为 0、1 或 2。这决定将哪种信息记录到日志文件中。值 0 表示禁止记录到日志文件中。值 1 表示只记录在提示时输入的 kdb 命令。值 2 记录 kdb 会话的输入和输出。
回页首
结束语
查找系统崩溃的根本原因是一个单调乏味的过程,因此了解 kdb 非常有用,它供一种分析系统崩溃的系统方法。通过 kdb,您将能够迅速跟踪导致系统崩溃问题。此外,您还学习到一种很有价值技能,借助它不仅能够节省大量调试时间,还可以了解可以避免系统崩溃的编程实践。最后这点也很重要,这是一种可以快速解决崩溃问题的系统性方法,它将帮助您赢得客户信任。
参考资料
学习
获得产品和技术
讨论