在程序中操作调用栈

作者: 青蛙跳井 | 来源:发表于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 ();
}

相关文章

  • 在程序中操作调用栈

    翻译自http://www.gnu.org/software/libc/manual/html_node/Back...

  • 算法面经--栈

    栈 一、栈的实际需求 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出...

  • Stack Queue

    在函数调用的过程中,操作系统使用函数调用栈来维护函数调用的状态。每个函数运行过程中有一段属于自己的栈帧结构,主要用...

  • iOS开发--探究iOS线程调用栈及符号化

    探究iOS线程调用栈及符号化 概念 调用栈,也称为执行栈、控制栈、运行时栈与机器栈,是计算机科学中存储运行子程序的...

  • 在程序中访问C++调用栈

    本文翻译自 https://eli.thegreenplace.net/2015/programmatic-acc...

  • golang的值类型和引用类型

    队列:先进先出栈:先进后出,在程序调用的时候从栈空间去分配堆:在程序调用的时候从系统的内存区分配 1)值类型:基本...

  • C 与汇编程序的约定调用

    1.说明 函数调用时通过栈帧来实现的,栈帧是指为一个单独的函数调用单独分配的那部分栈空间。当运行中的程序调用另一个...

  • 2.3收发数据

    2.3收发数据 将HTTP消息交给协议栈 应用程序调用socket库中的write将发送的数据交给协议栈协议栈收到...

  • 小程序返回上一级时的数据传递

    调用小程序的API: getCurrentPages(),来获取当前页面路由栈的信息,这个路由栈中按照页面的路由顺...

  • 微信小程序:wx.navigateBack页面返回传参

    通过调用小程序的API: getCurrentPages(),来获取当前页面路由栈的信息,这个路由栈中按照页面的路...

网友评论

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

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