1. 程式人生 > >通過gdb定位核心宕機

通過gdb定位核心宕機

核心宕機不要panic,我們有gdb,通過dump出來資訊可以快速定位出出錯的地方。下面就以一個實際遇到的例子描述一下怎麼通過gdb找到實際出錯的程式碼行。

  Unable to handle kernel paging request for data at address 0x000001d0

  Faulting instruction address: 0xc0220820

  Oops: Kernel access of bad area, sig: 11 [#1]

  MPC85xx CDS

  Modules linked in: hsl linux_bcm_diag_full(P) linux_uk_proxy(P) linux_kernel_bde(P) wdtDrv pciDrv i2cDrv flashDrv cpuDrv

  NIP: c0220820 LR: c0220818 CTR: c02209a4

  REGS: dde67b20 TRAP: 0300   Tainted: P           (2.6.27.21)

  MSR: 00029000 <EE,ME>  CR: 24000824  XER: 00000000

  DEAR: 000001d0, ESR: 00000000

  TASK = decea500[940] 'nsm' THREAD: dde66000

  GPR00: 00000001 dde67bd0 decea500 00000001 00000001 decea538 00000078 00000000

  GPR08: deceae80 00000000 83d0da00 c02209a4 e8669fbb 10219d44 00000290 ffffffff

  GPR16: 00000001 00000000 100d4464 100e7db4 00000000 00000008 dde67ddc dde67dfc

  GPR24: dde67ddc dde67dfc dde67dc0 e3970000 00000001 00000000 ddac8018 00000000

  NIP [c0220820] rollback_registered+0x2c/0x130

  LR [c0220818] rollback_registered+0x24/0x130

  Call Trace:

  [dde67bd0] [00000008] 0x8 (unreliable)

  [dde67be0] [c022094c] unregister_netdevice+0x28/0x80

  [dde67bf0] [c02209c4] unregister_netdev+0x20/0x38

  [dde67c10] [e393661c] hsl_eth_drv_destroy_netdevice+0x10/0x24 [hsl]

  [dde67c20] [e3938490] hsl_os_l3_if_unconfigure+0x20/0x34 [hsl]

  [dde67c30] [e393aa94] hsl_ifmgr_L3_delete2+0xf0/0x2b4 [hsl]

  [dde67c50] [e394103c] hsl_ifmgr_L3_unregister+0x60/0xfc [hsl]

  [dde67c70] [e3926004] hsl_msg_recv_if_delete_svi+0x54/0x15c [hsl]

  [dde67c90] [e391feb8] hsl_sock_process_msg+0x260/0x7a8 [hsl]

  [dde67ca0] [e392046c] _hsl_sock_sendmsg+0x6c/0xac [hsl]

  [dde67cc0] [c0211fc0] sock_sendmsg+0xac/0xe4

  [dde67db0] [c02121cc] sys_sendmsg+0x1d4/0x284

  [dde67f00] [c0212d40] sys_socketcall+0xe8/0x200

  [dde67f40] [c000df5c] ret_from_syscall+0x0/0x3c

  Instruction dump:

 4bffff48 9421fff0 7c0802a6 3d20c039 93e1000c 7c7f1b78 90010014 800954d8

  0f000000 4800a101 2f830000 419e00e4 <813f01d0> 2f890000 409e0034 3c60c033

  ---[ end trace fc1818f8d9535ae2 ]---

首先介紹一下ppc(powerpc)的暫存器:

通用暫存器:  r0   在函式開始(function prologs)時使用。  r1   堆疊指標,相當於ia32架構中的esp暫存器,idapro把這個暫存器反彙編標識為sp。  r2   內容表(toc)指標,idapro把這個暫存器反彙編標識為rtoc。系統呼叫時,它包含系統呼叫號。  r3   作為第一個引數和返回地址。  r4-r10 函式或系統呼叫開始的引數。  r11   用在指標的呼叫和當作一些語言的環境指標。  r12   它用在異常處理和glink(動態聯結器)程式碼。  r13   保留作為系統執行緒ID。  r14-r31 作為本地變數,非易失性。專用暫存器:  lr   連結暫存器,它用來存放函式呼叫結束處的返回地址。  ctr   計數暫存器,它用來當作迴圈計數器,會隨特定轉移操作而遞減。  xer   定點異常暫存器,存放整數運算操作的進位以及溢位資訊。  msr   機器狀態暫存器,用來配置微處理器的設定。  cr   條件暫存器,它分成84位欄位,cr0-cr7,它反映了某個演算法操作的結果並且提供條件分支的機制。  暫存器r1r14-r31是非易失性的,這意味著它們的值在函式呼叫過程保持不變。暫存器r2也算非易失性,但是隻有在呼叫函式在呼叫後必須恢復它的值時才被處理。

ppc使用了LR暫存器(Link Register)來完成:在bl指令跳轉前,下條指令的地址會被儲存到LR,函式返回時候呼叫 blr,系統會跳轉到LR所表示的地址,完成返回。

------------------------------分割線--------------------------------------------------

我們開始通過gdb查詢宕機的地方:

1. 首先進入核心的編譯目錄,保證程式碼與出錯的核心一直,沒有編譯的話要先編譯,編譯完之後,在當前目錄就會有vmlinux,這個是未壓縮過的核心bin檔案,用gdb執行它(這裡是powerpc平臺):

 gdb4ppc ./vmlinux

2. NIP顯示的下一條指令地址(0xc0220820),使用list 指令顯示出對應的函式:

(gdb) list *0xc0220820

0xc0220820 is in rollback_registered (net/core/dev.c:4051).

4046    {

4047            BUG_ON(dev_boot_phase);

4048            ASSERT_RTNL();

4049

4050            /* Some devices call without registering for initialization unwind. */

4051            if (dev->reg_state == NETREG_UNINITIALIZED) {

4052                    printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "

4053                                      "was registered\n", dev->name, dev);

4054

4055                    WARN_ON(1);

3. 反彙編這個函式 (disassemble /r表示原始的16進位制格式的彙編)

(gdb) disassemble /r rollback_registered

Dump of assembler code for function rollback_registered:

   0xc02207f4 <+0>:      94 21 ff f0    stwu    r1,-16(r1)

   0xc02207f8 <+4>:      7c 08 02 a6    mflr    r0

   0xc02207fc <+8>:      3d 20 c0 39    lis     r9,-16327

   0xc0220800 <+12>:     93 e1 00 0c    stw     r31,12(r1)

   0xc0220804 <+16>:     7c 7f 1b 78    mr      r31,r3

   0xc0220808 <+20>:     90 01 00 14    stw     r0,20(r1)

   0xc022080c <+24>:     80 09 54 d8    lwz     r0,21720(r9)

   0xc0220810 <+28>:     0f 00 00 00    twnei   r0,0

   0xc0220814 <+32>:     48 00 a1 01    bl      0xc022a914 <rtnl_is_locked>

   0xc0220818 <+36>:     2f 83 00 00    cmpwi   cr7,r3,0

   0xc022081c <+40>:        41 9e 00 e4    beq     cr7,0xc0220900 <rollback_registered+268>

   0xc0220820<+44>:     81 3f 01 d0    lwz     r9,464(r31)

   0xc0220824 <+48>:     2f 89 00 00    cmpwi   cr7,r9,0

   0xc0220828 <+52>:     40 9e 00 34    bne     cr7,0xc022085c <rollback_registered+104>

 可以看到彙編與dump資訊裡面的Instruction dump完全對得上,地址0xc022082

對應彙編 81 3f 01 d0dump資訊裡面特意用尖括號標識出來了,看來就死在這一條指令了。

4. 找到對應的程式碼行(disassemble /m 將原始碼嵌入到彙編裡面),分析原因。

(gdb) disassemble /m rollback_registered

Dump of assembler code for function rollback_registered:

4046    {

   0xc02207f4 <+0>:     stwu    r1,-16(r1)

   0xc02207f8 <+4>:     mflr    r0

   0xc0220800 <+12>:    stw     r31,12(r1)

   0xc0220804 <+16>:    mr      r31,r3

   0xc0220808 <+20>:    stw     r0,20(r1)

4047            BUG_ON(dev_boot_phase);

   0xc02207fc <+8>:     lis     r9,-16327

   0xc022080c <+24>:    lwz     r0,21720(r9)

   0xc0220810 <+28>:    twnei   r0,0

4048            ASSERT_RTNL();

   0xc0220814 <+32>:    bl      0xc022a914 <rtnl_is_locked>

   0xc0220818 <+36>:    cmpwi   cr7,r3,0

   0xc022081c <+40>:    beq     cr7,0xc0220900 <rollback_registered+268>

   0xc0220900 <+268>:   lis     r4,-16333

   0xc0220904 <+272>:   lis     r3,-16334

   0xc0220908 <+276>:   addi    r4,r4,-16240

   0xc022090c <+280>:   li      r5,4048

   0xc0220910 <+284>:   addi    r3,r3,-3816

   0xc0220914 <+288>:   crclr   4*cr1+eq

   0xc0220918 <+292>:   bl      0xc0027ca0 <printk>

   0xc022091c <+296>:   bl      0xc0006cfc <dump_stack>

   0xc0220920 <+300>:   b       0xc0220820 <rollback_registered+44>

4049

4050            /* Some devices call without registering for initialization unwind. */

4051            if (dev->reg_state == NETREG_UNINITIALIZED) {

   0xc0220820 <+44>:    lwz     r9,464(r31)

   0xc0220824 <+48>:    cmpwi   cr7,r9,0

   0xc0220828 <+52>:    bne     cr7,0xc022085c <rollback_registered+104>

這樣我們就找到對應的程式碼語句是 rollback_registered

if (dev->reg_state == NETREG_UNINITIALIZED) {

這一行,指令是lwz r9,464(r31)R31存放的是dev指標,我們先確認一下偏移464的地方就是

dev->reg_state

(gdb)    p &((struct net_device *)0)->reg_state

$1 = (enum {...} *) 0x1d0

0x1d0也就是十進位制的464dump資訊裡的R31暫存器是0,即devNULL,這也就是這條指令導致產生訪問非法地址0x000001d0的原因:

Unable to handle kernel paging request for data at address 0x000001d0