在Linux中如何利用backtrace信息解決問題_第1頁
在Linux中如何利用backtrace信息解決問題_第2頁
在Linux中如何利用backtrace信息解決問題_第3頁
在Linux中如何利用backtrace信息解決問題_第4頁
在Linux中如何利用backtrace信息解決問題_第5頁
已閱讀5頁,還剩2頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、在Linux中如何利用backtrace信息解決問題一、導(dǎo)讀在程序調(diào)試過程中如果遇到程序崩潰死機(jī)的情況下我們通常多是通過出問題時(shí)的棧信息來找到出錯(cuò)的地方,這一點(diǎn)我們?cè)谡{(diào)試一些高級(jí)編程語言程序的時(shí)候會(huì)深有體會(huì),它們通常在出問題時(shí)會(huì)主動(dòng)把出問題時(shí)的調(diào)用棧信息打印出來,比如我們?cè)趀clipse中調(diào)試Java程序時(shí)。當(dāng)這些換到Linux上的C/C+環(huán)境時(shí)情況將變的稍微復(fù)雜一些,通常在這種情況下是通過拿到出問題時(shí)產(chǎn)生的core文件然后再利用gdb調(diào)試來看到出錯(cuò)時(shí)的程序棧信息,這是再好不過的了,但當(dāng)某些特殊的情況如不正確的系統(tǒng)設(shè)置或文件系統(tǒng)出現(xiàn)問題時(shí)導(dǎo)致我們沒有拿到core文件那我們還有補(bǔ)救的辦法嗎?本文

2、將介紹在程序中安排當(dāng)出現(xiàn)崩潰退出時(shí)把當(dāng)前調(diào)用棧通過終端打印出來并定位問題的方法。二、輸出程序的調(diào)用棧1、獲取程序的調(diào)用棧在Linux上的C/C+編程環(huán)境下,我們可以通過如下三個(gè)函數(shù)來獲取程序的調(diào)用棧信息。#include <execinfo.h>/* Store up to SIZE return address of the current program state in ARRAY and return the exact number of values stored. */int backtrace(void *array, int size);/* Return nam

3、es of functions from the backtrace list in ARRAY in a newly malloc()ed memory block. */char *backtrace_symbols(void *const *array, int size);/* This function is similar to backtrace_symbols() but it writes the result immediately to a file. */void backtrace_symbols_fd(void *const *array, int size, in

4、t fd);它們由GNU C Library提供,關(guān)于它們更詳細(xì)的介紹可參考Linux Programmers Manual中關(guān)于backtrack相關(guān)函數(shù)的介紹。使用它們的時(shí)候有一下幾點(diǎn)需要我們注意的地方:backtrace的實(shí)現(xiàn)依賴于棧指針(fp寄存器),在gcc編譯過程中任何非零的優(yōu)化等級(jí)(-On參數(shù))或加入了棧指針優(yōu)化參數(shù)-fomit-frame-pointer后多將不能正確得到程序棧信息;backtrace_symbols的實(shí)現(xiàn)需要符號(hào)名稱的支持,在gcc編譯過程中需要加入-rdynamic參數(shù);內(nèi)聯(lián)函數(shù)沒有棧幀,它在編譯過程中被展開在調(diào)用的位置;尾調(diào)用優(yōu)化(Tail-call Op

5、timization)將復(fù)用當(dāng)前函數(shù)棧,而不再生成新的函數(shù)棧,這將導(dǎo)致棧信息不能正確被獲取。2、捕獲系統(tǒng)異常信號(hào)輸出調(diào)用棧當(dāng)程序出現(xiàn)異常時(shí)通常伴隨著會(huì)收到一個(gè)由內(nèi)核發(fā)過來的異常信號(hào),如當(dāng)對(duì)內(nèi)存出現(xiàn)非法訪問時(shí)將收到段錯(cuò)誤信號(hào)SIGSEGV,然后才退出。利用這一點(diǎn),當(dāng)我們?cè)谑盏疆惓P盘?hào)后將程序的調(diào)用棧進(jìn)行輸出,它通常是利用signal()函數(shù),關(guān)于系統(tǒng)信號(hào)的三、從backtrace信息分析定位問題1、測試程序?yàn)榱烁玫恼f明和分析問題,我這里將舉例一個(gè)小程序,它有三個(gè)文件組成分別是backtrace.c、dump.c、add.c,其中add.c提供了對(duì)一個(gè)數(shù)值進(jìn)行加一的方法,我們?cè)谒膱?zhí)行過程中故意

6、使用了一個(gè)空指針并為其賦值,這樣人為的造成段錯(cuò)誤的發(fā)生;dump.c中主要用于輸出backtrace信息,backtrace.c則包含了我們的man函數(shù),它會(huì)先注冊(cè)段錯(cuò)誤信號(hào)的處理函數(shù)然后去調(diào)用add.c提供的接口從而導(dǎo)致發(fā)生段錯(cuò)誤退出。它們的源程序分別如下:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片/* * add.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int add1(int num) int ret = 0x00; int *p

7、Temp = NULL; *pTemp = 0x01; /* 這將導(dǎo)致一個(gè)段錯(cuò)誤,致使程序崩潰退出 */ ret = num + *pTemp; return ret; int add(int num) int ret = 0x00; ret = add1(num); return ret; cpp view plain copy 在CODE上查看代碼片派生到我的代碼片/* * dump.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h&

8、gt; /* for signal */ #include <execinfo.h> /* for backtrace() */ #define BACKTRACE_SIZE 16 void dump(void) int j, nptrs; void *bufferBACKTRACE_SIZE; char *strings; nptrs = backtrace(buffer, BACKTRACE_SIZE); printf("backtrace() returned %d addressesn", nptrs); strings = backtrace_symb

9、ols(buffer, nptrs); if (strings = NULL) perror("backtrace_symbols"); exit(EXIT_FAILURE); for (j = 0; j < nptrs; j+) printf(" %02d %sn", j, stringsj); free(strings); void signal_handler(int signo) #if 0 char buff64 = 0x00; sprintf(buff,"cat /proc/%d/maps", getpid(); s

10、ystem(const char*) buff); #endif printf("n=>>>catch signal %d <<<=n", signo); printf("Dump stack start.n"); dump(); printf("Dump stack end.n"); signal(signo, SIG_DFL); /* 恢復(fù)信號(hào)默認(rèn)處理 */ raise(signo); /* 重新發(fā)送信號(hào) */ cpp view plain copy 在CODE上查看代碼片派生到我的代碼片/*

11、* backtrace.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> /* for signal */ #include <execinfo.h> /* for backtrace() */ extern void dump(void); extern void signal_handler(int signo); extern int add(int num); int main(int argc, char *

12、argv) int sum = 0x00; signal(SIGSEGV, signal_handler); /* 為SIGSEGV信號(hào)安裝新的處理函數(shù) */ sum = add(sum); printf(" sum = %d n", sum); return 0x00; 2、靜態(tài)鏈接情況下的錯(cuò)誤信息分析定位我們首先將用最基本的編譯方式將他們編譯成一個(gè)可執(zhí)行文件并執(zhí)行,如下:zoulmzoulm-VirtualBox:/home/share/work/backtrace$ gcc -g -rdynamic backtrace.c add.c dump.c -o backtr

13、acezoulmzoulm-VirtualBox:/home/share/work/backtrace$ ./backtrace =>>>catch signal 11 <<<=Dump stack start.backtrace() returned 8 addresses 00 ./backtrace(dump+0x1f) 0x400a9b 01 ./backtrace(signal_handler+0x31) 0x400b63 02 /lib/x86_64-linux-gnu/libc.so.6(+0x36150) 0x7f86afc7e150 03

14、./backtrace(add1+0x1a) 0x400a3e 04 ./backtrace(add+0x1c) 0x400a71 05 ./backtrace(main+0x2f) 0x400a03 06 /lib/x86_64-linux-gnu/libc.so.6(_libc_start_main+0xed) 0x7f86afc6976d 07 ./backtrace() 0x400919Dump stack end.段錯(cuò)誤 (核心已轉(zhuǎn)儲(chǔ))由此可見在調(diào)用完函數(shù)add1后就開始調(diào)用段錯(cuò)誤信號(hào)處理函數(shù)了,所以問題是出在函數(shù)add1中。這似乎還不夠,更準(zhǔn)確的位置應(yīng)該是在地址0x400a3e處,

15、但這到底是哪一行呢,我們使用addr2line命令來得到,執(zhí)行如下:zoulmzoulm-VirtualBox:/home/share/work/backtrace$ addr2line -e backtrace 0x400a3e/home/share/work/acktrace/add.c:132、動(dòng)態(tài)鏈接情況下的錯(cuò)誤信息分析定位然而我們通常調(diào)試的程序往往沒有這么簡單,通常會(huì)加載用到各種各樣的動(dòng)態(tài)鏈接庫。如果錯(cuò)誤是發(fā)生在動(dòng)態(tài)鏈接庫中那么處理將變得困難一些。下面我們將上述程序中的add.c編譯成動(dòng)態(tài)鏈接庫libadd.so,然后再編譯執(zhí)行backtrace看會(huì)得到什么結(jié)果呢。/* 編譯生成li

16、badd.so */gcc -g -rdynamic add.c -fPIC -shared -o libadd.so/* 編譯生成backtrace可執(zhí)行文件 */gcc -g -rdynamic backtrace.c dump.c -L. -ladd -Wl,-rpath=. -o backtrace其中參數(shù) -L. -ladd為編譯時(shí)鏈接當(dāng)前目錄的libadd.so;參數(shù)-Wl,-rpath=.為指定程序執(zhí)行時(shí)動(dòng)態(tài)鏈接庫搜索路徑為當(dāng)前目錄,否則會(huì)出現(xiàn)執(zhí)行找不到libadd.so的錯(cuò)誤。然后執(zhí)行backtrace程序結(jié)果如下:zoulmzoulm-VirtualBox:/home/sha

17、re/work/backtrace$ ./backtrace=>>>catch signal 11 <<<=Dump stack start.backtrace() returned 8 addresses 00 ./backtrace(dump+0x1f) 0x400a53 01 ./backtrace(signal_handler+0x31) 0x400b1b 02 /lib/x86_64-linux-gnu/libc.so.6(+0x36150) 0x7f8583672150 03 ./libadd.so(add1+0x1a) 0x7f85839fa5

18、c6 04 ./libadd.so(add+0x1c) 0x7f85839fa5f9 05 ./backtrace(main+0x2f) 0x400a13 06 /lib/x86_64-linux-gnu/libc.so.6(_libc_start_main+0xed) 0x7f858365d76d 07 ./backtrace() 0x400929Dump stack end.段錯(cuò)誤 (核心已轉(zhuǎn)儲(chǔ))此時(shí)我們?cè)儆们懊娴姆椒▽⒌貌坏饺魏涡畔?,如下:zoulmzoulm-VirtualBox:/home/share/work/backtrace$ addr2line -e libadd.so 0x

19、7f85839fa5c6?:0這是為什么呢?出現(xiàn)這種情況是由于動(dòng)態(tài)鏈接庫是程序運(yùn)行時(shí)動(dòng)態(tài)加載的而其加載地址也是每次可能多不一樣的,可見0x7f85839fa5c6是一個(gè)非常大的地址,和能得到正常信息的地址如0x400a13相差甚遠(yuǎn),其也不是一個(gè)實(shí)際的物理地址(用戶空間的程序無法直接訪問物理地址),而是經(jīng)過MMU(內(nèi)存管理單元)映射過的。有上面的認(rèn)識(shí)后那我們就只需要得到此次libadd.so的加載地址然后用0x7f85839fa5c6這個(gè)地址減去libadd.so的加載地址得到的結(jié)果再利用addr2line命令就可以正確的得到出錯(cuò)的地方;另外我們注意到(add1+0x1a)其實(shí)也是在描述出錯(cuò)的地

20、方,這里表示的是發(fā)生在符號(hào)add1偏移0x1a處的地方,也就是說如果我們能得到符號(hào)add1也就是函數(shù)add1在程序中的入口地址再加上偏移量0x1a也能得到正常的出錯(cuò)地址。我們先利用第一種方法即試圖得到libadd.so的加載地址來解決這個(gè)問題。我們可以通過查看進(jìn)程的maps文件來了解進(jìn)程的內(nèi)存使用情況和動(dòng)態(tài)鏈接庫的加載情況,所以我們?cè)诖蛴P畔⑶霸侔堰M(jìn)程的maps文件也打印出來,加入如下代碼:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片char buff64 = 0x00; sprintf(buff,"cat /proc/%d/maps",

21、 getpid(); system(const char*) buff); 然后編譯執(zhí)行得到如下結(jié)果(打印比較多這里摘取關(guān)鍵部分):.7f0962fb3000-7f0962fb4000 r-xp 00000000 08:01 2895572 /home/share/work/backtrace/libadd.so7f0962fb4000-7f09631b3000 -p 00001000 08:01 2895572 /home/share/work/backtrace/libadd.so7f09631b3000-7f09631b4000 r-p 00000000 08:01 2895572 /h

22、ome/share/work/backtrace/libadd.so7f09631b4000-7f09631b5000 rw-p 00001000 08:01 2895572 /home/share/work/backtrace/libadd.so.=>>>catch signal 11 <<<=Dump stack start.backtrace() returned 8 addresses 00 ./backtrace(dump+0x1f) 0x400b7f 01 ./backtrace(signal_handler+0x83) 0x400c99 02

23、/lib/x86_64-linux-gnu/libc.so.6(+0x36150) 0x7f0962c2b150 03 ./libadd.so(add1+0x1a) 0x7f0962fb35c6 04 ./libadd.so(add+0x1c) 0x7f0962fb35f9 05 ./backtrace(main+0x2f) 0x400b53 06 /lib/x86_64-linux-gnu/libc.so.6(_libc_start_main+0xed) 0x7f0962c1676d 07 ./race() 0x400a69Dump stack end.段錯(cuò)誤 (核心已轉(zhuǎn)儲(chǔ))Maps信息第一

24、項(xiàng)表示的為地址范圍如第一條記錄中的7f0962fb3000-7f0962fb4000,第二項(xiàng)r-xp分別表示只讀、可執(zhí)行、私有的,由此可知這里存放的為libadd.so的.text段即代碼段,后面的棧信息0x7f0962fb35c6也正好是落在了這個(gè)區(qū)間。所有我們正確的地址應(yīng)為0x7f0962fb35c6 - 7f0962fb3000 = 0x5c6,將這個(gè)地址利用addr2line命令得到如下結(jié)果:zoulmzoulm-VirtualBox:/home/share/work/backtrace$ addr2line -e libadd.so 0x5c6/home/share/work/backtrace/add.c:13可見也得到了正確的出錯(cuò)行號(hào)。接下來我們?cè)儆锰岬降牡诙N方法即想辦法得到函數(shù)add的入口地址再上偏移量來得到正確的地址。要得到一個(gè)函數(shù)的入口地址我們多種途徑和方法,比如生成查看程序的map文件;使用gcc的nm、readel

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論