在UNIX系统中实现堆栈跟踪

在程序运行的过程中,如果出现异常,通常会发出一个信号进入信号处理函数中处理。有些故障过于严重到无法实现程序的自恢复。这个时候,程序只能无奈的输出一些错误信息。当然这些错误信息对程序的调试也是非常有帮助的,我们在Java中如果出现异常的话,一般都会打印出堆栈跟踪的信息。

当然,除了打印堆栈信息外,也能在程序的某些点设置一些调试信息方便输出程序出错的行号,函数名和文件名。但是这种方式的功能毕竟是有限的,很多异常出现的位置可能并没有设置这样的调试语句。这样,还是堆栈信息比较重要。因为这是运行时输出的信息,而输出行号,文件名,函数名的方式只能在编译时确定。

堆栈跟踪主要和三个函数相关,分别为backtrace, backtrace_symbols,以及backtrace_symbols_fd.关于这三个函数的信息如下:

#include <execinfo.h>

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函数返回backtrace函数被调用时的堆栈信息。这些信息存放在缓冲区buffer中。backtrace函数有两个参数:

buffer:用于存放堆栈信息的缓冲区

size:用于指示buffer的大小。

要充分考虑到buffer的大小,因为如果函数调用的层次过深可能导致buffer空间不够,这样就只能保存一部分堆栈信息,靠近main函数这端的信息会因为空间不够而被裁掉。buffer是一个指向二维数组的指针,类型为void. buffer中所保存的是一系列堆栈帧的返回地址。traceback函数将返回堆栈中跟踪到的函数的个数。

将trace函数返回的buffer和返回的函数个数分别作为backtrace_symbols就可以解析出这些跟踪到的函数的符号名称,确切的说,backtrace_symbols函数将针对buffer中每一个函数返回地址进行解析,解析后的格式为:./程序名(<函数名+>函数的在程序中的十六进制格式偏移地址) [函数实际的返回十六进制格式的地址],解析后的结果保存为二维字符数组,返回值为二维数组的起始地址。

设返回的二维数组为strings,由于在backtrace_symbols内部调用了malloc开辟空间,所以需要用户来释放空间。不过用户只需要释放strings所指向的字符串指针数组即可,对于每个字符串不用释放,也不应该释放,内部会自动维护。

函数traceback_symbols_fd将输出堆栈信息到fd所说明的文件中。这样有一个好处就是不用调用malloc函数了,避免了内存分配失败的可能性。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wywfzy.html