在头文件*“execinfo.h”*中声明了三个函数用于获取当前线程的函数调用堆栈
#include
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);
Function: int backtrace(void**buffer, int size)
该函数用于获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。
参数buffer:是一个指针列表,保存获取的信息
参数size: 用来指buffer中可以保存多少个void* 元素。
函数返回值是实际获取的指针个数,最大不超过size大小
在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址。
注意: 某些编译器的优化选项对获取正确的调用堆栈有干扰, 另外内联函数没有堆栈框架; 删除框架指针也会使无法正确解析堆栈内容
backtrace() returns the number of addresses returned in buffer, which is not greater than size. If the return value is less than size, then the full backtrace was stored; if it
is equal to size, then it may have been truncated, in which case the addresses of the oldest stack frames are not returned.
Function: char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组
参数buffer:是从backtrace函数获取的数组指针,
size是该数组中的元素个数(backtrace的返回值)
函数返回值是一个指向字符串数组的指针,它的大小同buffer相同
On success, backtrace_symbols() returns a pointer to the array malloc(3)ed by the call; on error, NULL is returned.
每个字符串包含了一个相对于buffer中对应元素的可打印信息。它包括函数名,函数的偏移地址,和实际的返回地址现在,只有使用ELF二进制格式的程序和库中才能获取函数名称和偏移地址
在其他系统,只有十六进制的返回地址能被获取。另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))。该函数的返回值是通过malloc函数申请的空间,因此调用这必须使用free函数来释放指针
注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL
备注:gcc -rdynamic参数
选项 -rdynamic 用来通知链接器将所有符号添加到动态符号表中
(目的是能够通过使用 dlopen 来实现向后跟踪)
-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.
Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)
backtrace_symbols_fd() takes the same buffer and size arguments as backtrace_symbols(), but instead of returning an array of strings to the caller, it writes the strings, one per line, to the file descriptor fd.
backtrace_symbols_fd() does not call malloc(3), and so can be employed in situations where the latter function might fail.
backtrace_symbols_fd与backtrace_symbols函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行。它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。
下面是glibc中的实例:
#include
#include
#include
#include
void myfunc3(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
}
/* "static" means don't export the symbol... */
static void myfunc2(void)
{
myfunc3();
}
void myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "%s num-calls\n", argv[0]);
exit(EXIT_FAILURE);
}
myfunc(atoi(argv[1]));
exit(EXIT_SUCCESS);
}
运行结果:
[root@python zbb]# gcc trace.c -rdynamic -o trace
[root@python zbb]# ./trace 3
backtrace() returned 8 addresses
./trace(myfunc3+0x1f) [0x4009cc]
./trace() [0x400a61]
./trace(myfunc+0x25) [0x400a88]
./trace(myfunc+0x1e) [0x400a81]
./trace(myfunc+0x1e) [0x400a81]
./trace(main+0x59) [0x400ae3]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fde34fe63d5]
./trace() [0x4008e9]
[root@python zbb]# ./trace 2
backtrace() returned 7 addresses
./trace(myfunc3+0x1f) [0x4009cc]
./trace() [0x400a61]
./trace(myfunc+0x25) [0x400a88]
./trace(myfunc+0x1e) [0x400a81]
./trace(main+0x59) [0x400ae3]
我们还可以利用backtrace来定位程序段错误位置。
通常情况下,程序发生段错误时系统会发送SIGSEGV信号给程序,缺省处理是退出函数。我们可以使用 signal(SIGSEGV, &your_function); 函数来接管SIGSEGV信号的处理,程序在发生段错误后,自动调用我们准备好的函数,从而在那个函数里来获取当前函数调用栈。
#include
#include
#include
#include
#include
void Handler(int signo)
{
void *buffer[1024] = {0};
size_t size;
char **strings = NULL;
size_t i = 0;
size = backtrace(buffer, 1024);
fprintf(stdout, "backtrace() returned %d addresses\n", size);
strings = backtrace_symbols(buffer, size);
if (strings == NULL)
{
perror("backtrace_symbols.");
exit(EXIT_FAILURE);
}
for (i = 0; i < size; i++)
{
fprintf(stdout, "%s\n", strings[i]);
}
free(strings);
strings = NULL;
exit(0);
}
void myfunc3()
{
*((volatile char *)0x0) = 0x20;
}
static void myfunc2(void)
{
myfunc3();
}
void myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "%s num-calls\n", argv[0]);
exit(EXIT_FAILURE);
}
if (signal(SIGSEGV, Handler) == SIG_ERR)
perror("can't catch SIGSEGV");
myfunc(atoi(argv[1]));
exit(EXIT_SUCCESS);
}
[root@python zbb]# gcc -g testTrace.c -rdynamic -o testTrace
[root@python zbb]# ./testTrace 2
backtrace() returned 9 addresses
./testTrace(Handler+0x4f) [0x400a5c]
/lib64/libc.so.6(+0x36280) [0x7fbe462f4280]
./testTrace(myfunc3+0x9) [0x400b24]
./testTrace() [0x400b37]
./testTrace(myfunc+0x25) [0x400b5e]
./testTrace(myfunc+0x1e) [0x400b57]
./testTrace(main+0x78) [0x400bd8]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fbe462e03d5]
./testTrace() [0x400949]
[root@python zbb]# addr2line 0x400a5c -e testTrace -f
Handler
/home/zbb/testTrace.c:14
[root@python zbb]# addr2line 0x400b24 -e testTrace -f
myfunc3
/home/zbb/testTrace.c:34
Reference:
参考链接1
参考链接2