- 論壇徽章:
- 15
|
本帖最后由 humjb_1983 于 2014-07-16 21:19 編輯
(一時(shí)心血來潮總結(jié)的,供大家參考,時(shí)間倉促,不足之處勿拍磚,歡迎討論~)
Crash工具用于解析Vmcore文件,Vmcore文件為通過kdump等手段收集的操作系統(tǒng)core dump信息,在不采用壓縮的情況下,其相當(dāng)于整個(gè)物理內(nèi)存的鏡像,所以其中包括了最全面、最完整的信息,對于分析定位各種疑難問題有極大的幫助。配置kdump后,在內(nèi)核panic后,會(huì)自動(dòng)進(jìn)入kump流程,搜集Vmcore。
Crash工具即為專門用于分析vmcore文件的工具,其中提供了大量的實(shí)用分析命令,極大的提高了vmcore的分析效率。
在分析vmcore的過程中,常常需要解析內(nèi)核某個(gè)流程中的關(guān)鍵變量的值,以便確認(rèn)故障當(dāng)時(shí)系統(tǒng)的狀態(tài),本文主要介紹變量的解析方法,主要分全局變量和局部變量兩種情況。
1、全局變量解析非常簡單,可通過在crash中直接p <變量名>打印,如:- crash> p jiffies
- jiffies = $3 = 5540265294
復(fù)制代碼 2、局部變量的解析比較復(fù)雜。
Vmcore搜集的僅為故障當(dāng)時(shí)內(nèi)存使用情況的一個(gè)快照,是靜態(tài)信息,無法進(jìn)行動(dòng)態(tài)調(diào)試(雖然聽說可以,但沒見過~~),對于某個(gè)進(jìn)程而言,在Vmcore中能發(fā)掘的進(jìn)程上下文信息,通常只有堆棧和寄存器的值。而我們了解,通常局部變量在棧中分配,但也可能直接使用寄存器保存,所以可以(也只能)通過“尋找局部變量跟堆棧或寄存器的關(guān)系”來解析局部變量的值。所以這里分兩種情況:
1)位于棧中的局部變量
這種情況比較常見,此時(shí),局部變量必然位于某一級函數(shù)的堆棧中,該局部變量可能通過指針一級級向底層函數(shù)傳遞,所以可能位于多個(gè)函數(shù)的堆棧中,可以從不同的函數(shù)堆棧中解析。但解析會(huì)比較困難,難點(diǎn)在于難以確認(rèn)相關(guān)變量在堆棧中的具體位置,解析方法很靈活,需要結(jié)合相關(guān)源代碼,仔細(xì)分析流程,找到關(guān)鍵的點(diǎn),更多的取決于分析者的經(jīng)驗(yàn)和代碼理解能力。
如下以實(shí)例說明解析過程:
vmcore中某阻塞的進(jìn)程有如下的堆棧:- crash> bt 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
- #2 [ffff8805f3a235a0] __down at ffffffff814fa992
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
- #7 [ffff8805f3a23700] xfs_buf_read at ffffffffa0523e4c [xfs]
- #8 [ffff8805f3a23730] xfs_trans_read_buf at ffffffffa0519a98 [xfs]
- #9 [ffff8805f3a23780] xfs_read_agf at ffffffffa04cfd26 [xfs]
- #10 [ffff8805f3a237c0] xfs_alloc_read_agf at ffffffffa04cfe99 [xfs]
- #11 [ffff8805f3a237f0] xfs_alloc_fix_freelist at ffffffffa04d28a1 [xfs]
- #12 [ffff8805f3a238d0] xfs_alloc_vextent at ffffffffa04d2e16 [xfs]
復(fù)制代碼 可以看出,進(jìn)程阻塞在信號(hào)量上,需要解析如下函數(shù)中xfs_buf_t *bp變量的值,以確認(rèn)其中bp->b_sema信號(hào)量的狀態(tài)。- void
- xfs_buf_lock(
- xfs_buf_t *bp)
- {
- trace_xfs_buf_lock(bp, _RET_IP_);
- if (atomic_read(&bp->b_io_remaining))
- blk_run_address_space(bp->b_target->bt_mapping);
- down(&bp->b_sema);
- XB_SET_OWNER(bp);
- trace_xfs_buf_lock_done(bp, _RET_IP_);
- }
復(fù)制代碼 該變量是通過入?yún)纳霞壓瘮?shù)傳入的,而跟蹤上級函數(shù)會(huì)發(fā)現(xiàn)其為在上級函數(shù)_xfs_buf_find中分配的局部變量,解析方法有兩種:
A、在上級函數(shù)中通過該變量與堆棧的關(guān)系解析
bp變量是在上級函數(shù)_xfs_buf_find定義局部變量,那么其通常會(huì)在該級棧中分配空間。但難點(diǎn)還在于如何確認(rèn)該變量在堆棧中的位置,關(guān)鍵在于找到“寄存器和堆棧關(guān)聯(lián)”的地方,還得分析關(guān)鍵代碼流程:- _xfs_buf_find(
- xfs_buftarg_t *btp, /* block device target */
- xfs_off_t ioff, /* starting offset of range */
- size_t isize, /* length of range */
- xfs_buf_flags_t flags,
- xfs_buf_t *new_bp)
- {
- xfs_off_t range_base;
- size_t range_length;
- xfs_bufhash_t *hash;
- xfs_buf_t *bp, *n;
- range_base = (ioff << BBSHIFT);
- range_length = (isize << BBSHIFT);
- ...
- found:
- spin_unlock(&hash->bh_lock);
- /* Attempt to get the semaphore without sleeping,
- * if this does not work then we need to drop the
- * spinlock and do a hard attempt on the semaphore.
- */
- if (down_trylock(&bp->b_sema)) {
- if (!(flags & XBF_TRYLOCK)) {
- /* wait for buffer ownership */
- xfs_buf_lock(bp);
- XFS_STATS_INC(xb_get_locked_waited);
- ...
復(fù)制代碼 可以看到,bp是作為xfs_buf_lock函數(shù)的入?yún)魅氲模沁@里應(yīng)該會(huì)通過寄存器或其它方式進(jìn)行傳參,則必然會(huì)對bp所在的堆棧位置進(jìn)行操作,由此應(yīng)能找到bp在堆棧中的位置。
反匯編相關(guān)代碼:- crash> dis -l _xfs_buf_find
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 431
- 0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
- 0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
- 0xffffffffa05234f4 <_xfs_buf_find+4>: push %r15
- 0xffffffffa05234f6 <_xfs_buf_find+6>: push %r14
- ...
- 0xffffffffa05235e6 <_xfs_buf_find+246>: mov %rcx,%rdi
- 0xffffffffa05235e9 <_xfs_buf_find+249>: mov %rcx,-0x58(%rbp) //可以看出rbp偏移0x58即為入?yún)p的值
- 0xffffffffa05235ed <_xfs_buf_find+253>: callq 0xffffffffa05233e0 <xfs_buf_lock> //此處調(diào)用xfs_buf_lock
- ...
復(fù)制代碼 找到調(diào)用xfs_buf_lock函數(shù)的地方,在此之前準(zhǔn)備入?yún),操作了堆?strong>-0x58(%rbp) ,可以看出rbp偏移0x58即為入?yún)p的值
再看看堆棧中-0x58(%rbp)中的內(nèi)容具體是啥:- crash> bt -f 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
- ffff8805f3a23440: 0000000181164d0e ffff880c20518300
- ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
- ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
- ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
- ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
- ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
- ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
- ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
- ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
- ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
- ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
- ffff8805f3a234f0: ffffffff814f9a6d
- ...
- #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- ffff8805f3a23658: ffff8805f563e600 ffff8802ecd78480
- ffff8805f3a23668: 0001400500000000 ffff8805f563e690
- ffff8805f3a23678: ffff8805f3a236b8 0000000000000001
- ffff8805f3a23688: ffff8805f3a21af8 ffff8808fe07c0c0
- ffff8805f3a23698: 0000000000014005 000000003a382231
- ffff8805f3a236a8: 0000000000000001 ffff8805f563e0c0
- rsp--> ffff8805f3a236b8: ffff8805f3a236f8 ffffffffa05237db
復(fù)制代碼 我們知道:
(1)棧的地址是向低地址延伸的,也就是說壓棧時(shí),sp(棧頂?shù)刂?減小。
(2)第一個(gè)壓棧的上級函數(shù)的返回地址,所以其中的ffffffffa05237db為上級函數(shù)的返回地址(從上述堆棧中可以明顯看出~)
在_xfs_buf_find()函數(shù)反匯編的第一句,就對rbp(即上級堆棧棧幀指針)進(jìn)行了壓棧,所以 ffff8805f3a236f8為rbp。
0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
此時(shí)的rsp應(yīng)該就是ffff8805f3a236b8:接下來:
0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
那么此時(shí)的rbp也就等于ffff8805f3a236b8,那么rbp-0x58就是ffff8802ecd78480即為我們苦苦尋找的bp指針了!
通過如下命令驗(yàn)證下該bp指針的內(nèi)容,其類型為xfs_buf_t結(jié)構(gòu)體:- crash> struct xfs_buf_t ffff8802ecd78480
- struct xfs_buf_t {
- b_rbnode = {
- rb_parent_color = 18446612143895321536,
- rb_right = 0x0,
- rb_left = 0x0
- },
- b_file_offset = 500099736064,
- b_buffer_length = 512,
- b_hold = {
- counter = 6
- },
- b_lru_ref = {
- counter = 3
- },
- b_flags = 3145844,
- b_sema = {
- lock = {
- raw_lock = {
- slock = 151718155
- }
- },
- count = 0,
- wait_list = {
- next = 0xffff88008c3f38c8,
- prev = 0xffff8805f3a235a8
- }
- },
復(fù)制代碼 內(nèi)容輸出正常,應(yīng)說明解析是正確的。
從該結(jié)構(gòu)體內(nèi)容可以看出,該xfs_lock被其它進(jìn)程占用了,且等待隊(duì)列中有進(jìn)程正在等待該鎖,進(jìn)一步分析wait_list可以得到每個(gè)等待進(jìn)行的相關(guān)信息,這里不再贅述具體方法。
B、在本級或下級函數(shù)中通過該變量與堆棧的關(guān)系解析
解析關(guān)鍵在于:需要找到引用該變量的關(guān)鍵點(diǎn),比如這里的down(&bp->b_sema)函數(shù),以bp變量所為入?yún),那么就必然?huì)對該變量進(jìn)行操作,比如通過寄存器傳參到down函數(shù)中,在此可以尋找到蛛絲馬跡。
首先,需要對xfs_buf_lock函數(shù)進(jìn)行反匯編:- crash> dis -l xfs_buf_lock
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 933
- 0xffffffffa05233e0 <xfs_buf_lock>: push %rbp
- 0xffffffffa05233e1 <xfs_buf_lock+1>: mov %rsp,%rbp
- 0xffffffffa05233e4 <xfs_buf_lock+4>: sub $0x20,%rsp
- 0xffffffffa05233e8 <xfs_buf_lock+8>: mov %rbx,-0x18(%rbp)
- 0xffffffffa05233ec <xfs_buf_lock+12>: mov %r12,-0x10(%rbp)
- 0xffffffffa05233f0 <xfs_buf_lock+16>: mov %r13,-0x8(%rbp)
- 0xffffffffa05233f4 <xfs_buf_lock+20>: nopl 0x0(%rax,%rax,1)
- ...
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 940
- 0xffffffffa052342a <xfs_buf_lock+74>: lea 0x38(%rbx),%rdi
- 0xffffffffa052342e <xfs_buf_lock+78>: callq 0xffffffff81097bd0 <down>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_trace.h: 325
- 0xffffffffa0523433 <xfs_buf_lock+83>: mov 0x2976e(%rip),%r11d # 0xffffffffa054cba8 <__tracepoint_xfs_buf_lock_done+8>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 943
- 0xffffffffa052343a <xfs_buf_lock+90>: mov 0x8(%rbp),%r12
復(fù)制代碼 找到關(guān)鍵點(diǎn):調(diào)用down()函數(shù)的地方,然后可以發(fā)現(xiàn),在call down之前,就行了相關(guān)寄存器操作,可以推測是進(jìn)行傳參相關(guān)的準(zhǔn)備。
可以發(fā)現(xiàn)有
lea 0x38(%rbx),%rdi
對比代碼
down(&bp->b_sema);
可以看出,入?yún)⑹莃p結(jié)構(gòu)體的一個(gè)成員,剛好跟0x38偏移對應(yīng),由此可推測此時(shí)的rbx寄存器為存放bp指針的寄存器。
接下來,需要尋找rbx寄存器跟堆棧的關(guān)系,需要找到從rbx向堆棧寫、或從堆棧向rbx讀的相關(guān)操作,而在當(dāng)級函數(shù)的反匯編代碼中顯然沒有,需要進(jìn)入下級函數(shù)down()中,看看是否有相關(guān)操作,對down()進(jìn)行反匯編:- crash> dis -l down
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/kernel/semaphore.c: 54
- 0xffffffff81097bd0 <down>: push %rbp
- 0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
- 0xffffffff81097bd4 <down+4>: push %rbx
- 0xffffffff81097bd5 <down+5>: sub $0x18,%rsp
- 0xffffffff81097bd9 <down+9>: nopl 0x0(%rax,%rax,1)
- 0xffffffff81097bde <down+14>: mov %rdi,%rbx
- ...
復(fù)制代碼 可以看出,其中第5行對rbx寄存器進(jìn)行壓棧,bingo!,這正是我們要尋找的,由此說明bp指針的值,必然可以在down()這一級函數(shù)的棧中找到。
解析堆棧:- crash> bt -f 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
- ffff8805f3a23440: 0000000181164d0e ffff880c20518300
- ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
- ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
- ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
- ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
- ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
- ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
- ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
- ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
- ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
- ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
- ffff8805f3a234f0: ffffffff814f9a6d
- ...
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- ffff8805f3a235f8: ffff8805f3a23618 0000000000000292
- ffff8805f3a23608: ffff8802ecd78480 ffff8802ecd78480
- ffff8805f3a23618: ffff8805f3a23648 ffffffffa0523433
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- ffff8805f3a23628: 0000000000016ec0 0000000000016ec0
- ffff8805f3a23638: ffff8808fe07c0c0 ffff8802ecd78490
- ffff8805f3a23648: ffff8805f3a236b8 ffffffffa05235f2
復(fù)制代碼 down()的堆棧在第#3級,再看看down()的反匯編代碼的頭三句:- 0xffffffff81097bd0 <down>: push %rbp
- 0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
- 0xffffffff81097bd4 <down+4>: push %rbx
復(fù)制代碼 顯然,接下來壓棧的是rbp,即ffff8805f3a23648是rbp,即上一級堆棧的棧幀指針。
第3句,即對rbx壓棧,說明rbx(即我們要找的bp指針的值)就位于down()函數(shù)堆棧中的第3個(gè)位置(第1為上級函數(shù)返回地址、第2為rbp),即:
ffff8802ecd78480
所以,我們找到了。。。。
2)位于寄存器中的局部變量
由于Vmcore只是一個(gè)內(nèi)存快照的靜態(tài)數(shù)據(jù),所以其中保存的進(jìn)程上下文中,只保存了最后一級函數(shù)執(zhí)行時(shí)的寄存器內(nèi)容,所以,如果相關(guān)局部變量位于最后一級函數(shù)中,且用寄存器保存,那么此時(shí)可以直接通過相關(guān)進(jìn)程的bt上下文得到:- crash> bt 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
- #2 [ffff8805f3a235a0] __down at ffffffff814fa992
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
- ..
- #24 [ffff8805f3a23f80] system_call_fastpath at ffffffff8100b0f2
- RIP: 0000003885e0ed2d RSP: 00007f43871e36e0 RFLAGS: 00003297
- RAX: 0000000000000002 RBX: ffffffff8100b0f2 RCX: 0000000000000000
- RDX: 0000000000000241 RSI: 0000000000000241 RDI: 00007f43871e3710
- RBP: 00007f43871e365c R8: 00007f43871df440 R9: 0000000000100000
- R10: 0000000000000000 R11: 0000000000003293 R12: ffffffff8117a830
- R13: ffff8805f3a23f78 R14: 00007f43871e3710 R15: 00007f43871e391c
- ORIG_RAX: 0000000000000002 CS: 0033 SS: 002b
復(fù)制代碼 但是,如果需要解析的局部變量位于中間流程,且使用寄存器保存,且不能在最后一級函數(shù)執(zhí)行時(shí)的進(jìn)程上下文中體現(xiàn),那么此類局部變量就無法解析了。。 |
評分
-
查看全部評分
|