翻译自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 ();
}