crash处理core文件

(一时心血来潮总结的,供大家参考,时间仓促,不足之处勿拍砖,欢迎讨论~)
Crash工具用于解析Vmcore文件,Vmcore文件为通过kdump等手段收集的操作系统core dump信息,在不采用压缩的情况下,其相当于整个物理内存的镜像,所以其中包括了最全面、最完整的信息,对于分析定位各种疑难问题有极大的帮助。配置kdump后,在内核panic后,会自动进入kump流程,搜集Vmcore。
Crash工具即为专门用于分析vmcore文件的工具,其中提供了大量的实用分析命令,极大的提高了vmcore的分析效率。
在分析vmcore的过程中,常常需要解析内核某个流程中的关键变量的值,以便确认故障当时系统的状态,本文主要介绍变量的解析方法,主要分全局变量和局部变量两种情况。
1、全局变量解析非常简单,可通过在crash中直接p <变量名>打印,如:
  1.     jiffies = $3 = 5540265294
复制代码
2、局部变量的解析比较复杂。
Vmcore搜集的仅为故障当时内存使用情况的一个快照,是静态信息,无法进行动态调试(虽然听说可以,但没见过~~),对于某个进程而言,在Vmcore中能发掘的进程上下文信息,通常只有堆栈和寄存器的值。而我们了解,通常局部变量在栈中分配,但也可能直接使用寄存器保存,所以可以(也只能)通过“寻找局部变量跟堆栈或寄存器的关系”来解析局部变量的值。所以这里分两种情况:
1)位于栈中的局部变量
这种情况比较常见,此时,局部变量必然位于某一级函数的堆栈中,该局部变量可能通过指针一级级向底层函数传递,所以可能位于多个函数的堆栈中,可以从不同的函数堆栈中解析。但解析会比较困难,难点在于难以确认相关变量在堆栈中的具体位置,解析方法很灵活,需要结合相关源代码,仔细分析流程,找到关键的点,更多的取决于分析者的经验和代码理解能力。
如下以实例说明解析过程:
vmcore中某阻塞的进程有如下的堆栈:
  1.     PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
  2.      #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
  3.      #3 [ffff8805f3a235f0] down at ffffffff81097c11
  4.      #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  5.      #7 [ffff8805f3a23700] xfs_buf_read at ffffffffa0523e4c [xfs]
  6.      #9 [ffff8805f3a23780] xfs_read_agf at ffffffffa04cfd26 [xfs]
  7.     #11 [ffff8805f3a237f0] xfs_alloc_fix_freelist at ffffffffa04d28a1 [xfs]
  8. void
  9.         xfs_buf_t        *bp)
  10.         trace_xfs_buf_lock(bp, _RET_IP_);
  11.         if (atomic_read(&bp->b_io_remaining))
  12.         down(&bp->b_sema);

  13.     }
复制代码
该变量是通过入参从上级函数传入的,而跟踪上级函数会发现其为在上级函数_xfs_buf_find中分配的局部变量,解析方法有两种:

A、在上级函数中通过该变量与堆栈的关系解析
bp变量是在上级函数_xfs_buf_find定义局部变量,那么其通常会在该级栈中分配空间。但难点还在于如何确认该变量在堆栈中的位置,关键在于找到“寄存器和堆栈关联”的地方,还得分析关键代码流程:
  1.         xfs_buftarg_t        *btp,    /* block device target        */
  2.         size_t            isize,    /* length of range        */
  3.         xfs_buf_t        *new_bp)
  4.         xfs_off_t        range_base;
  5.         xfs_bufhash_t        *hash;

  6.         range_length = (isize << BBSHIFT);
  7.     found:

  8.          * if this does not work then we need to drop the
  9.          */
  10.             if (!(flags & XBF_TRYLOCK)) {
  11.                 xfs_buf_lock(bp);
  12.     ...
  13. crash> dis -l _xfs_buf_find
  14.     0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
  15.     0xffffffffa05234f4 <_xfs_buf_find+4>: push %r15
  16.     ...
  17.     0xffffffffa05235e9 <_xfs_buf_find+249>: mov %rcx,-0x58(%rbp) //可以看出rbp偏移0x58即为入参bp的值
  18.     ...
复制代码
找到调用xfs_buf_lock函数的地方,在此之前准备入参,操作了堆栈-0x58(%rbp) ,可以看出rbp偏移0x58即为入参bp的值
再看看堆栈中-0x58(%rbp)中的内容具体是啥:
  1.     PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
  2.         ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
  3.         ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
  4.         ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
  5.         ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
  6.         ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
  7.         ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
  8.         ffff8805f3a234f0: ffffffff814f9a6d
  9.      #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  10.         ffff8805f3a23668: 0001400500000000 ffff8805f563e690
  11.         ffff8805f3a23688: ffff8805f3a21af8 ffff8808fe07c0c0
  12.         ffff8805f3a236a8: 0000000000000001 ffff8805f563e0c0
复制代码
我们知道:
(1)栈的地址是向低地址延伸的,也就是说压栈时,sp(栈顶地址)减小。
(2)第一个压栈的上级函数的返回地址,所以其中的ffffffffa05237db为上级函数的返回地址(从上述堆栈中可以明显看出~)
在_xfs_buf_find()函数反汇编的第一句,就对rbp(即上级堆栈栈帧指针)进行了压栈,所以 ffff8805f3a236f8为rbp。
0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
此时的rsp应该就是ffff8805f3a236b8:接下来:
0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
那么此时的rbp也就等于ffff8805f3a236b8,那么rbp-0x58就是ffff8802ecd78480即为我们苦苦寻找的bp指针了!!
通过如下命令验证下该bp指针的内容,其类型为xfs_buf_t结构体:
  1.     struct xfs_buf_t {
  2.         rb_parent_color = 18446612143895321536,
  3.         rb_left = 0x0
  4.       b_file_offset = 500099736064,
  5.       b_hold = {
  6.       },
  7.         counter = 3
  8.       b_flags = 3145844,
  9.         lock = {
  10.             slock = 151718155
  11.         },
  12.         wait_list = {
  13.           prev = 0xffff8805f3a235a8
  14.       },
复制代码
内容输出正常,应说明解析是正确的。
从该结构体内容可以看出,该xfs_lock被其它进程占用了,且等待队列中有进程正在等待该锁,进一步分析wait_list可以得到每个等待进行的相关信息,这里不再赘述具体方法。

B、在本级或下级函数中通过该变量与堆栈的关系解析
解析关键在于:需要找到引用该变量的关键点,比如这里的down(&bp->b_sema)函数,以bp变量所为入参,那么就必然会对该变量进行操作,比如通过寄存器传参到down函数中,在此可以寻找到蛛丝马迹。
首先,需要对xfs_buf_lock函数进行反汇编:
  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: 933
  2.     0xffffffffa05233e1 : mov %rsp,%rbp
  3.     0xffffffffa05233e8 : mov %rbx,-0x18(%rbp)
  4.     0xffffffffa05233f0 : mov %r13,-0x8(%rbp)
  5. ...
  6.     0xffffffffa052342a : lea 0x38(%rbx),%rdi
  7.     /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
  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
  9. crash> dis -l down
  10.     0xffffffff81097bd0 : push %rbp
  11.     0xffffffff81097bd4 : push %rbx
  12.     0xffffffff81097bd9 : nopl 0x0(%rax,%rax,1)
  13.     ...
复制代码
可以看出,其中第5行对rbx寄存器进行压栈,bingo!,这正是我们要寻找的,由此说明bp指针的值,必然可以在down()这一级函数的栈中找到。
解析堆栈:
  1.     PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
  2.         ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
  3.         ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
  4.         ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
  5.         ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
  6.         ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
  7.         ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
  8.         ffff8805f3a234f0: ffffffff814f9a6d
  9.      #3 [ffff8805f3a235f0] down at ffffffff81097c11
  10.         ffff8805f3a23608: ffff8802ecd78480 ffff8802ecd78480
  11.      #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
  12.         ffff8805f3a23638: ffff8808fe07c0c0 ffff8802ecd78490
  13. 0xffffffff81097bd0 : push %rbp
  14. 0xffffffff81097bd4 : push %rbx
复制代码
显然,接下来压栈的是rbp,即ffff8805f3a23648是rbp,即上一级堆栈的栈帧指针。
第3句,即对rbx压栈,说明rbx(即我们要找的bp指针的值)就位于down()函数堆栈中的第3个位置(第1为上级函数返回地址、第2为rbp),即:
ffff8802ecd78480
所以,我们找到了。。。。

2)位于寄存器中的局部变量
由于Vmcore只是一个内存快照的静态数据,所以其中保存的进程上下文中,只保存了最后一级函数执行时的寄存器内容,所以,如果相关局部变量位于最后一级函数中,且用寄存器保存,那么此时可以直接通过相关进程的bt上下文得到:
  1.     PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
  2.      #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
  3.      #3 [ffff8805f3a235f0] down at ffffffff81097c11
  4.      #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  5.     ..
  6.         RIP: 0000003885e0ed2d RSP: 00007f43871e36e0 RFLAGS: 00003297
  7.         RDX: 0000000000000241 RSI: 0000000000000241 RDI: 00007f43871e3710
  8.         R10: 0000000000000000 R11: 0000000000003293 R12: ffffffff8117a830
  9.         ORIG_RAX: 0000000000000002 CS: 0033 SS: 002b
复制代码
但是,如果需要解析的局部变量位于中间流程,且使用寄存器保存,且不能在最后一级函数执行时的进程上下文中体现,那么此类局部变量就无法解析了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值