? ? 這里記錄平時(shí)使用gdb調(diào)試內(nèi)核KE的步驟和方法. 有不足的地方也請(qǐng)大家指出和完善.
1 必備工具和文件
Gdb,addr2line,vmlinux以及內(nèi)核coredump文件
在64位平臺(tái),gdb和addr2line 分別使用aarch64-linux-android-gdb
aarch64-linux-android-addr2line.
2. 調(diào)試過(guò)程
MTK平臺(tái)coredump文件名為: SYS_MINI_RDUMP,用GAT工具解析DB文件得到.
2.1 啟動(dòng)GDB
aarch64-linux-android-gdb vmlinux? coredump?
aarch64-linux-android-gdb ./vmlinux./aee_exp_backup/db.fatal.00.KE/20151107_170222_178/db.fatal.00.KE.dbg.DEC/SYS_MINI_RDUMP
控制臺(tái)輸出內(nèi)容:
#0 0xffffffc00088d2c8 in eth_start_xmit(skb=0xffffffc023ba8300, net=0xffffffc06d3d2000)
at kernel-3.10/drivers/usb/gadget/u_ether.c:893
(gdb)
可以看出異常點(diǎn)在u__ether.c文件893行.
2.2 gdb常用指令
bt :? ? 打印堆棧調(diào)用信息.
down : 跳轉(zhuǎn)到下一級(jí)FP指針
up? ? :? 回到上一級(jí)FP指針
P? ? :? ?打印變量值
x? ? :? 打印內(nèi)存內(nèi)容? ?
x / (n,f,u為可選參數(shù))
n: 需要顯示的內(nèi)存單元個(gè)數(shù),也就是從當(dāng)前地址向后顯示幾個(gè)內(nèi)存單元的內(nèi)容,一個(gè)內(nèi)存單元的大小由后面的u定義
f:顯示格式
x(hex) 按十六進(jìn)制格式顯示變量。
d(decimal) 按十進(jìn)制格式顯示變量。
u(unsigned decimal) 按十進(jìn)制格式顯示無(wú)符號(hào)整型。
o(octal) 按八進(jìn)制格式顯示變量。
t(binary) 按二進(jìn)制格式顯示變量。
a(address) 按十六進(jìn)制格式顯示變量。
c(char) 按字符格式顯示變量。
f(float) 按浮點(diǎn)數(shù)格式顯示變量
u:每個(gè)單元的大小,按字節(jié)數(shù)來(lái)計(jì)算。默認(rèn)是4 bytes。GDB會(huì)從指定內(nèi)存地址開(kāi)始讀取指定字節(jié),并把其當(dāng)作一個(gè)值取出來(lái),并使用格式f來(lái)顯示
b:1 byte? ? ?h:2 bytes? ? ?w:4 bytes g:8 bytes
比如x/3uh 0x54320表示從內(nèi)存地址0x54320讀取內(nèi)容,h表示以雙字節(jié)為單位,3表示輸出3個(gè)單位,u表示按照十六進(jìn)制顯示。
list? ? ?:? 以c語(yǔ)言列出當(dāng)前函數(shù)內(nèi)容(c語(yǔ)言)
disassemble :以匯編方式列出當(dāng)前函數(shù)內(nèi)容
2.3 異常點(diǎn)分析
可以從last_kmsg或者db文件解析出的SYS_KERNEL_LOG中得知異常類(lèi)型.重要信息為PC和寄存器值.
Unable to handle kernel NULL pointerdereference at virtual address 000000e4
[6464.203080]<0>-(0)[3:ksoftirqd/0]PC is at eth_start_xmit+0x1fc/0x748
[6464.203112]<0>-(0)[3:ksoftirqd/0]LR is at eth_start_xmit+0x1d8/0x748
[6464.203143]<0>-(0)[3:ksoftirqd/0]pc : [] lr :[] pstate: 800001c5
[6464.203168]<0>-(0)[3:ksoftirqd/0]sp : ffffffc071877b40
[ 6464.203192]<0>-(0)[3:ksoftirqd/0]x29:ffffffc071877b40 x28: 00000000000005bc
[6464.203231]<0>-(0)[3:ksoftirqd/0]x27: 00000000000005bc x26:ffffffc01ee14c40
[6464.203270]<0>-(0)[3:ksoftirqd/0]x25: ffffffc06ee12510 x24:ffffffc06d3d2730
[ 6464.203308]<0>-(0)[3:ksoftirqd/0]x23:ffffffc023ba8300 x22: ffffffc06d3d2720
[6464.203347]<0>-(0)[3:ksoftirqd/0]x21: ffffffc06d3d2000 x20:ffffffc06d3d2700
[6464.203385]<0>-(0)[3:ksoftirqd/0]x19: ffffffc00141e000 x18:0000000000000000
[ 6464.203422]<0>-(0)[3:ksoftirqd/0]x17:0000007f7ed6fcf8 x16: ffffffc000278828
[6464.203459]<0>-(0)[3:ksoftirqd/0]x15: 0000007f7eda9a24 x14:228f252b6f65a378
[6464.203498]<0>-(0)[3:ksoftirqd/0]x13: 9939719eb9fc9521 x12:0260832913e230f2
[6464.203535]<0>-(0)[3:ksoftirqd/0]x11: 63530fe2e6e696f3 x10:399aa79385bb3861
[6464.203573]<0>-(0)[3:ksoftirqd/0]x9 : 01a6b3c12e057068 x8 :2421eada8933ba1d
[6464.203610]<0>-(0)[3:ksoftirqd/0]x7 : e4324d79f1892abb x6 :ffffffc0393165bc
[6464.203646]<0>-(0)[3:ksoftirqd/0]x5 : 0000000000000000 x4 :0000000000000003
[6464.203682]<0>-(0)[3:ksoftirqd/0]x3 : 0000000000000002 x2 :0000000000000000
[6464.203718]<0>-(0)[3:ksoftirqd/0]x1 : 0000000000000140 x0 :ffffffc06d3d2000
啟動(dòng)gdb時(shí)會(huì)顯示最后出現(xiàn)點(diǎn), 以上面的異常來(lái)分析,u_ether.c:893
C語(yǔ)言代碼為:
if ((dev->tx_skb_hold_count dl_max_pkts_per_xfer) && (length <(dev->port_usb->dl_max_transfer_len - dev->net->mtu)))
從上面的log看,是由NULL指針引起. 這里涉及到三個(gè)指針,dev, dev->port_usb, dev->net.
那么怎么查找到底是哪個(gè)指針有問(wèn)題了?
2.3.1直接打印變量值
p dev
$1 =
可以看出已經(jīng)被編譯器優(yōu)化了,無(wú)法用p直接打印
2.3.2 PC+偏移量法
首先確定偏移量:
p&(((struct eth_dev *)0)->net)
$1 = (struct net_device **) 0x10
(gdb) p &(((struct eth_dev*)0)->port_usb)
$2 = (struct gether **) 0x8
p (((struct gether*)0)->dl_max_transfer_len)
Cannot access memory at address 0xe4
p (((struct net_device *)0)->mtu)
Cannot access memory at address 0x1b8
可以看出dev->net和port_usb的偏移量為16和8,
dl_max_transfer_len和mtu的偏移量為:0xe4 ,0x1b8
在log中提示無(wú)法處理虛擬地址為0x000000e4
Unable to handle kernel NULL pointerdereference at virtual address 000000e4
而dl_max_transfer_len的偏移量剛好為0xe4,則可以證明port_usb為空指針.
2.3.3 匯編+偏移量+寄存器
用disassemble 打印出當(dāng)前函數(shù)的匯編語(yǔ)言(這里只列舉部分)
再查找16,8, 228(0xe4),440(0x1b8)
0xffffffc00088d27c <+432>:? ? ? ? ?bl? ? ? 0xffffffc0004803c0
0xffffffc00088d280 <+436>:? ? ? ?ldr? ? w28, [x23,#104]
0xffffffc00088d284 <+440>:? ? ? ?ldr? ? w1, [x26,#-56]
0xffffffc00088d288 <+444>:? ? ? ?mov x0, x23
0xffffffc00088d28c <+448>:? ? ? ?add? w28, w28, w1
0xffffffc00088d290 <+452>:? ? ? ?str? ? w28, [x26,#-56]
0xffffffc00088d294 <+456>:? ? ? ?mov w27, w28
0xffffffc00088d298 <+460>:? ? ? ?bl? ? ? 0xffffffc0009ce020
0xffffffc00088d29c <+464>:? ? ? ?mov x0, x22
0xffffffc00088d2a0 <+468>:? ? ? ?bl? ? ? 0xffffffc000b52434<_raw_spin_lock_irqsave>
0xffffffc00088d2a4 <+472>:? ? ? ?mov x1, x0
0xffffffc00088d2a8 <+476>:? ? ? ?ldr? ? w2, [x20,#88]? /*dev->tx_skb_hold_count */
0xffffffc00088d2ac <+480>:? ? ? ?ldr? ? w4, [x20,#136]
0xffffffc00088d2b0 <+484>:? ? ? ?add? w2, w2, #0x1
0xffffffc00088d2b4 <+488>:? ? ? ?str? ? w2, [x20,#88]
0xffffffc00088d2b8 <+492>:? ? ? ?cmp w2, w4
0xffffffc00088d2bc <+496>:? ? ? ?b.cs? 0xffffffc00088d2dc
---Type to continue, or q to quit---
0xffffffc00088d2c0 <+500>:? ? ? ?ldr? ? x2, [x20,#8] /*dev->port_usb*/
0xffffffc00088d2c4 <+504>:? ? ? ?ldr? ? x0, [x20,#16]/*dev->net*/
=> 0xffffffc00088d2c8<+508>:? ? ? ? ldr? ? w2, [x2,#228]/*dev->port_usb->dl_max_transfer_len*/
0xffffffc00088d2cc <+512>:? ? ? ?ldr? ? w0, [x0,#440]/*dev->net->mtu*/
PC在0xffffffc00088d2c8出現(xiàn)異常,說(shuō)明x2寄存器為NULL,可以證明dev->port_usb為NULL 。
另外這里寄存器x20保存有dev的指針,x20的值為ffffffc06d3d2700 ,也可嘗試用p打印這個(gè)地址,port_usb的確為NULL.
p *(struct eth_dev*)0xffffffc06d3d2700
$10 = {lock = {{rlock = {raw_lock = {lock =0}, break_lock = 0}}}, port_usb = 0x0, net =0xffffffc06d3d2000, gadget = 0xffffffc06ee132a0, req_lock = {{rlock = {
raw_lock = {lock = 1}, break_lock= 0}}}, reqrx_lock = {{rlock = {raw_lock = {lock = 0}, break_lock = 0}}},tx_reqs = {next = 0xffffffc06d3d2730,
prev = 0xffffffc06d3d2730}, rx_reqs = {next = 0xffffffc06d3d2740, prev =0xffffffc06d3d2740}, tx_qlen = 1, no_tx_req_used = 0, tx_skb_hold_count = 1,
tx_req_bufsize = 4740, rx_frames = {next = 0xffffffc06d3d2760, prev =0xffffffc06d3d2760, qlen = 0, lock = {{rlock = {raw_lock = {lock = 0},break_lock = 0}}}},
header_len = 0, ul_max_pkts_per_xfer = 1, dl_max_pkts_per_xfer = 3, wrap= 0x0, unwrap = 0x0, work = {data = {counter = 68719476704}, entry = {
next = 0xffffffc06d3d27a8, prev = 0xffffffc06d3d27a8}, func =0xffffffc00089dda8 }, rx_work = {data = {counter = 512}, entry= {
next = 0xffffffc06d3d27c8, prev = 0xffffffc06d3d27c8}, func =0xffffffc00088db5c }, rx_work1 = {data = {counter = 512},entry = {
next = 0xffffffc06d3d27e8, prev = 0xffffffc06d3d27e8}, func = 0xffffffc00089dd74}, todo = 0, zlp = false,
host_mac = "\246\030\003", }
3. 調(diào)試總結(jié)
1 調(diào)試時(shí)需要確定vmlinux與DB文件對(duì)應(yīng).不然無(wú)法精準(zhǔn)定位, 打開(kāi)vmlinux ,搜索SMP關(guān)鍵字,可以確認(rèn)vmlinu的編譯時(shí)間.
2. 內(nèi)存標(biāo)示
有時(shí)用p打印出變量的值全部為0x6b6b6b6b,這說(shuō)明內(nèi)存已經(jīng)被其他地方釋放
內(nèi)核有定義.
#define POISON_INUSE 0x5a
/* for use-uninitialised poisoning */
#define POISON_FREE 0x6b
/* for use-after-free poisoning */
#define POISON_END 0xa5??
/* end-byte of poisoning */
?
評(píng)論