6.4 必修實(shí)驗(yàn)3--內(nèi)核異常分析(3) 接下來的這些信息,和這個模塊的調(diào)試沒多大關(guān)系,它們是虛擬內(nèi)存頁目錄、頁表信息、oops錯誤號以及最后訪問的sysfs文件等。 - pgd = c39d8000
- [00000000] *pgd=339cf031, *pte=00000000, *ppte=00000000
- Internal error: Oops: 817 [#1]
- last sysfs file: /sys/devices/platform/soc-audio/sound/card0/mixer/dev
- Modules linked in: oops(+)
再接下來是寄存器信息,這部分信息比較重要,其中最可能幫助定位錯誤的寄存器當(dāng)然是PC。在這部分信息中,下面這句最為關(guān)鍵。 - PC is at func_D+0x1c/0x28 [oops]
它直接地告訴了我們,oops出錯時,PC是位于func_D函數(shù)標(biāo)號之后的0x1c處(怎么去尋找它?后面會進(jìn)行分析。另外請思考后面的0x28代表什么?)。 寄存器信息之后是棧信息,但這里還用不上,先略過。 最后的部分,也就是Backtrace標(biāo)號開始的地方,它是oops的精華。它表示回溯信息,也告訴調(diào)試者在oops出錯之前,模塊調(diào)用了那些函數(shù)。當(dāng)然,在本實(shí)例中,可以看到模塊調(diào)用了func_D后就出錯了,顯然錯誤就在func_D中了。 結(jié)尾部分還有一點(diǎn)信息請注意。
- Code: e59f0010 eb412fb6 e3a0200b e3a03000 (e5832000)
Code標(biāo)號開始的字段記錄了模塊出錯前最后幾條機(jī)器碼,其中被括號括起來的就是oops出錯對應(yīng)的機(jī)器碼。 (4)根據(jù)上面的分析,可以使用反匯編來確定出錯的位置。在RHEL5中,使用命令:arm-linux-objdump -D -S oops.ko >log,將模塊文件反匯編到log中,使用vim打開該文件log,直接找到func_D標(biāo)號處,如圖6-17所示。
| (點(diǎn)擊查看大圖)圖6-17 反匯編結(jié)果 |
根據(jù)前面的信息,出錯位置應(yīng)該在func_D+0x1c處,func_D在0x1c,所以出錯地址應(yīng)該是0x38??纯催@句匯編代碼,前面的語句將寄存器r3賦值為0,然后這句又試圖將寄存器r2的值存入到r3指向的地址處,也就是向0地址寫。因此出錯。再來看看這句出錯代碼對應(yīng)的機(jī)器碼e5832000,顯然就是之前在opps的Code字段中看到的被括起來的那個。 (5)通過反匯編程序,定位了匯編代碼中的錯誤位置,但對于用C語言編寫的內(nèi)核模塊而言,這樣還不夠。如何才能準(zhǔn)確地定位到C語言中的語句呢?回憶一下在<<Linux應(yīng)用程序開發(fā)班>>中學(xué)習(xí)gdb調(diào)試時,能夠在調(diào)試中看到對應(yīng)的源碼。當(dāng)時為了進(jìn)行g(shù)db調(diào)試,在編譯時加入了-g選項(xiàng),這樣可以將調(diào)試信息加入到目標(biāo)文件中。由此得到靈感,在這里也加入-g試試。進(jìn)入實(shí)驗(yàn)代碼目錄2-3-1中的內(nèi)核源碼目錄,修改其頂層Makefile,如圖6-18所示。 | (點(diǎn)擊查看大圖)圖6-18 內(nèi)核調(diào)試信息開關(guān) |
提示 可見編譯模塊時加入調(diào)試信息,需要定義CONFIG_DEBUG_INFO這個宏,由于它默認(rèn)是關(guān)閉的,所以內(nèi)核并沒有啟用-g選項(xiàng),可以暫時把這個宏開關(guān)注釋掉,使KBUILD_CFLAGS標(biāo)識擁有-g選項(xiàng)。 (6)修改內(nèi)核Makefile后,再次編譯模塊。然后將新得到的oops.ko反匯編,使用命令:arm-linux-objdump -D -S oops.ko >log。用vim打開log文件。這時,通過匯編混合C語言調(diào)試信息的結(jié)果,結(jié)合前面的分析,可以很輕松的定位到C語言的錯誤語句就出現(xiàn)在"*p = a + 5"處,如圖6-19所示。
| (點(diǎn)擊查看大圖)圖6-19 定位出錯的C語言語句 |
5.總結(jié) 通過本節(jié)實(shí)驗(yàn),可以學(xué)會oops信息的分析方法,掌握利用oops信息調(diào)試內(nèi)核模塊的基本方法。下面列出利用oops定位出錯點(diǎn)的基本步驟。 (1)oops出錯時,首先搜集到所有oops打印信息,如果模塊中本身有很多printk打印語句,首先根據(jù)oops開頭的打印信息分析出錯點(diǎn)的大概位置。 (2)通過Backtrace字段,分析發(fā)生oops錯誤前模塊程序的執(zhí)行路徑,將范圍縮小到某個函數(shù)中。 (3)如果通過前面兩步仍無法定位出錯點(diǎn),那么就直接通過PC來定位。查看出錯時 oops信息中打印出的PC的值并記錄下來。在內(nèi)核Makefile中加入-g選項(xiàng),重新編譯模塊。 (4)通過objdump反匯編該模塊。在反匯編得出的匯編混合C語言調(diào)試信息的代碼中,結(jié)合前3步的分析結(jié)論,精確定位出錯點(diǎn)的位置。
|