linux程序莫名异常怎么查
内存异常经常导致程序出现莫名其妙的错误,往往很难查证,本文介绍在linux下的各种常见内存异常的查证工具和方法。
1 访问空指针/未初始化指针/重复释放内存
对于像访问空指针、未初始化指针(非法地址),重复释放内存等内存异常,linux默认会抛异常。
比如下面代码有空指针访问,编译运行后会coredump
intmain(){int*p=0;*p=6;return0;}
对于此类问题,我们只要在gcc编译程序时加入-g选项,同时在运行时能够生成coredump文件,利用gdb就可以定位到具体的问题代码行。
1.1 开启coredump
**ulimit -c unlimited** //unlimited表示不限制coredump文件大小,也可指定一个具体值来限制文件最大长度。
1.2 定制core文件名
默认的coredump文件名为core,如果想自己定制core文件名,可以运行如下命令:
**echo "./core-%e-%p-%t" > /proc/sys/kernel/core_pattern**
可以在core_pattern模板中使用变量还很多,见下面的列表:
%% 单个%字符
%p 所dump进程的进程ID
%u 所dump进程的实际用户ID
%g 所dump进程的实际组ID
%s 导致本次core dump的信号
%t core dump的时间 (由1970年1月1日计起的秒数)
%h 主机名
%e 程序文件名
1.3 使用gdb定位代码行
通过gdb即可定位出错代码行
root@ubuntu:/home/zte/test#gccnull.cc-groot@ubuntu:/home/zte/test#./a.outSegmentationfault(coredumped)root@ubuntu:/home/zte/test#gdba.outcore.......Corewasgeneratedby`./null'.ProgramterminatedwithsignalSIGSEGV,Segmentationfault.#00x00000000004004fdinmain()atnull.cc:44*p=6;
2、函数栈溢出
局部变量的写越界可能会破坏函数栈导致程序出现各种异常行为,但是OS默认不会在越界的第一现场coredump,因此导致问题查证非常困难。
幸运的是我们可以通过gcc的编译选项-fstack-protector 和 -fstack-protector-all在函数栈被破坏的函数返回时抛异常,从而可以很方便地定位问题所在函数。
代码示例
intmain(){inta=5;int*p=&a;p[3]=6;return0;}
上面代码会破坏函数栈,如果我们用gcc直接编译运行,不会抛异常。但是加了编译参数-fstack-protector 和 -fstack-protector-all后,再运行就会抛异常。下面是具体命令执行结果。
root@ubuntu:/home/zte/test#gcct.croot@ubuntu:/home/zte/test#./a.outroot@ubuntu:/home/zte/test#gcct.c-fstack-protector-fstack-protector-all-groot@ubuntu:/home/zte/test#./a.out***stacksmashingdetected***:./a.outterminatedAborted(coredumped)```可以进一步用gdb的bt命令定位出问题的函数。```root@ubuntu:/home/zte/test#gdba.outcore。。。。。。。。Corewasgeneratedby`./a.out'.ProgramterminatedwithsignalSIGABRT,Aborted.#00x00007f6bcfab5c37in__GI_raise(sig=sig@entry=6)at../nptl/sysdeps/unix/sysv/linux/raise.c:5656../nptl/sysdeps/unix/sysv/linux/raise.c:Nosuchfileordirectory.(gdb)bt#00x00007f6bcfab5c37in__GI_raise(sig=sig@entry=6)at../nptl/sysdeps/unix/sysv/linux/raise.c:56#10x00007f6bcfab9028in__GI_abort()atabort.c:89#20x00007f6bcfaf22a4in__libc_message(do_abort=do_abort@entry=1,fmt=fmt@entry=0x7f6bcfc01d70"***%s***:%sterminated\n")at../sysdeps/posix/libc_fatal.c:175#30x00007f6bcfb8d83cin__GI___fortify_fail(msg=<optimizedout>,msg@entry=0x7f6bcfc01d58"stacksmashingdetected")atfortify_fail.c:38#40x00007f6bcfb8d7e0in__stack_chk_fail()atstack_chk_fail.c:28#50x00000000004005aainmain()att.c:7(gdb)q
3 越界读写动态分配内存/读写已释放动态分配内存
动态分配内存读写越界、读写已释放动态分配内存系统往往不会抛异常,我们可以使用electric-fence来使得读写越界内存/已释放内存后立刻抛异常,加速问题定位。
3.1 安装Electric fence
sudo apt-get install electric-fence
3.2 使用Electric fence
下面是越界写代码
#include<stdlib.h>intmain(){int*p=(int*)malloc(sizeof(int));p[1]=6;return0;}
如果使用gcc直接编译运行,不会抛异常。
我们可以加上参数 -lefence -g编译后运行,就会抛异常。通过gdb的bt打印即可定位到问题代码行。
root@ubuntu:/home/zte/test#gccmalloc_read_free.cc-lefence-groot@ubuntu:/home/zte/test#./a.outElectricFence2.2Copyright(C)1987-1999BrucePerens<bruce@perens.com>Segmentationfault(coredumped)autogen.sh
4 内存泄漏
C/C++程序经常被内存泄漏问题困扰,本文介绍使用gperftools来快速定位内存泄漏问题。
4.1 安装gperftools工具
4.1.1 安装automake
sudo apt-get install automake
4.1.2 编译安装libunwind
从https://github.com/libunwind/libunwind/releases下载最新版本的libunwind源码包
解压到/usr/local/src目录
cd 解压源码目录
./autogen.sh
./configure
make -j6
make install
4.1.3 编译安装gperftools
从https://github.com/gperftools/gperftools/releases下载最新版本的gperftools源码包
解压到/usr/local/src目录
cd 解压源码目录
./autogen.sh
./configure
make -j6
make install
4.2 内存泄漏检测
下面是一段简单的内存泄漏源码
intmain(){int*p=(int*)malloc(sizeof(int));return0;}
编译代码、运行工具检察内存泄漏,注意设置下
root@ubuntu:/home/zte/#gccleak.cc-groot@ubuntu:/home/zte/#envHEAPCHECK=normalLD_PRELOAD=/usr/local/lib/libtcmalloc.so./a.outWARNING:Perftoolsheapleakcheckerisactive--PerformancemaysufferHavememoryregionsw/ocallers:mightreportfalseleaksLeakcheck_main_detectedleaksof4bytesin1objectsThe1largestleaks:***WARNING:Cannotconvertaddressestosymbolsinoutputbelow.***Reason:Cannotrun'pprof'(isPPROF_PATHsetcorrectly?)***Ifyoucannotfixthis,tryrunningpprofdirectly.Leakof4bytesin1objectsallocatedfrom:@40053f@7f334da06f45@400469Iftheprecedingstacktracesarenotenoughtofindtheleaks,tryrunningTHISshellcommand:pprof./a.out"/tmp/a.out.8497._main_-end.heap"--inuse_objects--lines--heapcheck--edgefraction=1e-10--nodefraction=1e-10--gvIfyouarestillpuzzledaboutwhytheleaksarethere,tryrerunningthisprogramwithHEAP_CHECK_TEST_POINTER_ALIGNMENT=1and/orwithHEAP_CHECK_MAX_POINTER_OFFSET=-1Iftheleakreportoccursinasmallfractionofruns,tryrunningwithTCMALLOC_MAX_FREE_QUEUE_SIZEoffewhundredMBorwithTCMALLOC_RECLAIM_MEMORY=false,itmighthelpfindleaksmorerepeatablExitingwitherrorcode(insteadofcrashing)becauseofwhole-programmemoryleaks
上面的关键的输入信息是:
Leak of 4 bytes in 1 objects allocated from:
@ 40053f //内存分配的指令地址
@ 7f334da06f45
@ 400469
由于工具没有直接输出问题代码行,我们通过反汇编来定位代码行:
objdump -S a.out //反汇编程序
截取汇编代码如下:
intmain(){40052d:55push%rbp40052e:4889e5mov%rsp,%rbp400531:4883ec10sub$0x10,%rspint*p=(int*)malloc(sizeof(int));400535:bf04000000mov$0x4,%edi40053a:e8f1feffffcallq400430<malloc@plt>40053f:488945f8mov%rax,-0x8(%rbp)return0;400543:b800000000mov$0x0,%eax
我们注意到40053f就是对应代码行int *p = (int*)malloc(sizeof(int));
至此,内存泄漏的元凶被揪出来了,呵呵。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。