先来了解三个函数
#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);
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
#include
#include
#include
#include
#include
#include
void out_stack(char *sig);
void signal_exit(int dunno)
{
char* signal_str = "";
char dunno_str[10] = {0};
sprintf(dunno_str, "%d", dunno);
switch (dunno)
{
case 1:
signal_str = "SIGHUP(1)";
break;
case 2:
signal_str = "SIGINT(2:CTRL_C)"; //CTRL_C
break;
case 3:
signal_str = "SIGQUIT(3)";
break;
case 6:
{
signal_str = "SIGABRT(6)";
out_stack(signal_str);
}
break;
case 9:
signal_str = "SIGKILL(9)";
break;
case 15:
signal_str = "SIGTERM(15 KILL)"; //kill
break;
case 11:
{
signal_str = "SIGSEGV(11)"; //SIGSEGV
out_stack(signal_str);
}
break;
default:
signal_str = "OTHER";
break;
}
exit(0);
}
static void output_addrline(char addr[])
{
char cmd[256];
char line[256];
char addrline[32]={0,};
char *str1, *str2;
FILE* file;
str1 = strchr(addr,'[');
str2 = strchr(addr, ']');
if(str1 == NULL || str2 == NULL)
{
return;
}
memcpy(addrline, str1 + 1, str2 -str1);
snprintf(cmd, sizeof(cmd), "addr2line -e /proc/%d/exe %s ", getpid(), addrline);
file = popen(cmd, "r");
if(NULL != fgets(line, 256, file))
{
printf("%s
", line);
}
pclose(file);
}
void out_stack(char *sig)
{
void *array[32];
size_t size;
char **strings;
int i;
printf("%s
", sig);
size = backtrace (array, 32);
strings = backtrace_symbols (array, size);
if (NULL == strings)
{
printf("backtrace_symbols
");
return ;
}
for (i = 0; i < size; i++)
{
printf("%s",strings[i]);
output_addrline(strings[i]);
}
free(strings);
}
void test3(int n)
{
char *str;
printf("in test3 [%d]
", n);
strcpy(str, "123");
}
void test2(int n)
{
printf("in test2 [%d]
", n);
test3(3);
}
void test1(int n)
{
printf("in test1 [%d]
", n);
test2(2);
}
int main()
{
signal(SIGHUP, signal_exit);
signal(SIGINT, signal_exit);
signal(SIGQUIT, signal_exit);
signal(SIGABRT, signal_exit);
signal(SIGKILL, signal_exit);
signal(SIGTERM, signal_exit);
signal(SIGSEGV, signal_exit);
test1(1);
}
注意编译的时候要加-g 和 -rdynamic选项