日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux backtrack函数,Linux调用backtrack函数打印程序崩溃时的调用堆栈

發布時間:2023/12/15 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux backtrack函数,Linux调用backtrack函数打印程序崩溃时的调用堆栈 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

可以給自己的程序都加上這個東西,便于快速的找到錯誤吧,看到別人都是這么用的

#include

#include

#include

#include

//signal 函數用法參考http://www.kernel.org/doc/man-pages/online/pages/man2/signal.2.html

//backtrace ,backtrace_symbols函數用法參考 http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html

static void WidebrightSegvHandler(int signum) {

void *array[10];

size_t size;

char **strings;

size_t i, j;

signal(signum, SIG_DFL); /* 還原默認的信號處理handler */

size = backtrace (array, 10);

strings = (char **)backtrace_symbols (array, size);

fprintf(stderr, "widebright received SIGSEGV! Stack trace:\n");

for (i = 0; i < size; i++) {

fprintf(stderr, "%d %s \n",i,strings[i]);

}

free (strings);

exit(1);

}

int invalide_pointer_error(char * p)

{

*p = 'd'; //讓這里出現一個訪問非法指針的錯誤

return 0;

}

void error_2(char * p)

{

invalide_pointer_error(p);

}

void error_1(char * p)

{

error_2(p);

}

void error_0(char * p)

{

error_1(p);

}

int main()

{

//設置 信好的處理函數,各種 信號的定義見http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html

signal(SIGSEGV, WidebrightSegvHandler); // SIGSEGV????? 11?????? Core??? Invalid memory reference

signal(SIGABRT, WidebrightSegvHandler); // SIGABRT?????? 6?????? Core??? Abort signal from

char *a = NULL;

error_0(a);

exit(0);

}

widebright@widebright:~/桌面$ gcc main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048580]

1 [0xb807a400]

2 ./a.out [0x8048636]

3 ./a.out [0x8048649]

4 ./a.out [0x804865c]

5 ./a.out [0x80486a9]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7f19775]

然后為了定位錯誤,我們需要加上-g參數重新編譯一個帶調試信息的版本

widebright@widebright:~/桌面$ gcc -g main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048580]

1 [0xb7fb3400]

2 ./a.out [0x8048636]

3 ./a.out [0x8048649]

4 ./a.out [0x804865c]

5 ./a.out [0x80486a9]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7e52775]

7 ./a.out [0x80484c1]

加上-rdynamic 參數的話,輸出的符號更清楚一些,不過好像地址不一樣了。

widebright@widebright:~/桌面$ gcc -g -rdynamic main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048840]

1 [0xb7f3d400]

2 ./a.out(error_2+0x11) [0x80488f6]

3 ./a.out(error_1+0x11) [0x8048909]

4 ./a.out(error_0+0x11) [0x804891c]

5 ./a.out(main+0x4b) [0x8048969]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7ddc775]

7 ./a.out [0x8048781]

可以看到有調試信息的時候,錯誤是一樣的。然后就可以用gdb定位和調試錯誤了:

-----------------------

(gdb) info line *0x8048580

Line 19 of "main.c" starts at address 0x804856d

and ends at 0x8048583 .

(gdb) list *0x8048580

0x8048580 is in WidebrightSegvHandler (main.c:19).

14??? ??? char **strings;

15??? ??? size_t i, j;

16

17??? ??? signal(signum, SIG_DFL); /* 還原默認的信號處理handler */

18

19??? ??? size = backtrace (array, 10);

20??? ??? strings = (char **)backtrace_symbols (array, size);

21

22??? ??? fprintf(stderr, "widebright received SIGSEGV! Stack trace:\n");

23??? ??? for (i = 0; i < size; i++) {

-----------------

(gdb) list *0x8048636

0x8048636 is in error_2 (main.c:41).

36

37

38??? void error_2(char * p)

39??? {

40??? ??? invalide_pointer_error(p);

41??? }

42

43??? void error_1(char * p)

44??? {

45??? ???? error_2(p);

--------------

(gdb) list *0x8048649

0x8048649 is in error_1 (main.c:46).

41??? }

42

43??? void error_1(char * p)

44??? {

45??? ???? error_2(p);

46??? }

47

48??? void error_0(char * p)

49??? {

50??? ???? error_1(p);

=============

(gdb) br main.c:40

Breakpoint 1 at 0x804862b: file main.c, line 40.

(gdb) run

Starting program: /home/widebright/桌面/a.out

Breakpoint 1, error_2 (p=0x0) at main.c:40

40??? ??? invalide_pointer_error(p);

(gdb) stepi

0x0804862e??? 40??? ??? invalide_pointer_error(p);

(gdb) stepi

0x08048631??? 40??? ??? invalide_pointer_error(p);

(gdb) stepi

invalide_pointer_error (p=0x0) at main.c:32

32??? {

(gdb) stepi

0x08048616??? 32??? {

(gdb) stepi

33??? ??? *p = 'd'; //讓這里出現一個訪問非法指針的錯誤

(gdb) stepi

0x0804861b??? 33??? ??? *p = 'd'; //讓這里出現一個訪問非法指針的錯誤

(gdb) stepi

Program received signal SIGSEGV, Segmentation fault.

0x0804861b in invalide_pointer_error (p=0x0) at main.c:33

33??? ??? *p = 'd'; //讓這里出現一個訪問非法指針的錯誤

(gdb) print p

$1 = 0x0

(gdb) print *p

Cannot access memory at address 0x0

===============================================

好像使用

int sigaction(int signum, const struct sigaction *act,

struct sigaction *oldact);

http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html

這個函數注冊信號的處理函數的話,可以得到更多的信息,比如出錯 時候的寄存器的值等等。

因為他函數 最后一個參數傳過來一個ucontext_t *ucontext 的指針

可以看到 “善用backtrace解決大問題” http://blog.chinaunix.net/u/3425/showart_263408.html 這個網頁上有給出一個例子。

最初看到這個用法的的在redhat的安裝程序的anaconda里面的。

------------------------

關于backtrack的原理 的解釋,參考這個:

從別人blog上拷來的,地址:http://blog.csdn.net/absurd/archive/2005/12/13/551585.aspx

開發嵌入式軟件通常是比較麻煩的事,一些常用的工具往往無法使用,在開發PC軟件時簡單的任務,此時變得很復雜。今天就遇到了這樣一件事,折騰了幾個小時,僅僅是為知道call stack。

我編譯了一個程序放到PDA(ARM9+LINUX+UCLIBC)上面運行,出現了一個ASSERT,并顯示了文件名和行號,原來是調用了一個沒有實現 的函數,我很想知道是誰調用了它,這看似簡單的問題卻讓我很頭疼,如果有gdb,那好辦-用bt命令就可以搞定,如果用的libc,那也好辦-用 backtrace函數就可以搞定,問題是兩者都沒有。

想來想去只有自己寫一個backtrace,要實現這個功能并不難,如果我們知道調用堆棧的格式,就可以很容易取出上層調用者的指令地址,有了這些上層調用者的指令地址,我們可以通過MAP文件找到指令地址對應的源文件名和行號。

下面簡要介紹一下實現原理:

要獲得調用者的地址,有必要介紹一下堆棧的格式:

+---------------------------+ (高地址)

+_參數1__________+

+---------------------------+

+_參數2__________+

+---------------------------+ 參數的順序依賴于調用方式

+_參數.__________+

+---------------------------+

+_參數N__________+

+---------------------------+

+_eip____________+ 返回本次調用后,下一條指令的地址

+----------------------------+

+_ebp____________+ 這里保存的調用者的ebp

+----------------------------+

(ebp 指向這里:相當于調用者和被調用者的分界線)

+----------------------------+

+_臨時變量1_______+

+----------------------------+

+_臨時變量2_______+

+----------------------------+

+_臨時變量.________+

+----------------------------+

+----------------------------+

+_臨時變量N_______+

+----------------------------+(低地址)

由于優化、調用方式、編譯器的不同,上述布局部可能有所不同,但一般來說,第一個局部變量前是調用者的ebp,ebp前是返回后下一條指令的地址。

知道了這個結構,要獲得上層調用的者指令地址就容易了,我們可以用如下代碼模擬glibc提供的backtrace的功能:

int backtrace (void **BUFFER, int SIZE)

{

int n = 0;

int *p = &n;

int i = 0;

int ebp = p[1];

int eip = p[2];

for(i = 0; i < SIZE; i++)

{

BUFFER[i] = (void*)eip;

p = (int*)ebp;

ebp = p[0];

eip = p[1];

}

return SIZE;

}

附:

通過addr2line可以找到地址對應的文件名和行號,不用手動去查MAP文件了。

=======================

windows系統上面要實現同樣的功能,可能要調用

Debug Help Library 里面的StackWalk64 等函數。

http://msdn.microsoft.com/en-us/library/ms680650(VS.85).aspx

找到一個使用StackWalk64 的例子http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html

這里又是一個模擬backtrace(stackwalk)函數的例子

http://www.cnblogs.com/lbq1221119/archive/2008/04/18/1159956.html

其實你可以在程序的任何地方調用backtrace和 stackwalk函數的,呵呵

總結

以上是生活随笔為你收集整理的linux backtrack函数,Linux调用backtrack函数打印程序崩溃时的调用堆栈的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。