在程序中操作调用栈

作者: 青蛙跳井 | 来源:发表于2014-04-29 10:50 被阅读0次

    翻译自http://www.gnu.org/software/libc/manual/html_node/Backtraces.html#Backtraces


    调用栈(Backtraces)

    调用栈是线程中当前激活的函数调用列表。通常借助外部调试工具如gdb来查看调用栈。然而,有时候在程序中通过编码的方式获取调用栈是非常有用的,例如,为了记录日志或者诊断程序。

    头文件execinfo.h声明了三个函数,从当前线程中获取和操作调用栈。


    函数:int backtrace (void **buffer, int size)

    Preliminary: | MT-Safe | AS-Unsafe init heap dlopen plugin lock | AC-Unsafe init mem lock fd | See POSIX Safety Concepts.

    backtrace 函数获取当前线程的调用栈作为一个列表(list of pointers),并将信息装入 buffer 中。参数 size 应当是将要装入 buffer 的 void * 类型元素的个数。返回值是实际装入 buffer 的全部元素的个数,最大值为 size

    buffer 中的指针全部返回通过检查堆栈(stack)获得的实际地址,每个指针返回一层栈帧(stack frame)的地址。

    需要注意的是某些编译器优化会打乱获取的有效的调用栈。函数内联导致内联函数不拥有调用帧栈;尾调用优化会用一个调用帧栈取代另一个;帧指针缺失将阻止 backtrace 正确解释堆栈内容。


    函数:char ** backtrace_symbols (void *const *buffer int size)

    Preliminary: | MT-Safe | AS-Unsafe heap | AC-Unsafe mem lock | See POSIX Safety Concepts.

    backtrace_symbols 函数将 backtrace 函数获取的信息转换成一个字符串数组。参数 buffer 应但是指向通过 backtrace 函数获取的地址数组的指针,size 是该数组的元素个数(backtrace的返回值)。

    返回值是指向一个字符串数组的指针,改数组和 buffer 一样拥有 size 个元素。每个字符转都包含 buffer 中元素对应的可阅读描述。其中包含函数名(如果可以确定),进入函数的偏移量,和实际返回地址(十六进制)。

    当前,函数名和偏移量只能在使用 ELF 作为程序和库的二进制格式的系统上获取。在其他系统上,只有十六进制返回地址可以表示。当然,你也可能需要通过向连接器传递额外标记使程序可以获取函数名。(例如,在使用 GNU ld 的系统上,你需要传递 -rdynamic)

    backtrace_symbols 的返回值是通过 malloc 函数获取的指针,调用者有义务释放该指针。注意只有返回值需要被释放,不是单独的字符串。

    如果没有足够的内存存储字符串,返回值为 NULL 。


    函数:void backtrace_symbols_fd (void *const *buffer, int size, int fd)

    Preliminary: | MT-Safe | AS-Safe | AC-Unsafe lock | See POSIX Safety Concepts.

    backtrace_symbols_fd 函数执行与 backtrace_symbols 函数相同的转换。该函数不将字符串返回给调用者,而是将字符串写入描述符为 fd 的文件,每个一行。由于不使用 malloc 函数,因此可以在该函数可能失败的情况下使用。


    下面的程序说明了这些函数的用法。注意包含由 backtrace 返回的地址的数组被分配到了栈上。因此这样的代码可以在无法通过 malloc 获取内存的情况下使用(同样也是 backtrace_symbols 需要呗替换为 backtrace_symbols_fd 的情况)。一般情况下返回的地址的个数不会太多。即使复杂的程序也很少有超过50嵌套的层次,200条记录应该足以覆盖所有程序了。

    #include <execinfo.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    /* Obtain a backtrace and print it to stdout. */
    void
    print_trace (void)
    {
      void *array[10];
      size_t size;
      char **strings;
      size_t i;
    
      size = backtrace (array, 10);
      strings = backtrace_symbols (array, size);
    
      printf ("Obtained %zd stack frames.\n", size);
    
      for (i = 0; i < size; i++)
         printf ("%s\n", strings[i]);
    
      free (strings);
    }
    
    /* A dummy function to make the backtrace more interesting. */
    void
    dummy_function (void)
    {
      print_trace ();
    }
    

    相关文章

      网友评论

        本文标题:在程序中操作调用栈

        本文链接:https://www.haomeiwen.com/subject/uukrtttx.html