美文网首页
linux 内核core文件调试工具----crash

linux 内核core文件调试工具----crash

作者: IvanGuan | 来源:发表于2019-10-02 16:00 被阅读0次

简介

 当我的用户态由于段错误或者其他错误发生了而退出进程时一般会在/var/crash/目录下生成相应的core文件(前提是配置了core文件转储机制)。对于一个core文件我们最为关心的就是在哪一行出错?出的什么错?哪个函数或者变量有问题?基本上只要回答了前面的是那个问题,该core的使命就已经完成了!一般用"gdb -c /var/crash/xxx.core”就能查看了,但是如果我们的内核程序奔溃了gdb就搞不定了,这时我们需要另一个工具crash

crash is a self-contained tool that can be used to investigate either live systems, kernel core dumps created from dump creation facilities such as kdump, kvmdump, xendump, the
netdump and diskdump.

环境准备

我的环境是Centos7, 内核版本:kernel-3.10.0-514.26.2.el7.x86_64
1.根据实验环境的内核版本安装对应的kernel debug info,装完的环境应该是

[root@xt2 ~]# rpm -qa |grep kernel
kernel-headers-3.10.0-514.16.1.el7.x86_64
kernel-debuginfo-common-x86_64-3.10.0-514.26.2.el7.x86_64
kernel-3.10.0-514.26.2.el7.x86_64
kernel-debuginfo-3.10.0-514.26.2.el7.x86_64
kernel-devel-3.10.0-514.26.2.el7.x86_6
  1. 直接yum install crash 安装crash工具

使用方法

1.从vmcore-dmesg.txt里查看大概出错类型
[260651.860820] BUG: unable to handle kernel NULL pointer dereference at           (null)
[260651.861697] IP: [<          (null)>]           (null)
[260651.862250] PGD 18f073067 PUD 18ca38067 PMD 0
[260651.862723] Oops: 0010 [#1] SMP
[260651.863060] Modules linked in: fuse(OE) ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 xt_addrtype iptable_filter xt_conntrack nf_nat nf_conntrack br_netfilter bridge stp llc dm_thin_pool dm_persistent_data dm_bio_prison dm_bufio loop dm_mod intel_powerclamp iosf_mbi crc32_pclmul ghash_clmulni_intel aesni_intel snd_intel8x0 snd_ac97_codec lrw gf128mul glue_helper ac97_bus ablk_helper snd_seq ppdev cryptd i2c_piix4 snd_seq_device snd_pcm i2c_core snd_timer video snd soundcore pcspkr parport_pc parport sg ip_tables xfs libcrc32c sd_mod sr_mod crc_t10dif cdrom crct10dif_generic ata_generic pata_acpi ahci libahci ata_piix crct10dif_pclmul crct10dif_common crc32c_intel serio_raw libata e1000 fjes [last unloaded: fuse]
[260651.868329] CPU: 1 PID: 10361 Comm: write_file_step Tainted: G           OE  ------------   3.10.0-514.26.2.el7.x86_64 #1
[260651.869389] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[260651.870121] task: ffff880087abde20 ti: ffff88018bea4000 task.ti: ffff88018bea4000
[260651.872062] RIP: 0010:[<0000000000000000>]  [<          (null)>]           (null)
[260651.875082] RSP: 0018:ffff88018bea7bf8  EFLAGS: 00010246
[260651.876682] RAX: ffffffffa03de780 RBX: 0000000000000000 RCX: 000000000000000a
[260651.880485] RDX: 0000000000000000 RSI: ffff880097fd4150 RDI: ffff88018f1e0900
[260651.883539] RBP: ffff88018bea7cb8 R08: 0000000000000000 R09: ffff88018bea7c58
[260651.886301] R10: 0000000000000000 R11: 000000000000000a R12: 000000000000000a
[260651.889198] R13: 0000000000001000 R14: ffff880097fd4150 R15: ffff88018bea7e20
[260651.891572] FS:  00007fef1a655740(0000) GS:ffff880198440000(0000) knlGS:0000000000000000
[260651.894542] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[260651.896141] CR2: 0000000000000000 CR3: 0000000188a83000 CR4: 00000000000406e0
[260651.899396] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[260651.902082] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[260651.904294] Stack:
[260651.905478]  ffffffff81181bbe ffff88018bea7c60 ffff88018bea7e68 0000000000000000
[260651.908038]  0000000000001000 ffff880087abde20 ffff88018bea7fd8 0000000097fd4000
[260651.910554]  0000000000000000 0000000000000000 ffffffffa03de780 ffff88018f1e0900
[260651.913064] Call Trace:
[260651.914226]  [<ffffffff81181bbe>] ? generic_file_buffered_write+0x11e/0x2a0
[260651.915669]  [<ffffffff811831a2>] __generic_file_aio_write+0x1e2/0x400
[260651.917126]  [<ffffffff81183419>] generic_file_aio_write+0x59/0xa0
[260651.918235]  [<ffffffffa03dad85>] fuse_file_aio_write+0x175/0x390 [fuse]
[260651.919780]  [<ffffffff81180c2b>] ? unlock_page+0x2b/0x30
[260651.920787]  [<ffffffff811acd74>] ? do_read_fault.isra.42+0xe4/0x130
[260651.921859]  [<ffffffff811fe18d>] do_sync_write+0x8d/0xd0
[260651.922860]  [<ffffffff811fe9fd>] vfs_write+0xbd/0x1e0
[260651.923913]  [<ffffffff811ff6d2>] SyS_pwrite64+0x92/0xc0
[260651.925008]  [<ffffffff81697809>] system_call_fastpath+0x16/0x1b
[260651.926307] Code:  Bad RIP value.
[260651.927594] RIP  [<          (null)>]           (null)
[260651.928938]  RSP <ffff88018bea7bf8>
[260651.930136] CR2: 0000000000000000

从上面的日志说明我们访问了一个空指针

2.调试crash
备注:为了每次执行简单,我定义了crash的别名:`alias crash='crash /usr/lib/debug/lib/modules/3.10.0-514.26.2.el7.x86_64/vmlinux’`
[root@xt1 127.0.0.1-2019.09.30-11:15:31]# crash vmcore
crash 7.2.3-10.el7
Copyright (C) 2002-2017  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011  NEC Corporation
Copyright (C) 1999, 2002, 2007  Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002  Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions.  Enter "help copying" to see the conditions.
This program has absolutely no warranty.  Enter "help warranty" for details.

GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <[http://gnu.org/licenses/gpl.html](http://gnu.org/licenses/gpl.html)>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying”
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu”...

      KERNEL: /usr/lib/debug/lib/modules/3.10.0-514.26.2.el7.x86_64/vmlinux
    DUMPFILE: vmcore  [PARTIAL DUMP]
        CPUS: 6
        DATE: Mon Sep 30 11:15:23 2019
      UPTIME: 3 days, 00:24:11
LOAD AVERAGE: 0.33, 0.93, 0.96
       TASKS: 763
    NODENAME: xt1
     RELEASE: 3.10.0-514.26.2.el7.x86_64
     VERSION: #1 SMP Tue Jul 4 15:04:05 UTC 2017
     MACHINE: x86_64  (3407 Mhz)
      MEMORY: 5.9 GB
       PANIC: "BUG: unable to handle kernel NULL pointer dereference at           (null)”
         PID: 10361
     COMMAND: “write_file_step”
        TASK: ffff880087abde20  [THREAD_INFO: ffff88018bea4000]
         CPU: 1
       STATE: TASK_RUNNING (PANIC)

再次验证访问了null指针,但是谁访问的还得往下看

3.查看出错栈的信息 bt
crash> bt
PID: 10361  TASK: ffff880087abde20  CPU: 1   COMMAND: “write_file_step”
 #0 [ffff88018bea7880] machine_kexec at ffffffff81059beb
 #1 [ffff88018bea78e0] __crash_kexec at ffffffff81105822
 #2 [ffff88018bea79b0] crash_kexec at ffffffff81105910
 #3 [ffff88018bea79c8] oops_end at ffffffff81690008
 #4 [ffff88018bea79f0] no_context at ffffffff8167fc96
 #5 [ffff88018bea7a40] __bad_area_nosemaphore at ffffffff8167fd2c
 #6 [ffff88018bea7a88] bad_area at ffffffff81680050
 #7 [ffff88018bea7ab0] __do_page_fault at ffffffff81692f4f
 #8 [ffff88018bea7b10] do_page_fault at ffffffff81692ff5
 #9 [ffff88018bea7b40] page_fault at ffffffff8168f208
    [exception RIP: unknown or invalid address]
    RIP: 0000000000000000  RSP: ffff88018bea7bf8  RFLAGS: 00010246
    RAX: ffffffffa03de780  RBX: 0000000000000000  RCX: 000000000000000a
    RDX: 0000000000000000  RSI: ffff880097fd4150  RDI: ffff88018f1e0900
    RBP: ffff88018bea7cb8   R8: 0000000000000000   R9: ffff88018bea7c58
    R10: 0000000000000000  R11: 000000000000000a  R12: 000000000000000a
    R13: 0000000000001000  R14: ffff880097fd4150  R15: ffff88018bea7e20
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
#10 [ffff88018bea7bf8] generic_file_buffered_write at ffffffff81181bbe
#11 [ffff88018bea7cc0] __generic_file_aio_write at ffffffff811831a2
#12 [ffff88018bea7d40] generic_file_aio_write at ffffffff81183419
#13 [ffff88018bea7d80] fuse_file_aio_write at ffffffffa03dad85 [fuse]
#14 [ffff88018bea7e18] do_sync_write at ffffffff811fe18d
#15 [ffff88018bea7ef0] vfs_write at ffffffff811fe9fd
#16 [ffff88018bea7f30] sys_pwrite64 at ffffffff811ff6d2
#17 [ffff88018bea7f80] system_call_fastpath at ffffffff81697809
    RIP: 00007fef1a169003  RSP: 00007ffef15260c8  RFLAGS: 00010246
    RAX: 0000000000000012  RBX: ffffffff81697809  RCX: 0000000000000000
    RDX: 000000000000000a  RSI: 00000000006020c0  RDI: 0000000000000003
    RBP: 00007ffef1526100   R8: 00007fef1a0c9988   R9: 000000000000000e
    R10: 0000000000000000  R11: 0000000000000246  R12: 0000000000000000
    R13: 00007ffef1526220  R14: 0000000000400640  R15: 0000000000000000
    ORIG_RAX: 0000000000000012  CS: 0033  SS: 002b

我们从栈中看到出错的地址在ffffffff81181bbe,而且exception RIP: unknown or invalid address说明指令地址有问题,从下面RIP: 0000000000000000 又看出该指针为空,说明没有指令地址为空,执行指令的时候取不到任何有效指令!

RIP is the instruction pointer. It points to a memory address, indicating the progress of program execution in memory。
在core文件里面RIP里面存的指令就是造成内核奔溃的指令!
4.我们根据出错函数报告的地址反汇编出出错位置 dis
crash> dis -l ffffffff81181bbe
/usr/src/debug/kernel-3.10.0-514.26.2.el7/linux-3.10.0-514.26.2.el7.x86_64/mm/filemap.c: 2984
0xffffffff81181bbe <generic_file_buffered_write+286>:   movslq %eax,%r15
generic_perform_write

2984行是一个函数的调用,从代码中观察函数出错的函数名脚generaic_perform_write,而我们可以看看a_ops->write_begin(由于是指令出错,大概率是它,别的话顶多是参数出错), file, mapping,这三个指针谁是错的,
我们就来用第4条看看这个函数的参数

4.查看函数的参数(实时的参数和出错当场的参数)

我们先来看看x86 机构cpu寄存器装载参数的情况:

According to the ABI, the first 6 integer or pointer arguments to a function are passed in registers. The first is placed in rdi, the second in rsi, the third in rdx, and then rcx, r8 and r9. Only the 7th argument and onwards are passed on the stack.
The stack frame

With the above in mind, let's see how the stack frame for this C function looks:
long myfunc(long a, long b, long c, long d,
            long e, long f, long g, long h)
{
    long xx = a * b * c * d * e * f * g * h;
    long yy = a + b + c + d + e + f + g + h;
    long zz = utilfunc(xx, yy, xx % yy);
    return zz + 20;
}

This is the stack frame:


出错代码的位置:

status = a_ops->write_begin(file, mapping, pos, bytes, flags,
                        &page, &fsdata);

第一个参数: file 地址:ffff88018f1e0900
第二个参数:mapping 地址:ffff880097fd4150
后面的几个参数都是普通变量,所以不需要打印他们的地址了。

打印file的值:

crash> struct file ffff88018f1e0900
struct file {
  f_u = {
    fu_list = {
      next = 0xffff88018f1e0900,
      prev = 0xffff88018f1e0900
    },
    fu_rcuhead = {
      next = 0xffff88018f1e0900,
      func = 0xffff88018f1e0900
    }
  },
  f_path = {
    mnt = 0xffff880182a6c520,
    dentry = 0xffff88019148e0c0
  },
  f_inode = 0xffff880097fd4000,
  f_op = 0xffffffffa03de840,
  ... 
  f_mapping = 0xffff880097fd4150,
  ...
}

打印mapping的值与上面类似,只是加地址的时候加上对应的寄存器地址就可以了,打印结果正常。

crash> struct address_space ffff880097fd4150
struct address_space {
  host = 0xffff880097fd4000,
  ...
  writeback_index = 0,
  a_ops = 0xffffffffa03de780,
  flags = 131290,
  backing_dev_info = 0xffff880184703148,
  ...
}

从之前的分析得知我们的指令是空,所以很有可能就是这样的逻辑:write_begin把所有的参数都压入栈里面了,在最后调用函数write_begin的时候发现自己是个空指针,没有指令可以执行!

所以我们就打印a_ops来看下它的成员函数指针write_begin是否为空:
从上面得知a_ops的地址为0xffffffffa03de780

crash> struct address_space_operations 0xffffffffa03de780
struct address_space_operations {
  writepage = 0xffffffffa03db3d0,
  readpage = 0xffffffffa03d99c0,
  writepages = 0x0,
  set_page_dirty = 0xffffffff8118cb20 <__set_page_dirty_nobuffers>,
  readpages = 0xffffffffa03d78c0,
  write_begin = 0x0,
  write_end = 0x0,
  bmap = 0xffffffffa03d68d0,
  invalidatepage = 0x0,
  releasepage = 0x0,
  freepage = 0x0,
  direct_IO = 0xffffffffa03da260,
  rh_reserved_get_xip_mem = 0x0,
  migratepage = 0x0,
  launder_page = 0xffffffffa03db370,
  is_partially_uptodate = 0x0,
  is_dirty_writeback = 0x0,
  error_remove_page = 0x0,
  swap_activate = 0x0,
  swap_deactivate = 0x0,
  invalidatepage_range = 0x0
}

小结

仓了个天,经过一番努力,终于找到真凶!write_begin为空,后来通过对比代码发现新版的kernel 源码给这个指针赋值了,但是老版的没有赋值,所以在老版的逻辑里冒然调用了这个接口时出现访问空指针错误!在此把所用到的东西和分析过程贴出来供大家参考,有好的建议和不对之处欢迎留言!

参考

1.https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
2.http://www.voidcn.com/article/p-eyktsjtm-qz.html

相关文章

  • linux 内核core文件调试工具----crash

    简介  当我的用户态由于段错误或者其他错误发生了而退出进程时一般会在/var/crash/目录下生成相应的core...

  • linux 内核参数core_pattern测试

    linux 内核参数core_pattern测试 参数介绍 简单来说,该参数就是可以设定core dump文件的文...

  • linux:core文件的产生和调试

    在Linux下程序不寻常退出时,内核会在当前工作目录下生成一个core文件(是一个内存映像,同时加上调试信息)。使...

  • coredump备忘

    背景 程序已经挂死等情况,内核会生成一个core文件(是内存映像以及调试信息)。可以通过使用gdb来查看core文...

  • linux:abrt-cli list

    在linux调试程序,最痛苦的就是程序异常宕掉,但是找不到core文件,很难定位问题。但是有了core文件就容易定...

  • Linux命令和shell编程基础

    Linux相关 ​ Linux是基于Linux内核的操作系统,除了内核外,操作系统还需要一些工具来执行例如文件...

  • 2018-09-18 Linux内核调试

    【Gooooood转】Linux内核调试方法总结

  • Linux常用内核网络参数及修改方法

    Linux常用内核网络参数及修改方法 Linux常用内核参数 参数描述net.core.rmem_default默...

  • Valgrind 和gdb内存调式工具

    Valgrind Valgrind是一套linux下,开放源代码的仿真调试工具的集合。它由内核以及基于内核的其他调...

  • systemtap工具

    一、简介 systemtap是Linux下一个非常有用的调试(跟踪/探测)工具,常用于Linux内核或者应用程序的...

网友评论

      本文标题:linux 内核core文件调试工具----crash

      本文链接:https://www.haomeiwen.com/subject/muqrpctx.html