函数调用堆栈追踪(1)

   对于一个比较大的工程,函数的调用关系错综复杂,这时候仅仅靠IDE静态查看函数的调用关系已经远远不够,所以为了更好地理解代码,还是有必要去掌握一些方法和工具来跟踪函数的调用堆栈。

一、      设断点

eclipse里可以设置断点查看函数的堆栈调用情况,这里比较方便的是可以直接定位到上级函数查看变量的值

函数调用堆栈追踪(1)_第1张图片

二、      使用backtrace

backtrace功能可以跟踪函数的调用堆栈,这个需要glibc库的支持,所以一般情况下是在linux平台使用。当然使用msys2cygwin等工具在win32平台下模拟linux环境也是可以的,就是需要折腾一下。

要获取函数的调用堆栈信息,主要通过以下3个函数来实现:

      #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);

backtrace只是获得了函数的堆栈地址,保存到buffer指针数组里,每个数组元素保存一个堆栈地址,size为堆栈的最大深度。

backtrace_symbols把堆栈地址转换为函数符号保存到一个二维指针里并返回,static函数不能导出

backtrace_symbols_fd的作用和backtrace_symbols类似,只是把函数符号导出的文件。

具体的例子可以参考:

http://man7.org/linux/man-pages/man3/backtrace.3.html

程序如下:

   #include 
       #include 
       #include 
       #include 

       #define BT_BUF_SIZE 100

       void
       myfunc3(void)
       {
           int j, nptrs;
           void *buffer[BT_BUF_SIZE];
           char **strings;

           nptrs = backtrace(buffer, BT_BUF_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);
       }

       static void   /* "static" means don't export the symbol... */
       myfunc2(void)
       {
           myfunc3();
       }

       void
       myfunc(int ncalls)
       {
           if (ncalls > 1)
               myfunc(ncalls - 1);
           else
               myfunc2();
       }

       int
       main(int argc, char *argv[])
       {
           if (argc != 2) {
               fprintf(stderr, "%s num-calls\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           myfunc(atoi(argv[1]));
           exit(EXIT_SUCCESS);
       }

注意编译的时候要加上-rdynamic选项,eclipse在工程选项里设置如下:

函数调用堆栈追踪(1)_第2张图片

运行结果如下:

           backtrace() returned 8 addresses
           ./prog(myfunc3+0x5c) [0x80487f0]
           ./prog [0x8048871]
           ./prog(myfunc+0x21) [0x8048894]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(main+0x65) [0x80488fb]
           /lib/libc.so.6(__libc_start_main+0xdc) [0xb7e38f9c]
           ./prog [0x8048711]

对于static函数,可以通过addr2line来获取对应的符号信息,对应以上的例子程序procmyfunc2static函数,backtrace取到的地址是0x8048871,使用addr2line的转换如下:

addr2line -e proc 0x8048871 –f

最后的结果应该是函数名、文件名和行号:

myfun2
./prog.c:37
         

你可能感兴趣的:(调试方法)