您好,登錄后才能下訂單哦!
內(nèi)存異常經(jīng)常導(dǎo)致程序出現(xiàn)莫名其妙的錯(cuò)誤,往往很難查證,本文介紹在linux下的各種常見內(nèi)存異常的查證工具和方法。
1 訪問空指針/未初始化指針/重復(fù)釋放內(nèi)存
對(duì)于像訪問空指針、未初始化指針(非法地址),重復(fù)釋放內(nèi)存等內(nèi)存異常,linux默認(rèn)會(huì)拋異常。
比如下面代碼有空指針訪問,編譯運(yùn)行后會(huì)coredump
int main() { int *p=0; *p=6; return 0; }
對(duì)于此類問題,我們只要在gcc編譯程序時(shí)加入-g選項(xiàng),同時(shí)在運(yùn)行時(shí)能夠生成coredump文件,利用gdb就可以定位到具體的問題代碼行。
1.1 開啟coredump
**ulimit -c unlimited** //unlimited表示不限制coredump文件大小,也可指定一個(gè)具體值來限制文件最大長度。
1.2 定制core文件名
默認(rèn)的coredump文件名為core,如果想自己定制core文件名,可以運(yùn)行如下命令:
**echo "./core-%e-%p-%t" > /proc/sys/kernel/core_pattern**
可以在core_pattern模板中使用變量還很多,見下面的列表:
%% 單個(gè)%字符
%p 所dump進(jìn)程的進(jìn)程ID
%u 所dump進(jìn)程的實(shí)際用戶ID
%g 所dump進(jìn)程的實(shí)際組ID
%s 導(dǎo)致本次core dump的信號(hào)
%t core dump的時(shí)間 (由1970年1月1日計(jì)起的秒數(shù))
%h 主機(jī)名
%e 程序文件名
1.3 使用gdb定位代碼行
通過gdb即可定位出錯(cuò)代碼行
root@ubuntu:/home/zte/test# gcc null.cc -g root@ubuntu:/home/zte/test# ./a.out Segmentation fault (core dumped) root@ubuntu:/home/zte/test# gdb a.out core ....... Core was generated by `./null'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00000000004004fd in main () at null.cc:4 4 *p=6;
2、函數(shù)棧溢出
局部變量的寫越界可能會(huì)破壞函數(shù)棧導(dǎo)致程序出現(xiàn)各種異常行為,但是OS默認(rèn)不會(huì)在越界的第一現(xiàn)場coredump,因此導(dǎo)致問題查證非常困難。
幸運(yùn)的是我們可以通過gcc的編譯選項(xiàng)-fstack-protector 和 -fstack-protector-all在函數(shù)棧被破壞的函數(shù)返回時(shí)拋異常,從而可以很方便地定位問題所在函數(shù)。
代碼示例
int main() { int a=5; int *p=&a; p[3]=6; return 0; }
上面代碼會(huì)破壞函數(shù)棧,如果我們用gcc直接編譯運(yùn)行,不會(huì)拋異常。但是加了編譯參數(shù)-fstack-protector 和 -fstack-protector-all后,再運(yùn)行就會(huì)拋異常。下面是具體命令執(zhí)行結(jié)果。
root@ubuntu:/home/zte/test# gcc t.c root@ubuntu:/home/zte/test# ./a.out root@ubuntu:/home/zte/test# gcc t.c -fstack-protector -fstack-protector-all -g root@ubuntu:/home/zte/test# ./a.out *** stack smashing detected ***: ./a.out terminated Aborted (core dumped) ``` 可以進(jìn)一步用gdb的bt命令定位出問題的函數(shù)。 ``` root@ubuntu:/home/zte/test# gdb a.out core 。。。。。。。。 Core was generated by `./a.out'. Program terminated with signal SIGABRT, Aborted. #0 0x00007f6bcfab5c37 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 56../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory. (gdb) bt #0 0x00007f6bcfab5c37 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 #1 0x00007f6bcfab9028 in __GI_abort () at abort.c:89 #2 0x00007f6bcfaf22a4 in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f6bcfc01d70 "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175 #3 0x00007f6bcfb8d83c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f6bcfc01d58 "stack smashing detected") at fortify_fail.c:38 #4 0x00007f6bcfb8d7e0 in __stack_chk_fail () at stack_chk_fail.c:28 #5 0x00000000004005aa in main () at t.c:7 (gdb) q
3 越界讀寫動(dòng)態(tài)分配內(nèi)存/讀寫已釋放動(dòng)態(tài)分配內(nèi)存
動(dòng)態(tài)分配內(nèi)存讀寫越界、讀寫已釋放動(dòng)態(tài)分配內(nèi)存系統(tǒng)往往不會(huì)拋異常,我們可以使用electric-fence來使得讀寫越界內(nèi)存/已釋放內(nèi)存后立刻拋異常,加速問題定位。
3.1 安裝Electric fence
sudo apt-get install electric-fence
3.2 使用Electric fence
下面是越界寫代碼
#include <stdlib.h> int main() { int *p = (int*)malloc(sizeof(int)); p[1] = 6; return 0; }
如果使用gcc直接編譯運(yùn)行,不會(huì)拋異常。
我們可以加上參數(shù) -lefence -g編譯后運(yùn)行,就會(huì)拋異常。通過gdb的bt打印即可定位到問題代碼行。
root@ubuntu:/home/zte/test# gcc malloc_read_free.cc -lefence -g root@ubuntu:/home/zte/test# ./a.out Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com> Segmentation fault (core dumped)autogen.sh
4 內(nèi)存泄漏
C/C++程序經(jīng)常被內(nèi)存泄漏問題困擾,本文介紹使用gperftools來快速定位內(nèi)存泄漏問題。
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 內(nèi)存泄漏檢測
下面是一段簡單的內(nèi)存泄漏源碼
int main() { int *p = (int*)malloc(sizeof(int)); return 0; }
編譯代碼、運(yùn)行工具檢察內(nèi)存泄漏,注意設(shè)置下
root@ubuntu:/home/zte/# gcc leak.cc -g root@ubuntu:/home/zte/# env HEAPCHECK=normal LD_PRELOAD=/usr/local/lib/libtcmalloc.so ./a.out WARNING: Perftools heap leak checker is active -- Performance may suffer Have memory regions w/o callers: might report false leaks Leak check _main_ detected leaks of 4 bytes in 1 objects The 1 largest leaks: *** WARNING: Cannot convert addresses to symbols in output below. *** Reason: Cannot run 'pprof' (is PPROF_PATH set correctly?) *** If you cannot fix this, try running pprof directly. Leak of 4 bytes in 1 objects allocated from: @ 40053f @ 7f334da06f45 @ 400469 If the preceding stack traces are not enough to find the leaks, try running THIS shell command: pprof ./a.out "/tmp/a.out.8497._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --gv If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1 If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more repeatabl Exiting with error code (instead of crashing) because of whole-program memory leaks
上面的關(guān)鍵的輸入信息是:
Leak of 4 bytes in 1 objects allocated from:
@ 40053f //內(nèi)存分配的指令地址
@ 7f334da06f45
@ 400469
由于工具沒有直接輸出問題代碼行,我們通過反匯編來定位代碼行:
objdump -S a.out //反匯編程序
截取匯編代碼如下:
int main() { 40052d:55 push %rbp 40052e:48 89 e5 mov %rsp,%rbp 400531:48 83 ec 10 sub $0x10,%rsp int *p = (int*)malloc(sizeof(int)); 400535:bf 04 00 00 00 mov $0x4,%edi 40053a:e8 f1 fe ff ff callq 400430 <malloc@plt> 40053f:48 89 45 f8 mov %rax,-0x8(%rbp) return 0; 400543:b8 00 00 00 00 mov $0x0,%eax
我們注意到40053f就是對(duì)應(yīng)代碼行int *p = (int*)malloc(sizeof(int));
至此,內(nèi)存泄漏的元兇被揪出來了,呵呵。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。