int backtrace(void **buffer,int size)
- 该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址。
注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容*
char ** backtrace_symbols (void *const *buffer, int size)
- backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值),函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址
现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))
backtrace_symbols生成的字符串都是malloc出来的,但是不要最后一个一个的free,因为backtrace_symbols是根据backtrace给出的call stack层数,一次性的malloc出来一块内存来存放结果字符串的,所以,像上面代码一样,只需要在最后,free backtrace_symbols的返回指针就OK了。这一点backtrace的manual中也是特别提到的。
注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL
void backtrace_symbols_fd (void *const *buffer, int size, int fd)
- backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。
- 下边是实例:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#define SIZE 100
int mystr(void)
{
char *str = NULL;
char *str2 = "hello world";
strncpy(str, str2, strlen(str));//此处发生段错误
printf("str: [%s],str2:[%s]\n", str, str2);
return 0;
}
int myfun(void)
{
mystr();
}
int myfun1(void)
{
myfun();
}
void trace_dump(void)
{
int j, nptrs;
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);
}
void signal_handler(int signo)
{
printf("\n=========>>>catch signal %d <<<=========\n", signo);
printf("Dump stack start...\n");
trace_dump();
printf("Dump stack end...\n");
signal(signo, SIG_DFL); /* 恢复信号默认处理 */
raise(signo); /* 重新发送信号 */
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "%s num-calls\n", argv[0]);
exit(EXIT_FAILURE);
}
signal(SIGSEGV, signal_handler);
myfun1();
exit(EXIT_SUCCESS);
}
保存文件名为brace.c,然后编译:
gcc -g -rdynamic -o brate brate.c
生成brace,运行brace:
General-74-190:~/nfs/test/singal$ ./brate 2
串口打印如下:
=========>>>catch signal 11 <<<=========
Dump stack start...
backtrace() returned 9 addresses
./brate(trace_dump+0x1f) [0x400baf]
./brate(signal_handler+0x31) [0x400c68]
/lib/x86_64-linux-gnu/libc.so.6(+0x36150) [0x7f905e2d5150]
./brate(mystr+0x33) [0x400b37]
./brate(myfun+0x9) [0x400b83]
./brate(myfun1+0x9) [0x400b8e]
./brate(main+0x56) [0x400ce3]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f905e2c076d]
./brate() [0x400a49]
Dump stack end...
段错误 (核心已转储)
说明此时已经产生了段错误,相关的地址也打印出来,现在通过栈回溯的方法找到出错的地方:
eneral-74-190:~/nfs/test/singal$ addr2line -e brate 0x400b37
/data1/nfs/test/singal/brate.c:14
此时表明在第14行出现了段错误,和我们的代码一致。