Fork me on GitHub
侧边栏

Linux中oops信息调试【转】

1、Oops 信息来源及格式

Oops 这个单词含义为“惊讶”,当内核出错时(比如访问非法地址)打印出来的信息被称为 Oops 信息。

 

2、Oops 信息包含以下几部分内容

2.1 一段文本描述信息。

比如类似“Unable to handle kernel NULL pointer dereference at virtual address 00000000”的信息,它说明了发生的是哪类错误。

2.2 Oops 信息的序号。

比如是第 1 次、第 2 次等。这些信息与下面类似,中括号内的数据表示序号。Internal error: Oops: 805 [#1]

2.3 内核中加载的模块名称,也可能没有,以下面字样开头。

Modules linked in:

2.4 发生错误的 CPU 的序号。

对于单处理器的系统,序号为 0,比如:CPU: 0Not tainted (2.6.22.6 #36)

2.5 发生错误时 CPU 的各个寄存器值。

2.6 当前进程的名字及进程 ID

比如:
Process swapper (pid: 1, stack limit = 0xc0480258)
这并不是说发生错误的是这个进程,而是表示发生错误时,当前进程是它。错误可能发
生在内核代码、驱动程序,也可能就是这个进程的错误。

2.7 栈信息。

2.8 栈回溯信息,可以从中看出函数调用关系,形式如下:

Backtrace:[<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_probe+0x20/0x24)

2.9 出错指令附近的指令的机器码,比如(出错指令在小括号里):

Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)

 

3、配置内核使 Oops 信息的栈回溯信息更直观

可以通过配置 CONFIG_FRAME_POINTER 来实现。

 

4、实例

使用 Oops 信息调试内核的实例获得 Oops 信息本小节故意修改 LCD 驱动程序 drivers/video/s3c2410fb.c,加入错误代码:在 s3c2410fb_probe 函数的开头增加下面两条代码:int *ptest = NULL;*ptest = 0x1234;重新编译内核,启动后会出错并打印出如下 Oops 信息:

 1 Unable to handle kernel NULL pointer dereference at virtual address 00000000
 2 pgd = c0004000
 3 [00000000] *pgd=00000000
 4 Internal error: Oops: 805 [#1]
 5 Modules linked in:
 6 CPU: 0
 7 Not tainted (2.6.22.6 #36)
 8 PC is at s3c2410fb_probe+0x18/0x560
 9 LR is at platform_drv_probe+0x20/0x24
10 pc : [<c001a70c>]
11 lr : [<c01bf4e8>]
12 psr: a0000013
13 sp : c0481e64 ip : c0481ea0 fp : c0481e9c
14 r10: 00000000 r9 : c0024864 r8 : c03c420c
15 r7 : 00000000 r6 : c0389a3c r5 : 00000000 r4 : c036256c
16 r3 : 00001234 r2 : 00000001 r1 : c04c0fc4 r0 : c0362564
17 Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment kernel
18 Control: c000717f Table: 30004000 DAC: 00000017
19 Process swapper (pid: 1, stack limit = 0xc0480258)
20 Stack: (0xc0481e64 to 0xc0482000)
21 1e60:c02b1f70 00000020 c03625d4 c036256c c036256c 00000000 c0389a3c
22 1e80: c0389a3c c03c420c c0024864 00000000 c0481eac c0481ea0 c01bf4e8 c001a704
23 1ea0: c0481ed0 c0481eb0 c01bd5a8 c01bf4d8 c0362644 c036256c c01bd708 c0389a3c
24 1ec0: 00000000 c0481ee8 c0481ed4 c01bd788 c01bd4d0 00000000 c0481eec c0481f14
25 1ee0: c0481eec c01bc5a8 c01bd718 c038dac8 c038dac8 c03625b4 00000000 c0389a3c
26 1f00: c0389a44 c038d9dc c0481f24 c0481f18 c01bd808 c01bc568 c0481f4c c0481f28
27 1f20: c01bcd78 c01bd7f8 c0389a3c 00000000 00000000 c0480000 c0023ac8 00000000
28 1f40: c0481f60 c0481f50 c01bdc84 c01bcd0c 00000000 c0481f70 c0481f64 c01bf5fc
29 1f60: c01bdc14 c0481f80 c0481f74 c019479c c01bf5a0 c0481ff4 c0481f84 c0008c14
30 1f80: c0194798 e3c338ff e0222423 00000000 00000001 e2844004 00000000 00000000
31 1fa0: 00000000 c0481fb0 c002bf24 c0041328 00000000 00000000 c0008b40 c00476ec
32 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
33 1fe0: 00000000 00000000 00000000 c0481ff8 c00476ec c0008b50 c03cdf50 c0344178
34 Backtrace:
35 [<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
36 probe+0x20/0x24)
37 [<c01bf4c8>] (platform_drv_probe+0x0/0x24) from [<c01bd5a8>] (driver_probe_
38 device+0xe8/0x18c)
39 [<c01bd4c0>] (driver_probe_device+0x0/0x18c) from [<c01bd788>] (__driver_
40 attach+0x80/0xe0)
41 r8:00000000 r7:c0389a3c r6:c01bd708 r5:c036256c r4:c0362644
42 [<c01bd708>] (_ _driver_attach+0x0/0xe0) from [<c01bc5a8>] (bus_for_each_
43 dev+0x50/0x84)
44 r5:c0481eec r4:00000000
45 [<c01bc558>] (bus_for_each_dev+0x0/0x84) from [<c01bd808>] (driver_attach+
46 0x20/0x28)
47 r7:c038d9dc r6:c0389a44 r5:c0389a3c r4:00000000
48 [<c01bd7e8>] (driver_attach+0x0/0x28) from [<c01bcd78>] (bus_add_driver+
49 0x7c/0x1b4)
50 [<c01bccfc>] (bus_add_driver+0x0/0x1b4) from [<c01bdc84>] (driver_register+
51 0x80/0x88)
52 [<c01bdc04>] (driver_register+0x0/0x88) from [<c01bf5fc>] (platform_driver_
53 register+0x6c/0x88)
54 r4:00000000
55 [<c01bf590>] (platform_driver_register+0x0/0x88) from [<c019479c>] (s3c2410fb_
56 init+0x14/0x1c)
57 [<c0194788>] (s3c2410fb_init+0x0/0x1c) from [<c0008c14>] (kernel_init+0xd4/
58 0x28c)
59 [<c0008b40>] (kernel_init+0x0/0x28c) from [<c00476ec>] (do_exit+0x0/0x760)
60 Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)
61 Kernel panic - not syncing: Attempted to kill init!

 

 (1)明确出错原因。

由出错信息“Unable to handle kernel NULL pointer dereference at virtual address 00000000”可知内核是因为非法地址访问出错,使用了空指针。

 

(2)根据栈回溯信息找出函数调用关系。

内核崩溃时,可以从 pc 寄存器得知崩溃发生时的函数、出错指令。但是很多情况下,错误有可能是它的调用者引入的,所以找出函数的调用关系也很重要。
部分栈回溯信息如下:
[<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
probe+0x20/0x24)
这行信息分为两部分,
表示后面的 platform_drv_probe 函数调用了前面的 s3c2410fb_probe
函数。
前半部含义为:
“c001a6f4”是 s3c2410fb_probe 函数首地址偏移 0 的地址,这个函数大
小为 0x560。
后半部含义为:
“c01bf4e8”是 platform_drv_probe 函数首地址偏移 0x20 的地址,这个函
数大小为 0x24。
另外,后半部的“[<c01bf4e8>]”表示 s3c2410fb_probe 执行后的返回地址。
对于类似下面的栈回溯信息,其中是 r8~r4 表示 driver_probe_device 函数刚被调用时这
些寄存器的值。
[<c01bd4c0>] (driver_probe_device+0x0/0x18c) from [<c01bd788>] (__driver_
attach+0x80/0xe0)
r8:00000000 r7:c0389a3c r6:c01bd708 r5:c036256c r4:c0362644
从上面的栈回溯信息可以知道内核出错时的函数调用关系如下,
最后在 s3c2410fb_probe
函数内部崩溃。
do_exit ->
kernel_init ->
s3c2410fb_init ->
platform_driver_register ->
driver_register ->
bus_add_driver ->
driver_attach ->
bus_for_each_dev ->
__driver_attach ->
driver_probe_device ->
platform_drv_probe ->
s3c2410fb_probe

 

(3)根据 pc 寄存器的值确定出错位置。

上述 Oops 信息中出错时的寄存器值如下:PC is at s3c2410fb_probe+0x18/0x560
LR is at platform_drv_probe+0x20/0x24
pc : [<c001a70c>]
lr : [<c01bf4e8>]
psr: a0000013
...
“PC is at s3c2410fb_probe+0x18/0x560”表示出错指令为 s3c2410fb_probe 函数中偏移为
0x18 的指令。
“pc : [<c001a70c>]”表示出错指令的地址为 c001a70c(十六进制)。

 

(4)结合内核源代码和反汇编代码定位问题。

先生成内核的反汇编代码 vmlinux.dis,执行以下命令:

$ cd /work/system/linux-2.6.22.6
$ arm-linux-objdump -D vmlinux > vmlinux.dis
出错地址 c001a70c 附近的部分汇编代码如下:
c001a6f4 <s3c2410fb_probe>:
c001a6f4: e1a0c00d mov ip, sp
c001a6f8: e92ddff0 stmdb
c001a6fc: e24cb004 sub fp, ip, #4 ; 0x4
c001a700: e24dd010 sub sp, sp, #16 ; 0x10
c001a704: e59f34e0 ldr r3, [pc, #1248] ; c001abec <.init+0x1284c>
c001a708: e3a07000 mov r7, #0
c001a70c: e5873000 str r3, [r7]
c001a710: e59030fc ldr r3, [r0, #252]
sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}
; 0x0
<===========出错指令
出错指令为“str r3, [r7]”
,它把 r3 寄存器的值放到内存中,内存地址为 r7 寄存器的值。
根据 Oops 信息中的寄存器值可知:r3 为 0x00001234,r7 为 0。0 地址不可访问,所以出错。
s3c2410fb_probe 函数的部分 C 代码如下:
static int __init s3c2410fb_probe(struct platform_device *pdev)
{
struct s3c2410fb_info *info;
struct fb_info
*fbinfo;
struct s3c2410fb_hw *mregs;
int ret;
int irq;
int i;
u32 lcdcon1;
int *ptest = NULL;
*ptest = 0x1234;
mach_info = pdev->dev.platform_data;
结合反汇编代码,很容易知道是“*ptest = 0x1234;”导致错误,其中的 ptest 为空。
对于大多数情况,从反汇编代码定位到 C 代码并不会如此容易,这需要较强的阅读汇编
程序的能力。通过栈回溯信息知道函数的调用关系,这已经可以帮助定位很多问题了。

 

posted @ 2017-07-20 17:04  yooooooo  阅读(3653)  评论(0)    收藏  举报