https://read01.com/55a8R4.html

Oops中的error code解釋

當核心發生異常(比如非法地址存取)時,產生Oops,常見的列印如下:
...
BUG:unable to handle kernel paging request at f80c6131
*pdpt=0000000000d08001 *pde=00000000372a0067 *pte=0000000000000000
0ops:0010 [#1] SMP
Modules linked in:...
...

其中的0010即為error code,當異常發生時,由硬體壓入堆疊中。可以通過這個看出Oops發生的大致原因。
對於x86架構來說,error code具體定義如下:


// Page fault error code bits:
enum x86_pf_error_code {
PF_PROT = 1 << 0,          // bit 0 == 0: no page found 1: protection fault
PF_WRITE = 1 << 1,       // bit 1 == 0: read access 1: write access
PF_USER = 1 << 2,          // bit 2 == 0: kernel-mode access 1: user-mode access
PF_RSVD = 1 << 3,         // bit 3 == 1: use of reserved bit detected
PF_INSTR = 1 << 4,        // bit 4 == 1: fault was an instruction fetch
};


常用低3位,具體含義為:
1、如果第0位被清0,則異常是由一個不存在的頁所引起的;否則是由無效的存取權限引起的。
2、如果第1位被清0,則異常由讀存取或者執行存取所引起;否則異常由寫存取引起。
3、如果第2位被清0,則異常發生在核心態;否則異常發生在用戶態。

所以,上述樣例中的error code 0010,表示:
1、異常由無效的存取權限引起,也就是說被存取的地址存在對應的物理頁,但是沒有權限存取。
2、異常由寫操作引起
3、異常發生在核心態
總結來說就是該異常由於在核心態對沒有寫權限的地址進行寫操作時產生。

Oops中的 [#1]crash發生次數。Oops中的PREEMPT是指系統支持搶占模式,有時會還會輸出SMP(多核) ARM/THUMB(指令集)等信息。

有時候,Oops還會列印出Tainted信息。這個信息用來指出核心是因何種原因被tainted(直譯為「玷污」)。具體的定義如下:

1: 'G' if all modules loaded have a GPL or compatible license, 'P' if any proprietary module has been loaded. Modules without a MODULE_LICENSE or with a MODULE_LICENSE that is not recognised by insmod as GPL compatible are assumed to be proprietary.
2: 'F' if any module was force loaded by "insmod -f", ' ' if all modules were loaded normally.
3: 'S' if the oops occurred on an SMP kernel running on hardware that hasn't been certified as safe to run multiprocessor. Currently this occurs only on various Athlons that are not SMP capable.
4: 'R' if a module was force unloaded by "rmmod -f", ' ' if all modules were unloaded normally.
5: 'M' if any processor has reported a Machine Check Exception, ' ' if no Machine Check Exceptions have occurred.
6: 'B' if a page-release function has found a bad page reference or some unexpected page flags.
7: 'U' if a user or user application specifically requested that the Tainted flag be set, ' ' otherwise.
8: 'D' if the kernel has died recently, i.e. there was an OOPS or BUG.
9: 'A' if the ACPI table has been overridden.
10: 'W' if a warning has previously been issued by the kernel. (Though some warnings may set more specific taint flags.)
11: 'C' if a staging driver has been loaded.
12: 'I' if the kernel is working around a severe bug in the platform firmware (BIOS or similar).

基本上,這個Tainted信息是留給核心開發者看的。用戶在使用Linux的過程中如果遇到Oops,可以把Oops的內容發送給核心開發者去debug,核心開發者根據這個Tainted信息大概可以判斷出kernel panic時核心運行的環境。如果我們只是debug自己的驅動,這個信息就沒什麼意義了。

Linux雖然沒有藍屏現象,不過Kernel報錯有時也會讓人頭疼。有時重啟後正常,linux系統運行一段時間後又down了,總不能出現問題就reboot啊。我從網上搜集一下資料,整理了出來,希望大家能在評論與我交流您的看法與經驗。

上面兩種Oops:第一種是UND_32表示是發生了用戶空間,第二種SVC_32表示發生在核心空間。pc表示出問題的地址。

幾個常識概念:

1.程序計數器PC R15),可以作為一般的通用暫存器使用,但有一些指令在使用R15時有一些限制。由於ARM採用了流水線處理器機制,當正確讀取了PC的值時,該值為當前指令地址值加上8個字節。也就是說,對於ARM指令集來說,PC指向當前指令的下兩條指令的地址。由於ARM指令是字對齊的,PC值的第0位和第一位總為 0

2.暫存器R13(SP),通常用作堆疊指標,每一種模式都有自己的物理R13,程序初始化R13。當進入該模式

時,可以將要使用的暫存器保存在R13所指的堆疊中,當退出時,將彈出,從而實現了現場保護。

3.暫存器R14被稱為連結暫存器(LR),當中存放每種模式下,當前子程序的返回地址或者

發生異常中斷的時候,將R14設置成異常模式將要返回的地址。

4.暫存器R12

在給linux核心添加netfilteriptables配置後,生成核心。下載到開發板的後啟動插入網路線出現錯誤提示:

eth1: link up, 100Mbps, full-duplex, lpa 0xCDE1 //插入網路線後提示

首先這些列印是kernelpanic函數列出的,具體意義可以直接找到kernel程式碼去看,很有幫助。
Unable to handle kernel paging request at virtual address 06400040

空指標錯誤,這個一般就是非法地址存取,至於為什麼導致非法,請關注PC周圍的程式碼邏輯。有必要的話就printk出來。

pgd = c0004000
[06400040] *pgd=00000000
Internal error: Oops: 5 [#1] //
錯誤提示 ------5代表什麼?要在你的手冊或kernel程式碼中查,null pointer??

Modules linked in: zd1211rw rt73usb rt2x00usb rt2x00lib asix usbnet mac80211 inp
ut_polldev
CPU: 0 Not tainted (2.6.30.4-LanxumDomas #19)


PC is at skb_release_data+0x74/0xc4

--------當前pc指標,這個十分有用!可以將kernel反彙編,然後找dequeue_task的相對位置0xc

LR is at __kfree_skb+0x1c/0xd0

] lr : [] psr: 20000013
sp : c040dd90 ip : c040dda8 fp : c040dda4
r10: 00000001 r9 : c0465fc8 r8 : c3a22000
r7 : c3b54300 r6 : c3b50004 r5 : c3ae1600 r4 : c3ae1600
r3 : 00000000 r2 : c3b50822 r1 : 00000000 r0 : 06400040
Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: c000717f Table: 33af0000 DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc040c268)
Stack: (0xc040dd90 to 0xc040e000)
dd80: c3ae1600 c3ae1600 c040ddbc c040dda8
dda0: c028266c c0282b04 c3ae19c0 c3ae1600 c040ddcc c040ddc0 c0282794 c0282660

以下是呼叫堆疊,可以看到程序的流程,便於跟蹤。

出現以上錯誤後,可以根據錯誤的提示oops;以下介紹根據錯誤提示進行錯誤定位。

1.首先在編譯生成核心的時候同時生成了一個vmlinux,使用gdb

在核心配置時,make menuconfig 要打開complie with debug info選項。

注意這行: PC is at skb_release_data+0x74/0xc4

這告訴我們,skb_release_data函數有0xc4這麼大,而Oops發生在0x74處。 那麼我們先看一下skb_release_data從哪裡開始:

# grep skb_release_data ./System.map

c0282af4 t skb_release_data

於是我們知道在系統出現錯誤時程序指標在 c0282af4+0x74=c0282b68

2.然後用gdb查看,gdb ./vmlinux (linux目錄下執行),進入調試模式。

(gdb) b *0xc0282b68

Breakpoint 1 at 0xc0282b68: file net/core/skbuff.c ,line312

這就是告訴我們在哪個文件,在哪一行。如此知道了錯誤的位置,具體的原因帶解決。

3,反彙編

(gdb) disassemble 0xc0282b68

panic是英文中是驚慌的意思,Linux Kernel panic正如其名,linux kernel不知道如何走了,它會儘可能把它此時能獲取的全部信息都列印出來。

有兩種主要類型kernel panic

1.hard panic(也就是Aieee信息輸出)
2.soft panic (
也就是Oops信息輸出)

常見Linux Kernel Panic報錯內容:

Kernel panic    -       not syncing fatal exception in interrupt
kernel panic     -       not syncing: Attempted to kill the idle task!
kernel panic     -       not syncing: killing interrupt handler!
Kernel Panic    -       not syncing
Attempted to kill init !

什麼會導致Linux Kernel Panic?

只有加載到核心空間的驅動模塊才能直接導致kernel panic,你可以在系統正常的情況下,使用lsmod查看當前系統加載了哪些模塊。
除此之外,內建在核心里的組件(比如memory map等)也能導致panic

因為hard panicsoft panic本質上不同,因此我們分別討論。

hard panic

一般出現下面的情況,就認為是發生了kernel panic:

  1. 機器徹底被鎖定,不能使用
  2. 數字鍵(Num Lock),大寫鎖定鍵(Caps Lock),滾動鎖定鍵(Scroll Lock)不停閃爍。
  3. 如果在終端下,應該可以看到核心dump出來的信息(包括一段」Aieee」信息或者」Oops」信息)
  4. Windows藍屏相似

原因:

對於hard panic而言,最大的可能性是驅動模塊的中斷處理(interrupt handler)導致的,一般是因為驅動模塊在中斷處理程序中存取一個空指標(null pointre)。一旦發生這種情況,驅動模塊就無法處理新的中斷請求,最終導致系統崩潰。

信息收集
根據panic的狀態不同,核心將記錄所有在系統鎖定之前的信息。因為kenrel panic是一種很嚴重的錯誤,不能確定系統能記錄多少信息,下面是一些需要收集的關鍵信息,他們非常重要,因此儘可能收集全,當然如果系統啟動的時候就kernel panic,那就無法只知道能收集到多少有用的信息了。

  1. /var/log/messages: 幸運的時候,整個kernel panic堆疊跟蹤信息都能記錄在這裡。
  2. 應用程式/庫 日誌: 可能可以從這些日誌信息里能看到發生panic之前發生了什麼。
  3. 其他發生panic之前的信息,或者知道如何重現panic那一刻的狀態
  4. 終端螢幕dump信息,一般OS被鎖定後,複製,粘貼肯定是沒戲了,因此這類信息,你可以需要藉助數位相機或者原始的紙筆工具了。

如果kernel dump信息既沒有在/var/log/message里,也沒有在螢幕上,那麼嘗試下面的方法來獲取(當然是在還沒有死機的情況下):

  1. 如果在圖形介面,切換到終端介面,dump信息是不會出現在圖形介面的,甚至都不會在圖形模式下的虛擬終端里。
  2. 確保螢幕不黑屏,可以使用下面的幾個方法:
    • setterm -blank 0
    • setterm -powerdown 0
    • setvesablank off
  3. 從終端,拷貝螢幕信息(方法見上)

完整堆疊跟蹤信息的排查方法

堆疊跟蹤信息(stack trace)是排查kernel panic最重要的信息,該信息如果在/var/log/messages日誌里當然最好,因為可以看到全部的信息,如果僅僅只是在螢幕上,那麼最上面的信息可能因為滾屏消失了,只剩下堆疊跟蹤信息的一部分。如果你有一個完整堆疊跟蹤信息的話,那麼就可能根據這些充分的信息來定位panic的根本原因。要確認是否有一個足夠的堆疊跟蹤信息,你只要查找包含」EIP」的一行,它顯示了是什麼函數和模塊呼叫時導致panic

使用核心調試工具(kenrel debugger ,aka KDB)

如果跟蹤信息只有一部分且不足以用來定位問題的根本原因時,kernel debugger(KDB)就需要請出來了。
KDB編譯到核心里,panic發生時,他將核心引導到一個shell環境而不是鎖定。這樣,我們就可以收集一些與panic相關的信息了,這對我們定位問題的根本原因有很大的幫助。

使用KDB需要注意,核心必須是基本核心版本,比如是2.4.18,而不是2.4.18-5這樣子的,因為KDB僅對基本核心有效。

soft panic

症狀:

  1. 沒有hard panic嚴重
  2. 通常導致段錯誤(segmentation fault)
  3. 可以看到一個oops信息,/var/log/messages里可以搜索到』Oops
  4. 機器稍微還能用(但是收集信息後,應該重啟系統)

原因:

凡是非中斷處理引發的模塊崩潰都將導致soft panic。在這種情況下,驅動本身會崩潰,但是還不至於讓系統出現致命性失敗,因為它沒有鎖定中斷處理例程。導致hard panic的原因同樣對soft panic也有用(比如在運行時存取一個空指標)

信息收集:
soft panic發生時,核心將產生一個包含核心符號(kernel symbols)信息的dump數據,這個將記錄在/var/log/messages里。為了開始排查故障,可以使用ksymoops工具來把核心符號信息轉成有意義的數據。

為了生成ksymoops文件,需要:

  • /var/log/messages里找到的堆疊跟蹤文本信息保存為一個新文件。確保刪除了時間戳(timestamp),否則ksymoops會失敗。
  • 運行ksymoops程序(如果沒有,請安裝)
  • 詳細的ksymoops執行用法,可以參考ksymoops(8)手冊。

Oops 信息包含以下幾部分內容。
1 一段文本描述信息。
比如類似「Unable to handle kernel NULL pointer dereference at virtual address 00000000
的信息,它說明了發生的是哪類錯誤。
2 Oops 信息的序號。
比如是第 1 次、第 2 次等。這些信息與下面類似,中括號內的數據表示序號。
Internal error: Oops: 805 [#1]
3
核心中加載的模塊名稱,也可能沒有,以下面字樣開頭。
Modules linked in:
4
發生錯誤的 CPU 的序號,對於單處理器的系統,序號為 0,比如:
CPU: 0
Not tainted (2.6.22.6 #36)
5
發生錯誤時 CPU 的各個暫存器值。
6 當前進程的名字及進程 ID,比如:
Process swapper (pid: 1, stack limit = 0xc0480258)

這並不是說發生錯誤的是這個進程,而是表示發生錯誤時,當前進程是它。錯誤可能發
生在核心程式碼、驅動程序,也可能就是這個進程的錯誤。
7 堆疊信息。
8 堆疊回溯信息,可以從中看出函數呼叫關係,形式如下:
Backtrace:
(s3c2410fb_probe+0x0/0x560) from [] (platform_drv_
probe+0x20/0x24)
...
9
出錯指令附近的指令的機器碼,比如(出錯指令在小括號里)
:
Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)

    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()