前言

最近碰到了一个程序崩溃的问题,根据我的统计,这个问题出现的概率大概是400小时出现一次,出问题的概率比较小,而且由于未知原因,程序崩溃时的core dump文件没有生成。

后面我搭了一个测试环境反复去跑数据,连续测试了12个小时也并没有复现出来问题。这也是正常的,毕竟400小时/次的概率也是非常小的。目前可以利用的信息有 /var/log/kern.log文件以及不含调试信息的可执行文件。

记录一下解决问题的方法。

kern.log的崩溃信息

我在系统 /var/log/kern.log文件中找到了崩溃信息,如下:

Nov 24 06:20:42 PC kernel: [ 2906.214694] A[9536]: segfault at 28 ip 00007f55572573a8 sp 00007f55367f9ec0 error 4 in B.so[7f5557249000+1d000]
Nov 24 06:20:42 PC kernel: [ 2906.214714] Code: c3 49 f7 f1 48 8b 47 18 4c 8b 14 d0 48 89 d5 4d 85 d2 74 4e 4d 8b 02 49 8b 48 08 eb 24 0f 1f 44 00 00 49 8b 00 48 85 c0 74 38 <48> 8b 48 08 4d 89 c2 31 d2 49 89 c0 48 89 c8 49 f7 f1 48 39 d5 75

总共就两行,发生了段错误,比较关键的是这一行信息 A[9536]: segfault at 28 ip 00007f55572573a8 sp 00007f55367f9ec0 error 4 in B.so[7f5557249000+1d000]

具体来说,

  1. A[9536],代表名为A、进程号(PID)为9536的程序
  2. segfault at 28,表示程序在试图访问28这个地址的内存时产生了段错误
  3. ip 00007f55572573a8,表示当时程序在执行地址为00007f55572573a8的程序
  4. sp 00007f55367f9ec0,表示当时程序的栈的地址
  5. error 4,是错误码,4表示用户态的错误
  6. in B.so[7f5557249000+1d000],表示是在B.so文件产生的错误,这个.so映射到内存(VMA)中的起始地址是7f5557249000,其大小为1d000

计算 .so文件的崩溃地址

我们知道 ip 00007f55572573a8以及 B.so[7f5557249000+1d000],我们就可以计算执行的这一行指令在 .so文件中的相对位置,即为 00007f55572573a8-7f5557249000=e3a8。因此,我们需要在 .so文件中找到 e3a8地址对应的函数就可以找到是哪里出现的崩溃了。

找到崩溃的地方

我们可以使用 objdump来查看二进制可能的附带信息,这个附带信息一般都会有函数名称。因为 objdump里面包含的信息太多,通过 grep会更方便一些,找到离 e3a8最近的函数即可

objdump -tT B.so | grep e3返回

000000000000e350  w    F .text    00000000000000c2              _ZN1A1B1CI1DE1FERKS2_
000000000000e350  w   DF .text    00000000000000c2  Base        _ZN1A1B1CI1DE1FERKS2_

我们可以看到,离 e3a8最近的函数是 _ZN1A1B1CI1DE1EERKS2_,这个名字是经过 Name Mangling的,通过命令 c++filt _ZN1A1B1CI1DE1EERKS2_解析得到 A::B::C<D>::E(D const&),这个就是崩溃时候的函数了

Ref

  1. https://utcc.utoronto.ca/~cks/space/blog/linux/KernelSegfaultMessageMeaning
  2. https://en.wikipedia.org/wiki/Name_mangling
Last modification:December 13, 2021
If you think my article is useful to you, please feel free to appreciate