1 #include <stdio.h> 2 #include <execinfo.h> 3 void print_trace(void); 4 void funcC() 5 { 6 /* 打印调用堆栈,看看谁调用了本函数 */ 7 print_trace(); 8 } 9 void funcB() 10 { 11 funcC(); 12 } 13 void funcA() 14 { 15 funcB(); 16 } 17 int main (void) 18 { 19 funcA(); 20 return 0; 21 } 22 void print_trace(void) 23 { 24 int i; 25 const int MAX_CALLSTACK_DEPTH = 32; /* 需要打印堆栈的最大深度 */ 26 void *traceback[MAX_CALLSTACK_DEPTH]; /* 用来存储调用堆栈中的地址 */ 27 /* 利用 addr2line 命令可以打印出一个函数地址所在的源代码位置 28 * 调用格式为: addr2line -f -e /tmp/a.out 0x400618 29 * 使用前,源代码编译时要加上 -rdynamic -g 选项 30 */ 31 char cmd[512] = "addr2line -f -e "; 32 char *prog = cmd + strlen(cmd); 33 /* 得到当前可执行程序的路径和文件名 */ 34 int r = readlink("/proc/self/exe",prog,sizeof(cmd)-(prog-cmd)-1); 35 /* popen会fork出一个子进程来调用/bin/sh, 并执行cmd字符串中的命令, 36 * 同时,会创建一个管道,由于参数是'w', 管道将与标准输入相连接, 37 * 并返回一个FILE的指针fp指向所创建的管道,以后只要用fp往管理里写任何内容, 38 * 内容都会被送往到标准输入, 39 * 在下面的代码中,会将调用堆栈中的函数地址写入管道中, 40 * addr2line程序会从标准输入中得到该函数地址,然后根据地址打印出源代码位置和函数名。 41 */ 42 FILE *fp = popen(cmd, "w"); 43 /* 得到当前调用堆栈中的所有函数地址,放到traceback数组中 */ 44 int depth = backtrace(traceback, MAX_CALLSTACK_DEPTH); 45 for (i = 0; i < depth; i++) 46 { 47 /* 得到调用堆栈中的函数的地址,然后将地址发送给 addr2line */ 48 fprintf(fp, "%p/n", traceback[i]); 49 /* addr2line 命令在收到地址后,会将函数地址所在的源代码位置打印到标准输出 */ 50 } 51 fclose(fp); 52 }