您好,登錄后才能下訂單哦!
內存異常經常導致程序出現莫名其妙的錯誤,往往很難查證,本文介紹在linux下的各種常見內存異常的查證工具和方法。
1 訪問空指針/未初始化指針/重復釋放內存
對于像訪問空指針、未初始化指針(非法地址),重復釋放內存等內存異常,linux默認會拋異常。
比如下面代碼有空指針訪問,編譯運行后會coredump
int main() { int *p=0; *p=6; return 0; }
對于此類問題,我們只要在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# 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、函數棧溢出
局部變量的寫越界可能會破壞函數棧導致程序出現各種異常行為,但是OS默認不會在越界的第一現場coredump,因此導致問題查證非常困難。
幸運的是我們可以通過gcc的編譯選項-fstack-protector 和 -fstack-protector-all在函數棧被破壞的函數返回時拋異常,從而可以很方便地定位問題所在函數。
代碼示例
int main() { int a=5; int *p=&a; p[3]=6; return 0; }
上面代碼會破壞函數棧,如果我們用gcc直接編譯運行,不會拋異常。但是加了編譯參數-fstack-protector 和 -fstack-protector-all后,再運行就會拋異常。下面是具體命令執行結果。
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) ``` 可以進一步用gdb的bt命令定位出問題的函數。 ``` 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 越界讀寫動態分配內存/讀寫已釋放動態分配內存
動態分配內存讀寫越界、讀寫已釋放動態分配內存系統往往不會拋異常,我們可以使用electric-fence來使得讀寫越界內存/已釋放內存后立刻拋異常,加速問題定位。
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直接編譯運行,不會拋異常。
我們可以加上參數 -lefence -g編譯后運行,就會拋異常。通過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 內存泄漏
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 內存泄漏檢測
下面是一段簡單的內存泄漏源碼
int main() { int *p = (int*)malloc(sizeof(int)); return 0; }
編譯代碼、運行工具檢察內存泄漏,注意設置下
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
上面的關鍵的輸入信息是:
Leak of 4 bytes in 1 objects allocated from:
@ 40053f //內存分配的指令地址
@ 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就是對應代碼行int *p = (int*)malloc(sizeof(int));
至此,內存泄漏的元兇被揪出來了,呵呵。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。