天天看點

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 代碼并不會如此容易,這需要較強的閱讀彙編

程式的能力。通過棧回溯資訊知道函數的調用關系,這已經可以幫助定位很多問題了。

繼續閱讀