溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

linux程序莫名異常怎么查

發(fā)布時(shí)間:2020-06-16 18:14:05 來源:網(wǎng)絡(luò) 閱讀:8461 作者:鄔領(lǐng)東 欄目:編程語言

內(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)存泄漏的元兇被揪出來了,呵呵。

向AI問一下細(xì)節(jié)

免責(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)容。

AI