一直以来都没有花太多精力放在学习调试方面,主要还是平时调试的机会相对较少,一般情况下,用strace、gdb、以及通过打印log基本上就能解决问题了,还有就是,与其花精力去提高调试技能,还不如在设计、防御式编程和单元测试等能力去提高,以及提高自已编码的质量,减少BUG的出现或者缩少BUG的范围。


 

  但是,有时使用调试工具并不是为了查找BUG,在阅读和分析源代码时也非常有用,下面的代码演示如何在自已的程序中打印调用堆栈,有时你想知道某一个函数在某一时刻被哪一个函数调用了,只要在这个函数中打印一下调用堆栈即可,在一些不方便使用调试工具的场合(例如程序在开发板上运行时)还是比较有用的。

 

-rdynamic和-g:

 



1. #include <stdio.h> 
2. #include <execinfo.h> 
3. void print_trace(void);  
4. void
5. {  
6. /* 打印调用堆栈,看看谁调用了本函数 */
7.     print_trace();  
8. }  
9. void
10. {  
11.     funcC();  
12. }  
13. void
14. {  
15.     funcB();  
16. }  
17. int main (void)  
18. {  
19.     funcA();  
20. return
21. }  
22. void print_trace(void)  
23. {  
24. int
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
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
45. for
46.     {  
47. /* 得到调用堆栈中的函数的地址,然后将地址发送给 addr2line */
48. "%p/n", traceback[i]);  
49. /* addr2line 命令在收到地址后,会将函数地址所在的源代码位置打印到标准输出 */
50.     }  
51.     fclose(fp);  
52. }




 

  但是,有时使用调试工具并不是为了查找BUG,在阅读和分析源代码时也非常有用,下面的代码演示如何在自已的程序中打印调用堆栈,有时你想知道某一个函数在某一时刻被哪一个函数调用了,只要在这个函数中打印一下调用堆栈即可,在一些不方便使用调试工具的场合(例如程序在开发板上运行时)还是比较有用的。

 

-rdynamic和-g:

 


    1. #include <stdio.h> 
    2. #include <execinfo.h> 
    3. void print_trace(void);  
    4. void
    5. {  
    6. /* 打印调用堆栈,看看谁调用了本函数 */
    7.     print_trace();  
    8. }  
    9. void
    10. {  
    11.     funcC();  
    12. }  
    13. void
    14. {  
    15.     funcB();  
    16. }  
    17. int main (void)  
    18. {  
    19.     funcA();  
    20. return
    21. }  
    22. void print_trace(void)  
    23. {  
    24. int
    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
    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
    45. for
    46.     {  
    47. /* 得到调用堆栈中的函数的地址,然后将地址发送给 addr2line */
    48. "%p/n", traceback[i]);  
    49. /* addr2line 命令在收到地址后,会将函数地址所在的源代码位置打印到标准输出 */
    50.     }  
    51.     fclose(fp);  
    52. }