原文出處 http://blog.csdn.net/yetyongjin/article/details/7759144
以下不能windows + mingw下執行. windows下參考 http://code.google.com/p/backtrace-mingw/
我們知道,GDB的backtrace命令可以查看堆棧信息。但很多時候,GDB根本用不上。比如說,在線上環境中可能沒有GDB,即使有,也不太可能讓我們直接在上面調試。如果能讓程序自己輸出調用棧,那是最好不過了。本文介紹和調用椎棧相關的幾個函數。
NAME
backtrace, backtrace_symbols, backtrace_symbols_fd - support for application self-debugging
SYNOPSIS
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
以上內容源自這幾個函數的man手冊。
先簡單介紹一下這幾個函數的功能:
l backtrace:獲取當前的調用棧信息,結果存儲在buffer中,返回值為棧的深度,參數size限制棧的最大深度,即最大取size步的棧信息。
l backtrace_symbols:把backtrace獲取的棧信息轉化為字符串,以字符指針數組的形式返回,參數size限定轉換的深度,一般用backtrace調用的返回值。
l backtrace_symbols_fd:它的功能和backtrace_symbols差不多,只不過它不把轉換結果返回給調用方,而是寫入fd指定的文件描述符。
Man手冊里,給出了一個簡單的實例,我們看一下:
1 #include<execinfo.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 6 void myfunc3(
void) {
7 int j, nptrs;
8 #define SIZE 100
9 void *buffer[100];
10 char **strings;
11 nptrs = backtrace(buffer, SIZE);
12 printf("backtrace() returned %d addresses\n", nptrs);
13 /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
14 * would produce similar output to the following: */15 16 strings = backtrace_symbols(buffer, nptrs);
17 if (strings == NULL) {
18 perror("backtrace_symbols");
19 exit(EXIT_FAILURE);
20 }
21 22 for (j = 0; j < nptrs; j++)
23 printf("%s\n", strings[j]);
24 free(strings);
25 }
26 27 static void myfunc2(
void) {
/* "static" means don't export the symbol
*/28 myfunc3();
29 }
30 31 void myfunc(
int ncalls) {
32 if (ncalls > 1)
33 myfunc(ncalls - 1);
34 else35 myfunc2();
36 }
37 38 int main(
int argc,
char *argv[]) {
39 if (argc != 2) {
40 fprintf(stderr,"%s num-calls\n", argv[0]);
41 exit(EXIT_FAILURE);
42 }
43 myfunc(atoi(argv[1]));
44 exit(EXIT_SUCCESS);
45 }
46 編譯:
# cc prog.c -o prog
運行:
# ./prog 0
backtrace() returned 6 addresses
./prog() [0x80485a3]
./prog() [0x8048630]
./prog() [0x8048653]
./prog() [0x80486a7]
這樣,是輸出了調用棧,不過只是以十六進制輸出函數地址而已,可讀性很差。仔細看下man手冊,原來很簡單,編譯時加上個參數:
重新編譯:
# cc -rdynamic prog.c -o prog
通過gcc手冊,我們可以也解下參數的說明:
-rdynamic
Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of "dlopen" or to allow obtaining backtraces from within a program.
再執行:
# ./prog 0
backtrace() returned 6 addresses
./prog(myfunc3+0x1f) [0x8048763]
./prog() [0x80487f0]
./prog(myfunc+0x21) [0x8048813]
./prog(main+0x52) [0x8048867]
/lib/libc.so.6(__libc_start_main+0xe6) [0xaf9cc6]
./prog() [0x80486b1]
這回,可以看到函數名了。