C程序运行时,经常会碰到”segmentfault”错误。这是由于程序中非法访问内存导致的。当操作系统的内存保护机制发现进程访问了非法内存的时候会向此进程发送一个SIGSEGV信号,导致进程直接退出,并在shell中提示segment fault。
因此,可以通过设置SIGSEGV信号处理函数,在处理函数中调用backtrace系列函数得到异常时的函数调用栈信息。
一:backtrace
backtrace系列函数的原型如下:
#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函数通过指针数组buffer返回调用程序的回溯信息,也就是所谓的函数调用栈。buffer数组中的元素是void*类型,也就是栈中保存的返回地址。
size参数指定buffer中可以保存的地址的最大个数。如果实际的回溯信息大于size,则只返回最近的size个地址。
backtrace函数返回buffer中保存的地址个数,返回值不会大于size。如果返回值小于size,则说明所有的回溯信息都已经返回了,如果等于size,则有可能被截断了。
backtrace函数在buffer数组中返回的都是一些虚拟地址,不适于分析。backtrace_symbols函数可以将backtrace返回的buffer中的地址,根据符号表中的信息,转换为字符串(函数名+偏移地址)。size参数指明了buffer中的地址个数。
backtrace_symbols返回字符串数组的首地址,该字符串是在backtrace_symbols中通过malloc分配的,因此,调用者必须使用free释放内存。如果发生了错误,则backtrace_symbols返回NULL。
backtrace_symbols_fd类似于backtrace_symbols,只不过它是把字符串信息写到文件描述符fd所表示的文件中。backtrace_symbols_fd不会调用malloc函数。
注意,编译器的优化策略,可能导致得到的回溯信息不准确。而且,对于GUN编译器而言,必须使用-rdynamic链接选项,才能正确解析出符号名。
二:示例
#include <signal.h>
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#define BTSIZE 100
static void sig_handler(int sig, siginfo_t *info, void *secret)
{
ucontext_t *uc = (ucontext_t*) secret;
void *buffer[BTSIZE];
char **strings;
int nptrs = 0;
printf("in sig_handler\n");
pri