(一时心血来潮总结的,供大家参考,时间仓促,不足之处勿拍砖,欢迎讨论~)
Crash工具用于解析Vmcore文件,Vmcore文件为通过kdump等手段收集的操作系统core dump信息,在不采用压缩的情况下,其相当于整个物理内存的镜像,所以其中包括了最全面、最完整的信息,对于分析定位各种疑难问题有极大的帮助。配置kdump后,在内核panic后,会自动进入kump流程,搜集Vmcore。
Crash工具即为专门用于分析vmcore文件的工具,其中提供了大量的实用分析命令,极大的提高了vmcore的分析效率。
在分析vmcore的过程中,常常需要解析内核某个流程中的关键变量的值,以便确认故障当时系统的状态,本文主要介绍变量的解析方法,主要分全局变量和局部变量两种情况。
1、全局变量解析非常简单,可通过在crash中直接p <变量名>打印,如:

  1. crash> p jiffies
  2. jiffies = $3 = 5540265294

复制代码

2、局部变量的解析比较复杂。
Vmcore搜集的仅为故障当时内存使用情况的一个快照,是静态信息,无法进行动态调试(虽然听说可以,但没见过~~),对于某个进程而言,在Vmcore中能发掘的进程上下文信息,通常只有堆栈和寄存器的值。而我们了解,通常局部变量在栈中分配,但也可能直接使用寄存器保存,所以可以(也只能)通过“寻找局部变量跟堆栈或寄存器的关系”来解析局部变量的值。所以这里分两种情况:
1)位于栈中的局部变量
这种情况比较常见,此时,局部变量必然位于某一级函数的堆栈中,该局部变量可能通过指针一级级向底层函数传递,所以可能位于多个函数的堆栈中,可以从不同的函数堆栈中解析。但解析会比较困难,难点在于难以确认相关变量在堆栈中的具体位置,解析方法很灵活,需要结合相关源代码,仔细分析流程,找到关键的点,更多的取决于分析者的经验和代码理解能力。
如下以实例说明解析过程:
vmcore中某阻塞的进程有如下的堆栈:

  1. crash> bt 9242
  2. PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
  3. #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
  4. #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
  5. #2 [ffff8805f3a235a0] __down at ffffffff814fa992
  6. #3 [ffff8805f3a235f0] down at ffffffff81097c11
  7. #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
  8. #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  9. #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
  10. #7 [ffff8805f3a23700] xfs_buf_read at ffffffffa0523e4c [xfs]
  11. #8 [ffff8805f3a23730] xfs_trans_read_buf at ffffffffa0519a98 [xfs]
  12. #9 [ffff8805f3a23780] xfs_read_agf at ffffffffa04cfd26 [xfs]
  13. #10 [ffff8805f3a237c0] xfs_alloc_read_agf at ffffffffa04cfe99 [xfs]
  14. #11 [ffff8805f3a237f0] xfs_alloc_fix_freelist at ffffffffa04d28a1 [xfs]
  15. #12 [ffff8805f3a238d0] xfs_alloc_vextent at ffffffffa04d2e16 [xfs]

复制代码

可以看出,进程阻塞在信号量上,需要解析如下函数中xfs_buf_t *bp变量的值,以确认其中bp->b_sema信号量的状态。

  1. void
  2. xfs_buf_lock(
  3. xfs_buf_t        *bp)
  4. {
  5. trace_xfs_buf_lock(bp, _RET_IP_);
  6. if (atomic_read(&bp->b_io_remaining))
  7. blk_run_address_space(bp->b_target->bt_mapping);
  8. down(&bp->b_sema);
  9. XB_SET_OWNER(bp);
  10. trace_xfs_buf_lock_done(bp, _RET_IP_);
  11. }

复制代码

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

A、在上级函数中通过该变量与堆栈的关系解析
bp变量是在上级函数_xfs_buf_find定义局部变量,那么其通常会在该级栈中分配空间。但难点还在于如何确认该变量在堆栈中的位置,关键在于找到“寄存器和堆栈关联”的地方,还得分析关键代码流程:

  1. _xfs_buf_find(
  2. xfs_buftarg_t        *btp,    /* block device target        */
  3. xfs_off_t        ioff,    /* starting offset of range    */
  4. size_t            isize,    /* length of range        */
  5. xfs_buf_flags_t        flags,
  6. xfs_buf_t        *new_bp)
  7. {
  8. xfs_off_t        range_base;
  9. size_t            range_length;
  10. xfs_bufhash_t        *hash;
  11. xfs_buf_t        *bp, *n;
  12. range_base = (ioff << BBSHIFT);
  13. range_length = (isize << BBSHIFT);
  14. ...
  15. found:
  16. spin_unlock(&hash->bh_lock);
  17. /* Attempt to get the semaphore without sleeping,
  18. * if this does not work then we need to drop the
  19. * spinlock and do a hard attempt on the semaphore.
  20. */
  21. if (down_trylock(&bp->b_sema)) {
  22. if (!(flags & XBF_TRYLOCK)) {
  23. /* wait for buffer ownership */
  24. xfs_buf_lock(bp);
  25. XFS_STATS_INC(xb_get_locked_waited);
  26. ...

复制代码

可以看到,bp是作为xfs_buf_lock函数的入参传入的,那这里应该会通过寄存器或其它方式进行传参,则必然会对bp所在的堆栈位置进行操作,由此应能找到bp在堆栈中的位置。
反汇编相关代码:

  1. crash> dis -l _xfs_buf_find
  2. /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
  3. 0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
  4. 0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
  5. 0xffffffffa05234f4 <_xfs_buf_find+4>: push %r15
  6. 0xffffffffa05234f6 <_xfs_buf_find+6>: push %r14
  7. ...
  8. 0xffffffffa05235e6 <_xfs_buf_find+246>: mov %rcx,%rdi
  9. 0xffffffffa05235e9 <_xfs_buf_find+249>: mov %rcx,-0x58(%rbp) //可以看出rbp偏移0x58即为入参bp的值
  10. 0xffffffffa05235ed <_xfs_buf_find+253>: callq 0xffffffffa05233e0 <xfs_buf_lock>//此处调用xfs_buf_lock
  11. ...

复制代码

找到调用xfs_buf_lock函数的地方,在此之前准备入参,操作了堆栈-0x58(%rbp) ,可以看出rbp偏移0x58即为入参bp的值
再看看堆栈中-0x58(%rbp)中的内容具体是啥:

  1. crash> bt -f 9242
  2. PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
  3. #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
  4. ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
  5. ffff8805f3a23440: 0000000181164d0e ffff880c20518300
  6. ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
  7. ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
  8. ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
  9. ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
  10. ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
  11. ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
  12. ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
  13. ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
  14. ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
  15. ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
  16. ffff8805f3a234f0: ffffffff814f9a6d
  17. ...
  18. #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  19. ffff8805f3a23658: ffff8805f563e600 ffff8802ecd78480
  20. ffff8805f3a23668: 0001400500000000 ffff8805f563e690
  21. ffff8805f3a23678: ffff8805f3a236b8 0000000000000001
  22. ffff8805f3a23688: ffff8805f3a21af8 ffff8808fe07c0c0
  23. ffff8805f3a23698: 0000000000014005 000000003a382231
  24. ffff8805f3a236a8: 0000000000000001 ffff8805f563e0c0
  25. rsp--> ffff8805f3a236b8: ffff8805f3a236f8 ffffffffa05237db

复制代码

我们知道:
(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. crash> struct xfs_buf_t ffff8802ecd78480
  2. struct xfs_buf_t {
  3. b_rbnode = {
  4. rb_parent_color = 18446612143895321536,
  5. rb_right = 0x0,
  6. rb_left = 0x0
  7. },
  8. b_file_offset = 500099736064,
  9. b_buffer_length = 512,
  10. b_hold = {
  11. counter = 6
  12. },
  13. b_lru_ref = {
  14. counter = 3
  15. },
  16. b_flags = 3145844,
  17. b_sema = {
  18. lock = {
  19. raw_lock = {
  20. slock = 151718155
  21. }
  22. },
  23. count = 0,
  24. wait_list = {
  25. next = 0xffff88008c3f38c8,
  26. prev = 0xffff8805f3a235a8
  27. }
  28. },

复制代码

内容输出正常,应说明解析是正确的。
从该结构体内容可以看出,该xfs_lock被其它进程占用了,且等待队列中有进程正在等待该锁,进一步分析wait_list可以得到每个等待进行的相关信息,这里不再赘述具体方法。

B、在本级或下级函数中通过该变量与堆栈的关系解析
解析关键在于:需要找到引用该变量的关键点,比如这里的down(&bp->b_sema)函数,以bp变量所为入参,那么就必然会对该变量进行操作,比如通过寄存器传参到down函数中,在此可以寻找到蛛丝马迹。
首先,需要对xfs_buf_lock函数进行反汇编:

  1. crash> dis -l xfs_buf_lock
  2. /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
  3. 0xffffffffa05233e0 <xfs_buf_lock>: push %rbp
  4. 0xffffffffa05233e1 <xfs_buf_lock+1>: mov %rsp,%rbp
  5. 0xffffffffa05233e4 <xfs_buf_lock+4>: sub $0x20,%rsp
  6. 0xffffffffa05233e8 <xfs_buf_lock+8>: mov %rbx,-0x18(%rbp)
  7. 0xffffffffa05233ec <xfs_buf_lock+12>: mov %r12,-0x10(%rbp)
  8. 0xffffffffa05233f0 <xfs_buf_lock+16>: mov %r13,-0x8(%rbp)
  9. 0xffffffffa05233f4 <xfs_buf_lock+20>: nopl 0x0(%rax,%rax,1)
  10. ...
  11. /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
  12. 0xffffffffa052342a <xfs_buf_lock+74>: lea 0x38(%rbx),%rdi
  13. 0xffffffffa052342e <xfs_buf_lock+78>: callq 0xffffffff81097bd0
  14. /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
  15. 0xffffffffa0523433 <xfs_buf_lock+83>: mov 0x2976e(%rip),%r11d # 0xffffffffa054cba8 <__tracepoint_xfs_buf_lock_done+8>
  16. /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
  17. 0xffffffffa052343a <xfs_buf_lock+90>: mov 0x8(%rbp),%r12

复制代码

找到关键点:调用down()函数的地方,然后可以发现,在call down之前,就行了相关寄存器操作,可以推测是进行传参相关的准备。
可以发现有
lea 0x38(%rbx),%rdi
对比代码
down(&bp->b_sema);
可以看出,入参是bp结构体的一个成员,刚好跟0x38偏移对应,由此可推测此时的rbx寄存器为存放bp指针的寄存器。
接下来,需要寻找rbx寄存器跟堆栈的关系,需要找到从rbx向堆栈写、或从堆栈向rbx读的相关操作,而在当级函数的反汇编代码中显然没有,需要进入下级函数down()中,看看是否有相关操作,对down()进行反汇编:

  1. crash> dis -l down
  2. /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/kernel/semaphore.c: 54
  3. 0xffffffff81097bd0 : push %rbp
  4. 0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
  5. 0xffffffff81097bd4 <down+4>: push %rbx
  6. 0xffffffff81097bd5 <down+5>: sub $0x18,%rsp
  7. 0xffffffff81097bd9 <down+9>: nopl 0x0(%rax,%rax,1)
  8. 0xffffffff81097bde <down+14>: mov %rdi,%rbx
  9. ...

复制代码

可以看出,其中第5行对rbx寄存器进行压栈,bingo!,这正是我们要寻找的,由此说明bp指针的值,必然可以在down()这一级函数的栈中找到。
解析堆栈:

  1. crash> bt -f 9242
  2. PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
  3. #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
  4. ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
  5. ffff8805f3a23440: 0000000181164d0e ffff880c20518300
  6. ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
  7. ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
  8. ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
  9. ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
  10. ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
  11. ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
  12. ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
  13. ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
  14. ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
  15. ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
  16. ffff8805f3a234f0: ffffffff814f9a6d
  17. ...
  18. #3 [ffff8805f3a235f0] down at ffffffff81097c11
  19. ffff8805f3a235f8: ffff8805f3a23618 0000000000000292
  20. ffff8805f3a23608: ffff8802ecd78480 ffff8802ecd78480
  21. ffff8805f3a23618: ffff8805f3a23648 ffffffffa0523433
  22. #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
  23. ffff8805f3a23628: 0000000000016ec0 0000000000016ec0
  24. ffff8805f3a23638: ffff8808fe07c0c0 ffff8802ecd78490
  25. ffff8805f3a23648: ffff8805f3a236b8 ffffffffa05235f2

复制代码

down()的堆栈在第#3级,再看看down()的反汇编代码的头三句:

  1. 0xffffffff81097bd0 : push %rbp
  2. 0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
  3. 0xffffffff81097bd4 <down+4>: push %rbx

复制代码

显然,接下来压栈的是rbp,即ffff8805f3a23648是rbp,即上一级堆栈的栈帧指针。
第3句,即对rbx压栈,说明rbx(即我们要找的bp指针的值)就位于down()函数堆栈中的第3个位置(第1为上级函数返回地址、第2为rbp),即:
ffff8802ecd78480
所以,我们找到了。。。。

2)位于寄存器中的局部变量
由于Vmcore只是一个内存快照的静态数据,所以其中保存的进程上下文中,只保存了最后一级函数执行时的寄存器内容,所以,如果相关局部变量位于最后一级函数中,且用寄存器保存,那么此时可以直接通过相关进程的bt上下文得到:

  1. crash> bt 9242
  2. PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
  3. #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
  4. #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
  5. #2 [ffff8805f3a235a0] __down at ffffffff814fa992
  6. #3 [ffff8805f3a235f0] down at ffffffff81097c11
  7. #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
  8. #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
  9. #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
  10. ..
  11. #24 [ffff8805f3a23f80] system_call_fastpath at ffffffff8100b0f2
  12. RIP: 0000003885e0ed2d RSP: 00007f43871e36e0 RFLAGS: 00003297
  13. RAX: 0000000000000002 RBX: ffffffff8100b0f2 RCX: 0000000000000000
  14. RDX: 0000000000000241 RSI: 0000000000000241 RDI: 00007f43871e3710
  15. RBP: 00007f43871e365c R8: 00007f43871df440 R9: 0000000000100000
  16. R10: 0000000000000000 R11: 0000000000003293 R12: ffffffff8117a830
  17. R13: ffff8805f3a23f78 R14: 00007f43871e3710 R15: 00007f43871e391c
  18. ORIG_RAX: 0000000000000002 CS: 0033 SS: 002b

复制代码

但是,如果需要解析的局部变量位于中间流程,且使用寄存器保存,且不能在最后一级函数执行时的进程上下文中体现,那么此类局部变量就无法解析了。

crash处理core文件的更多相关文章

  1. Core文件作用、设置及用法

    http://blog.csdn.net/lanmolei814/article/details/45201693 ====================================== 1.C ...

  2. linux包之gdb之gdb命令与core文件产生

    gdb-7.2-64.el6_5.2.x86_64/usr/bin/gcore/usr/bin/gdb/usr/bin/gdb-add-index/usr/bin/gdbtui/usr/bin/gst ...

  3. gdb强制生成core文件

    如何为自己的进程产生core 文件,又不想退出这个进程? 系统只在程序崩溃退出时自动产生core file. 有的人像自己处理异常信号,然后自己产生一个core file,然后继续运行.那该怎么办呢? ...

  4. [转载]linux下core文件设置与查看

    转自:https://blog.csdn.net/dingqinghui/article/details/77855330?locationNum=9&fps=1 linux下core文件设置 ...

  5. mysql core文件的正确打开姿势

         最近两天自己负责的一个实例频繁出现crash的情况,分析了日志,大致明白了crash的原因,但是没有定位到具体的SQL,也没有找到很好的规避的办法,因此想在mysql出现crash的时候自动 ...

  6. GDB Core,gdb 调试大全,core文件调试

    编译: gcc -g -o hello hello.c gdb 调试: 基本 gdb 命令. 命 令 描 述 小结:常用的gdb命令 backtrace 显示程序中的当前位置和表示如何到达当前位置的栈 ...

  7. Go -- 通过GOTRACEBACK生成程序崩溃后core文件的方法(gcore gdb)

    写一个错误的c程序   package dlsym import "testing" func Test_intercept(t *testing.T) { Intercept(& ...

  8. linux core文件的打开和分析

    1. core文件生成打开方式: ulimit -c unlimited echo "1" > /proc/sys/kernel/core_uses_pid 看下服务器上是否 ...

  9. 如何查找并简单分析core文件

    当系统发生coredump时,通常需要通过分析core文件来定位问题所在,但实际工作中,有时却发现core 文件找不到,或者core文件被删除了. 一.core文件没有生成 KINGBASE core ...

随机推荐

  1. signed distance field 算法

    将二值图转化成signed distance field后,可以在双线性插值下实现平滑放大. 定义: 到前景的distance field:各点到最近前景点的距离. 到背景的distance fiel ...

  2. atitit.atiOrm.js v2 q61 版本新特性.docx

    atitit.atiOrm.js v2 q61 版本新特性.docx 1. V1新特性如下1 1.1. V2规划,直接生成sql在js端1 2. Orm设计框架图1 2.1. atiOrm.js的原理 ...

  3. Atitit.  单列索引与多列索引 多个条件的查询原理与设计实现

    Atitit.  单列索引与多列索引 多个条件的查询原理与设计实现 1. MySQL只能使用一个索引1 1.1. 最左前缀1 1.2. 从另一方面理解,它相当于我们创建了(firstname,last ...

  4. 85. Insert Node in a Binary Search Tree【easy】

    Given a binary search tree and a new tree node, insert the node into the tree. You should keep the t ...

  5. Bootstrap学习笔记(1)栅格系统

    栅格系统: .row 1行12列 .col-md-3 占3列,一行就是4个 <!DOCTYPE html> <html lang="en"> <hea ...

  6. ant.xml

    <?xml version="1.0"?> <project name="dxcc" default="buildplugins&q ...

  7. 搭建hadoop集群,

    这个教程是2.4.1的 ,但是亲测对于2.6.5,是可以用的,对2.5.4应该也是支持的 1.准备Linux环境 1.0先将虚拟机的网络模式选为NAT 1.1修改主机名 vi /etc/sysconf ...

  8. 迅搜sdk试用

    1. sdk支持PHP 2. 针对mysql的某个库的某个表??进行索引,简单的说就是一个project,需要对应一个配置文件: 3. 分索引服务与搜索服务两个,另带中文分词功能:索引数据会有演示,但 ...

  9. 使用WinSCP这个软件使linux和win7互传文件

    使用这个软件之前首先win7要可以ping通linux系统,且linux要开启,关机可不能通啊!!!!!!!!! 双击这个快捷方式 主机名写ip地址 我们可以将虚拟机上的文件下载下来进行使用 也可以将 ...

  10. Master Sudoku:Get The Skill

    自己做的小游戏 google play store: https://play.google.com/store/apps/details?id=com.ffipp.sodoku app store: ...