目录

  • 1.全局变量解析
  • 2.局部变量的解析
    • 2.1 位于栈中的局部变量
      • 2.1.1 在上级函数中通过该变量与堆栈的关系解析
      • 2.2.2 在本级或下级函数中通过该变量与堆栈的关系解析
    • 2.2 位于寄存器中的局部变量

本文转自 https://www.cnblogs.com/hehheai/p/6513699.html, 主要讲述了如何根据crash dump来查找某个特定函数局部变量值的方法,总结起来包括如下几种方法:

  1. 位于栈中的局部变量的解析
    (1)在上级函数中通过该变量与堆栈的关系解析
    如果以函数参数传递给下级被调用函数,在上级调用函数中必然会有寄存器操作来传参,找到对寄存器的操作,结合上级调用函数的栈帧来推断最终的局部变量值;
    (2) 在本级或下级函数中通过该变量与堆栈的关系解析
    在本级函数或下级函数中找到传参寄存器的操作,再结合本级函数或下级函数的栈帧来推断最终的局部变量值
  2. 位于寄存器中的局部变量的解析
    如果相关局部变量位于最后一级函数中,且用寄存器保存,那么此时可以直接通过相关进程的bt上下文

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

1.全局变量解析

非常简单,可通过在crash中直接p <变量名>打印,如:

crash> p jiffies
jiffies = $3 = 5540265294

2.局部变量的解析

比较复杂。
Vmcore搜集的仅为故障当时内存使用情况的一个快照,是静态信息,无法进行动态调试(虽然听说可以,但没见过~~),对于某个进程而言,在Vmcore中能发掘的进程上下文信息,通常只有堆栈和寄存器的值。而我们了解,通常局部变量在栈中分配,但也可能直接使用寄存器保存,所以可以(也只能)通过“寻找局部变量跟堆栈或寄存器的关系”来解析局部变量的值。所以这里分两种情况:

2.1 位于栈中的局部变量

这种情况比较常见,此时,局部变量必然位于某一级函数的堆栈中,该局部变量可能通过指针一级级向底层函数传递,所以可能位于多个函数的堆栈中,可以从不同的函数堆栈中解析。但解析会比较困难,难点在于难以确认相关变量在堆栈中的具体位置,解析方法很灵活,需要结合相关源代码,仔细分析流程,找到关键的点,更多的取决于分析者的经验和代码理解能力。
如下以实例说明解析过程:
vmcore中某阻塞的进程有如下的堆栈:

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]

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

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_);}

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

2.1.1 在上级函数中通过该变量与堆栈的关系解析

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

_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);...

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

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: 4310xffffffffa05234f0 <_xfs_buf_find>: push %rbp0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp0xffffffffa05234f4 <_xfs_buf_find+4>: push %r150xffffffffa05234f6 <_xfs_buf_find+6>: push %r14...0xffffffffa05235e6 <_xfs_buf_find+246>: mov %rcx,%rdi0xffffffffa05235e9 <_xfs_buf_find+249>: mov %rcx,-0x58(%rbp) //可以看出rbp偏移0x58即为入参bp的值0xffffffffa05235ed <_xfs_buf_find+253>: callq 0xffffffffa05233e0 <xfs_buf_lock>//此处调用xfs_buf_lock...

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

crash> bt -f 9242PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"#0 [ffff8805f3a23428] schedule at ffffffff814f8b42ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8ffff8805f3a23440: 0000000181164d0e ffff880c20518300ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8ffff8805f3a234a0: 0000000000000246 ffff880c1d102400ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffffffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffffffff8805f3a234e0: 0000000000000200 ffff8805f3a23598ffff8805f3a234f0: ffffffff814f9a6d...#5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]ffff8805f3a23658: ffff8805f563e600 ffff8802ecd78480ffff8805f3a23668: 0001400500000000 ffff8805f563e690ffff8805f3a23678: ffff8805f3a236b8 0000000000000001ffff8805f3a23688: ffff8805f3a21af8 ffff8808fe07c0c0ffff8805f3a23698: 0000000000014005 000000003a382231ffff8805f3a236a8: 0000000000000001 ffff8805f563e0c0rsp--> 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结构体:

crash> struct xfs_buf_t ffff8802ecd78480struct 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}},

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

2.2.2 在本级或下级函数中通过该变量与堆栈的关系解析

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

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: 9330xffffffffa05233e0 <xfs_buf_lock>: push %rbp0xffffffffa05233e1 <xfs_buf_lock+1>: mov %rsp,%rbp0xffffffffa05233e4 <xfs_buf_lock+4>: sub $0x20,%rsp0xffffffffa05233e8 <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: 9400xffffffffa052342a <xfs_buf_lock+74>: lea 0x38(%rbx),%rdi0xffffffffa052342e <xfs_buf_lock+78>: callq 0xffffffff81097bd0 /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: 3250xffffffffa0523433 <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: 9430xffffffffa052343a <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()进行反汇编:

crash> dis -l down/usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/kernel/semaphore.c: 540xffffffff81097bd0 : push %rbp0xffffffff81097bd1 <down+1>: mov %rsp,%rbp0xffffffff81097bd4 <down+4>: push %rbx0xffffffff81097bd5 <down+5>: sub $0x18,%rsp0xffffffff81097bd9 <down+9>: nopl 0x0(%rax,%rax,1)0xffffffff81097bde <down+14>: mov %rdi,%rbx...

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

crash> bt -f 9242PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"#0 [ffff8805f3a23428] schedule at ffffffff814f8b42ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8ffff8805f3a23440: 0000000181164d0e ffff880c20518300ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8ffff8805f3a234a0: 0000000000000246 ffff880c1d102400ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffffffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffffffff8805f3a234e0: 0000000000000200 ffff8805f3a23598ffff8805f3a234f0: ffffffff814f9a6d...#3 [ffff8805f3a235f0] down at ffffffff81097c11ffff8805f3a235f8: ffff8805f3a23618 0000000000000292ffff8805f3a23608: ffff8802ecd78480 ffff8802ecd78480ffff8805f3a23618: ffff8805f3a23648 ffffffffa0523433#4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]ffff8805f3a23628: 0000000000016ec0 0000000000016ec0ffff8805f3a23638: ffff8808fe07c0c0 ffff8802ecd78490ffff8805f3a23648: ffff8805f3a236b8 ffffffffa05235f2

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

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

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

2.2 位于寄存器中的局部变量

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

crash> bt 9242PID: 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 ffffffff8100b0f2RIP: 0000003885e0ed2d RSP: 00007f43871e36e0 RFLAGS: 00003297RAX: 0000000000000002 RBX: ffffffff8100b0f2 RCX: 0000000000000000RDX: 0000000000000241 RSI: 0000000000000241 RDI: 00007f43871e3710RBP: 00007f43871e365c R8: 00007f43871df440 R9: 0000000000100000R10: 0000000000000000 R11: 0000000000003293 R12: ffffffff8117a830R13: ffff8805f3a23f78 R14: 00007f43871e3710 R15: 00007f43871e391cORIG_RAX: 0000000000000002 CS: 0033 SS: 002b

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

crash处理core文件相关推荐

  1. core文件怎么分析_c++ crash 分析工具:breakpad

    做为一位c++开发人员,如果你没有遇到过线上程序崩溃,说明你写的代码少或者说你的测试同学很给力,或者是你的服务太简单了,这个题外话哈.俗话说,"夜路走多了总会遇到鬼(bug)". ...

  2. 2、崩溃文件(Crash Dump Core FIles)

    默认情况下所有的操作系统都会在操作系统状态发生变化,或者说发生了异常时,会生成系统级别的崩溃日志,对应的JVM也创造了类似的机制.一般情况下,JVM会创造两种类型的崩溃文件,即文本形式的.二进制形式的 ...

  3. Linux下程序崩溃dump时的 core文件的使用方法

    Linux下程序崩溃dump时的 core文件的使用方法 1.在启动程序前执行 ulimit -c unlimited unlimited 表示生成文件的大小限制,也可以修改为自定义的大小,例如: u ...

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

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

  5. c++ dump某个变量_linux内核调试之 crash分析dump文件

    Linux 下也有众多的内存转储分析工具,lcrash.Alicia.Crash.Crash 是由 Dave Anderson 开发和维护的一个内存转储分析工具,目前它的最新版本是 5.0.0. 在没 ...

  6. ubuntu core 文件产生

    关于内核转储的设置方法 1. 内核转储作用 (1) 内核转储的最大好处是能够保存问题发生时的状态. (2) 只要有可执行文件和内核转储,就可以知道进程当时的状态. (3) 只要获取内核转储,那么即使没 ...

  7. linux打开core文件,[转载]linux下core文件设置与查看

    程序异常推出时,内核会生成一个core文件(是内存映像以及调试信息).可以通过使用gdb来查看core文件,指示出导致程序出错的代码所在的文件和行数. 1.查看系统中core文件生成的开关是否打开 1 ...

  8. ulimit -c unlimited linux系统对core文件限制解除

    ulimit -c unlimited ulimint -a 用来显示当前的各种用户进程限制 Linux对于每个用户,系统限制其最大进程数,为提高性能,可以根据设备资源情况, 设置个Linux用户的最 ...

  9. ulimit -c unlimited生成core文件

    ulimit -c unlimited ulimint -a 用来显示当前的各种用户进程限制 Linux对于每个用户,系统限制其最大进程数,为提高性能,可以根据设备资源情况, 设置个Linux用户的最 ...

最新文章

  1. 计算机二级关于数据结构的题目,计算机二级MS OFFICE 练习题(一五三)
  2. mysql 批量_mysql LOAD语句批量录入数据
  3. TypeError at / 'AnonymousUser' object is not iterable
  4. 基于JAVA+Servlet+JSP+MYSQL的运动会管理系统
  5. 搭建1078部标流媒体服务器
  6. Android 更改头像(图片)并上传服务器功能Demo详解
  7. magisk安装失败_crDroid OS 安装刷入教程
  8. C语言:L1-009 N个数求和 (20 分)
  9. 做眼镜识别的人脸检测
  10. 特斯拉被曝低级漏洞:用树莓派DIY车钥匙,开锁仅需90秒
  11. OpenCore 版本升级后清除NVRAM
  12. TradingView--Charting Library是什么
  13. 清华大学胡事民是计算机视觉,ResNet也能用在3D模型上了,清华“计图”团队新研究已开源...
  14. nohup 执行mysql命令_Linux nohup命令:后台命令脱离终端运行
  15. Android解决fragment来回切换重新实例化问题
  16. [POI2008]CLO-Toll
  17. Please add following line to .bashrc: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/username/.muj
  18. 存放素数数组JAVA_java – 返回素数数组
  19. 求 精通数据科学 从线性回归到深度学习 PDF
  20. 基于JSP+Servlet+MySQL+Bootstrap的防疫知识在线问答系统

热门文章

  1. 【聆思CSK6 视觉AI开发套件试用】体验头肩检测和手势识别最全教程
  2. Apache Kafka教程A系列:简单生产者示例
  3. 蓝牙室内定位方案-灵思科
  4. HoloLens2的vuforia模型识别
  5. 10_Rapidly Exploring Random Trees_宾夕法尼亚大学机器人运动规划专项课程【学习笔记】
  6. 常用的博客社区(续)
  7. JADE学习笔记2 :Agent的创建和运行
  8. 2023年清华大学电子与通信工程考研上岸前辈备考经验
  9. 高中数学必修一二次函数与幂函数试题及答案
  10. 2022氯化工艺特种作业证考试题库及答案