linux c 程序异常退出时打印堆栈调用信息

2019-07-12 23:20发布

先来了解三个函数 #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选项