linux 調試
目錄[-] 一 調試前的準備 二 核心中的bug 三 核心調試配置選項 1 核心配置 2 調試原子操作 四 引發bug並列印資訊 1 BUG()和BUG_ON() 2 dump_stack() 五 printk() 1 printk函數的健壯性 2 printk函數脆弱之處 3 LOG等級 4 記錄緩衝區 5 syslogd/klogd 6 dmesg 7 注意 8 核心printk和日誌系統的總體結構 9 動態調試 六 記憶體調試工具 1 MEMWATCH 2 YAMD 3 Electric Fence 七 strace 八 OOPS 1 ksymoops 2 kallsyms 3 Kdump 九 KGDB 1 kgdb的調試原理 2 Kgdb的安裝與設定 3 在VMware中搭建調試環境 4 kgdb的一些特點和不足 十 使用SkyEye構建Linux核心調試環境 1 SkyEye的安裝和μcLinux核心編譯 2 使用SkyEye調試 3 使用SkyEye調試核心的特點和不足 十一 KDB 1 入門 2 初始化並設定環境變數 3 啟用 KDB 4 KDB 命令 5 技巧和訣竅 6 結束語 十二 Kprobes 1 安裝 2 編寫 Kprobes 模組 3 使用 Kprobes 更好地進行調試 核心開發比使用者空間開發更難的一個因素就是核心調試艱難。核心錯誤往往會導致系統宕機,很難保留出錯時的現場。調試核心的關鍵在於你的對核心的深刻理解。
一 調試前的準備 在調試一個bug之前,我們所要做的準備工作有:
有一個被確認的bug。 包含這個bug的核心版本號碼,需要分析出這個bug在哪一個版本被引入,這個對於解決問題有極大的協助。可以採用二分尋找法來逐步鎖定bug引入版本號碼。 對核心代碼理解越深刻越好,同時還需要一點點運氣。 該bug可以複現。如果能夠找到複現規律,那麼離找到問題的原因就不遠了。 最小化系統。把可能產生bug的因素逐一排除掉。
二 核心中的bug 核心中的bug也是多種多樣的。它們的產生有無數的原因,同時表象也變化多端。從隱藏在原始碼中的錯誤到展現在目擊者面前的bug,其發作往往是一系列連鎖反應的事件才可能出發的。雖然核心調試有一定的困難,但是通過你的努力和理解,說不定你會喜歡上這樣的挑戰。
三 核心調試配置選項 學習編寫驅動程式要構建安裝自己的核心(標準主線核心)。最重要的原因之一是:核心開發人員已經建立了多項用於調試的功能。但是由於這些功能會造成額外的輸出,並導致能下降,因此發行版廠商通常會禁止發行版核心中的調試功能。
1 核心配置
為了實現核心調試,在核心配置上增加了幾項: view source print ?
| 04 |
[*] Debug slab memory allocations |
| 05 |
[*] Spinlock and rw-lock debugging: basic checks |
| 06 |
[*] Spinlock debugging: sleep-inside-spinlock checking |
| 07 |
[*] Compile the kernel with debug info |
| 09 |
Generic Driver Options ---> |
| 10 |
[*] Driver Core verbose debug messages |
| 12 |
[*] Configure standard kernel features (for small systems) ---> |
| 13 |
[*] Load all symbols for debugging/ksymoops |
啟用選項例如:
view source print ?
| 1 |
slab layer debugging(slab層調試選項) |
| 2 |
high-memory debugging(高端記憶體調試選項) |
| 3 |
I/O mapping debugging(I/O映射調試選項) |
| 4 |
spin-lock debugging(自旋鎖調試選項) |
| 5 |
stack-overflow checking(棧溢出檢查選項) |
| 6 |
sleep-inside-spinlock checking(自旋鎖內睡眠選項) |
2 調試原子操作 從核心2.5開發,為了檢查各類由原子操作引發的問題,核心提供了極佳的工具。
核心提供了一個原子操作計數器,它可以配置成,一旦在原子操作過程中,進城進入睡眠或者做了一些可能引起睡眠的操作,就列印警告資訊並提供追蹤線索。
所以,包括在使用鎖的時候調用schedule(),正使用鎖的時候以阻塞方式請求分配記憶體等,各種潛在的bug都能夠被探測到。
下面這些選項可以最大限度地利用該特性:
view source print ?
| 2 |
CONFIG_DEBUG_KERNEL = y |
| 4 |
CONFIG_SPINLOCK_SLEEP = y |
四 引發bug並列印資訊
1 BUG()和BUG_ON()
一些核心調用可以用來方便標記bug,提供斷言並輸出資訊。最常用的兩個是BUG()和BUG_ON()。
定義在<include/asm-generic>中:
view source print ?
| 03 |
printk("BUG: failure at %s:%d/%s()! ", __FILE__, __LINE__, __FUNCTION__); |
| 04 |
panic("BUG!"); /* 引發更嚴重的錯誤,不但列印錯誤訊息,而且整個系統業會掛起 */ |
| 08 |
#ifndef HAVE_ARCH_BUG_ON |
| 09 |
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0) |
當調用這兩個宏的時候,它們會引發OOPS,導致棧的回溯和錯誤訊息的列印。
※ 可以把這兩個調用當作斷言使用,如:BUG_ON(bad_thing);
2 dump_stack() 有些時候,只需要在終端上列印一下棧的回溯資訊來協助你調試。這時可以使用dump_stack()。這個函數只在終端上列印寄存器上下文和函數的跟蹤線索。
view source print ?
| 2 |
printk(KERN_DEBUG “provide some information…/n”); |
五 printk() 核心提供的格式化列印函數。
1 printk函數的健壯性 健壯性是printk最容易被接受的一個特質,幾乎在任何地方,任何時候核心都可以調用它(中斷上下文、進程上下文、持有鎖時、多處理器處理時等)。
2 printk函數脆弱之處 在系統啟動過程中,終端初始化之前,在某些地方是不能調用的。如果真的需要調試系統啟動過程最開始的地方,有以下方法可以使用:
使用串口調試,將調試資訊輸出到其他終端裝置。 使用early_printk(),該函數在系統啟動初期就有列印能力。但它只支援部分硬體體系。
3 LOG等級 printk和printf一個主要的區別就是前者可以指定一個LOG等級。核心根據