int backtrace (void **buffer, int size)
The backtrace function obtains a backtrace for the current thread, as a list of pointers, and places the information into buffer.
将当前线程的调用栈信息存放在buffer数组里;size为buffer数组的大小;如果实际的调用栈的大小(层数)小于size,那么返回实际的层数,否则返回size。
addr2line
Translates addresses into file names and line numbers.
将地址信息翻译成相应的源文件名和行号。
# define CURRENT_STACK_FRAME ({ char __csf; &__csf; })
//找到当前栈顶;这个宏仅用于下面的越界检查
# define INNER_THAN <
//栈是从高地址向低地址生长的。被调用者的栈地址小于调用者的栈地址
# define ADVANCE_STACK_FRAME(next) ((struct layout *) (next))
//layout结构定义于frame.h中
struct layout
{
void *next;
void *return_address;
};
//从定义可以看出next位于低地址,return_address位于高地址;这符合栈的约定
+---------------+
| 调用者的帧指针 |
+---------------+
| caller |
| 调用者 |
| |
+---------------+
| return address| <-- 调用者的返回地址
+---------------+
| next | <-- next为调用者的帧指针
+---------------+
| callee |
| 被调用者 |
| … |
# define FIRST_FRAME_POINTER __builtin_frame_address (0)
//一个gcc内建函数,返回寄存器ebp(帧指针)的值;传入参数0时,返回当前栈的帧指针
//见http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
int __backtrace (array, size)
void **array;
int size;
{
struct layout *current;
void *top_frame;
void *top_stack;
int cnt = 0;
top_frame = FIRST_FRAME_POINTER;
top_stack = CURRENT_STACK_FRAME;
/* We skip the call to this function, it makes no sense to record it. */
current = ((struct layout *) top_frame);
while (cnt < size)
{
if ((void *) current INNER_THAN top_stack
|| !((void *) current INNER_THAN __libc_stack_end))
/* This means the address is out of range. Note that for the
toplevel we see a frame pointer with value NULL which clearly is
out of range. */
break;
//边界检查
array[cnt++] = current->return_address;
//将调用者的返回地址放入array数组中
current = ADVANCE_STACK_FRAME (current->next);
//current的值是被调用者(callee)的帧指针所对应的内存地址;
//而这块内存中存放的(current->next)是调用者(caller)的帧指针所对应的内存地址
//向高地址方向移动帧指针
}
return cnt;
}
backtrace函数和addr2line工具
有些时候(比如debug或者抛异常的时候),你希望得到当前函数的调用栈。使用backtrace函数和addr2line程序可以达到这一目的。backtrace是GNU提供的C标准库中的一个函数;addr2line则是GNU提供的二进制工具集Binutils中的一个程序。
int backtrace (void **buffer, int size)
The backtrace function obtains a backtrace for the current thread, as a list of pointers, and places the information into buffer.
将当前线程的调用栈信息存放在buffer数组里;size为buffer数组的大小;如果实际的调用栈的大小(层数)小于size,那么返回实际的层数,否则返回size。
addr2line
Translates addresses into file names and line numbers.
将地址信息翻译成相应的源文件名和行号。
###backtrace的实现 backtrace函数的定义在glibc库中backtrace.c文件中,是一个非常小巧的函数。
###简单的应用 一个简单的示例程序,实现一个异常类,这个异常类被抛出时记录了当时的调用栈信息。
编译时打开调试选项(-g),addr2line程序需要可执行文件中的调试信息才能进行“翻译”。
有时候,程序需要拷贝到远程主机运行。含有调试信息的程序体积太大,可以通过下面的命令“剥离”调试信息,
TODO: Maybe need a better picture to illustrate…
如果你觉得这篇文章对你有用,可以微信扫一扫表示🙏 / If you find this post is useful to you, buy me 🍶 via Wechat