调试segfault 经验总结

最近遇到一个棘手的问题,用在现场的程序崩溃了,但是却无法复现,隔个一个月左右出一次,已经出现了三次,各种测试并没有能复现问题。现在只好把目光聚焦在能够获得的仅有的信息,系统日志上了,查看/var/log/message,找到了关键的一套信息:

 kernel: myapp[1427]: segfault at 494d1f84 ip 00d73e54 sp b17fac20 error 6 in libThdDllAlg.so[d63000+1e000]

这条信息太宝贵了,这是唯一能够获得的信息。但是怎么用呢?一头雾水。

经过查找资料终于有了一点眉目。

1.myapp是崩溃的程序名,后面中括号中的是PID号,对我没什么用。

2.后面的at 494d1f84 ip 00d73e54 sp b17fac20 ,at后面是发生段错误时访问的地址,是错误当时的地址,好像对我的调试用处不大,ip后面是指令地址,这个就非常重要了,配合后面库的基地址就可以找到出错的代码了。sp后面的stack pointer栈顶指针,貌似我也没用上。

3.error后面的6,这个还是比较重要的,但是得到的信息不多,只能得到信息:由于用户态程序写操作访问越界造成的。

error number是由三个字位组成的,从高到底分别为bit2 bit1和bit0,所以它的取值范围是0~7.
bit2: 值为1表示是用户态程序内存访问越界,值为0表示是内核态程序内存访问越界
bit1: 值为1表示是写操作导致内存访问越界,值为0表示是读操作导致内存访问越界
bit0: 值为1表示没有足够的权限访问非法地址的内容,值为0表示访问的非法地址根本没有对应的页面,也就是无效地址

4. libThdDllAlg.so[d63000+1e000]这个是崩在了哪个库上,这个非常重要,方框号里第一个是运行时库的基地址,跟ip后面的一块用,用ip后面的值减去基地址就可以得到出错在这个库里的哪个位置了。在这里是00d73e54-d63000=10e54.至于+号后面是什么意思,我还没搞懂。希望以后能补充

下面知道了出错的库和出错的指令地址,就可以看看是出在了哪条指令上了

还好这个库是我自己 写的库,不是系统库。用objdump -d libThdDllAlg.so>dumpcode 把文件反汇编写进dumpcode文件。这个libThdDllAlg.so是从现场拷贝回来了,而不是自己重新编译的。

打开dumpcode文件,找到10e54地址,往上翻能够找到是在哪个函数里,再结合源文件,找到上下文,大概就能知道出错在哪里了。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

前面讲到了把崩溃点定位到了函数级别,那么怎么找到是那一行代码出了问题呢?

经过后来的实践,总结了定位到行的方法。

之前的方法使用的前提是获得的release版程序的崩溃信息,由于时release版,没有调试信息,所以没法直接使用objdump来显示出源代码行号等信息。不过,我们仍然可以重新编译一个debug版本的汇编代码,与release版的反汇编来对照,也可以找到出错代码所在的行。

debug版objdump使用(引自objdump反汇编用法示例

-d:将代码段反汇编
-S:将代码段反汇编的同时,将反汇编代码和源代码交替显示,编译时需要给出-g,即需要调试信息。
-C:将C++符号名逆向解析。
-l:反汇编代码中插入源代码的文件名和行号。
-j section:仅反汇编指定的section。可以有多个-j参数来选择多个section。
 

怎样通过现有代码生成带有源代码和行号的汇编代码呢?

1.使用g++ -S -g -fverbose-asm来生成汇编代码,当然可以直接在原有的编译命令基础上增加这几个参数,由于已经定位到了函数,所以可以直接只编译函数所在的cpp文件,将cpp编译为s

例如:

g++ -Wall -L./lib -DLINUX -O2 -fPIC -S -g -fverbose-asm -o xxx.s xxx.cpp

2使用as来查看,当然也可以打印到文件中来与objdump出来的对比

例如:

as -alhnd xxx.s>dump.txt

3.现在我们有了两个汇编版本,一个是release版,也就是实际运行的版本反汇编出来的(以下简称release版),另一个是带调试信息和源代码的(以下简称debug版)。下面就是对比这两个文件了。

首先找到release版中崩溃发生的代码,从上下寻找特征代码,一般可以找特征数字

例如

   274da:    0f b7 4f 06              movzwl 0x6(%edi),%ecx
   274de:    0f b7 c0                 movzwl %ax,%eax
   274e1:    0f b7 57 02              movzwl 0x2(%edi),%edx
   274e5:    89 85 38 fe ff ff        mov    %eax,-0x1c8(%ebp)
   274eb:    66 89 8d 28 fe ff ff     mov    %cx,-0x1d8(%ebp)
   274f2:    89 95 30 fe ff ff        mov    %edx,-0x1d0(%ebp)
   274f8:    0f b7 85 28 fe ff ff     movzwl -0x1d8(%ebp),%eax
   274ff:    8b 8d 30 fe ff ff        mov    -0x1d0(%ebp),%ecx
   27505:    8b 95 10 fe ff ff        mov    -0x1f0(%ebp),%edx
   2750b:    89 44 24 10              mov    %eax,0x10(%esp)
   2750f:    8b 85 38 fe ff ff        mov    -0x1c8(%ebp),%eax
   27515:    89 4c 24 0c              mov    %ecx,0xc(%esp)
   27519:    89 44 24 08              mov    %eax,0x8(%esp)
   2751d:    e9 2c ff ff ff           jmp    2744e <_ZN9SlClass3RunEtt+0xdde>
   27522:    8d b6 00 00 00 00        lea    0x0(%esi),%esi
   27528:    8b 95 34 fe ff ff        mov    -0x1cc(%ebp),%edx
   2752e:    0f b7 c0                 movzwl %ax,%eax
   27531:    89 85 30 fe ff ff        mov    %eax,-0x1d0(%ebp)
   27537:    8b 42 04                 mov    0x4(%edx),%eax
   2753a:    8b 95 30 fe ff ff        mov    -0x1d0(%ebp),%edx
   27540:    8b 04 90                 mov    (%eax,%edx,4),%eax
   27543:    85 c0                    test   %eax,%eax
   27545:    0f 84 99 02 00 00        je     277e4 <_ZN9SlClass3RunEtt+0x1174>
   2754b:    0f b7 57 02              movzwl 0x2(%edi),%edx
   2754f:    3b 50 0c                 cmp    0xc(%eax),%edx
   27552:    89 95 04 fe ff ff        mov    %edx,-0x1fc(%ebp)
   27558:    0f 83 6a 01 00 00        jae    276c8 <_ZN9SlClass3RunEtt+0x1058>
   2755e:    8b 40 08                 mov    0x8(%eax),%eax
   27561:    8b 95 04 fe ff ff        mov    -0x1fc(%ebp),%edx
   27567:    8b 04 90                 mov    (%eax,%edx,4),%eax
   2756a:    85 c0                    test   %eax,%eax
   2756c:    89 85 28 fe ff ff        mov    %eax,-0x1d8(%ebp)
   27572:    0f 84 50 01 00 00        je     276c8 <_ZN9SlClass3RunEtt+0x1058>
   27578:    8b 95 28 fe ff ff        mov    -0x1d8(%ebp),%edx
   2757e:    0f b7 47 06              movzwl 0x6(%edi),%eax
   27582:    66 3b 42 06              cmp    0x6(%edx),%ax

这是我的relase版汇编代码,程序崩溃在27582行,我找的特征代码是274e5行,之所以选这个,是因为后面连续的几个代码都带有不同的数字,这个重复的概率就比较小了。这里有个地方要注意,这里是16进制,debug版文件里是10进制,需要转换一下。

-0x1c8转为-456,直接在debug版中搜索-456,找到movl    %eax, -456,再搜索%eax, -456,还好,只搜到4处,然后结合下一行代码,可以定位到是哪一个。看看上下文,就定位到了源文件所在的行。

-----------------------------------------------------------------------------------------------

引用以下文章

1.一次segfault错误的排查过程,引用地址https://blog.csdn.net/zhaohaijie600/article/details/45246569

                                                                                                 一次segfault错误的排查过程

正常运行了几年的程序忽然崩溃了,由于机器没有设置CORE文件,无法从CORE中取得错误信息,程序运行在centOS 7上, 本来对centOS用的也是不熟,只能边查资料边查问题。

首先、我需要确认程序是否真的崩溃了,还是别人误操作关闭了。如果程序真的崩溃了,会在系统中留下痕迹,我查了一下,在messages文件中发现了一条信息:

xxxxx.o[2374]: segfault at7f0ed0bfbf70 ip 00007f0edd646fe7 sp 00007f0ed3603978 error 4 inlibc-2.17.so[7f0edd514000+1b6000]

由上面信息看出,系统确实是崩溃了,发生了段错误。

查看messages需要root权限,用命令:cat /var/log/messages 就可以了,还有一个命令dmesg也可以查到上面的信息。

从上面的信息,我们可以得到以下信息:

1、从libc-2.17.so[7f0edd514000+1b6000]可以看出错误发生在libc上,libc在此程序中映射的内存基址为7f0edd514000,这个信息是个坏消息,这个so上的东西太多了;

2、segfault at和error 4这两条信息可以得出是内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值