1.简介
在计算机科学中,函数调用栈是用来存储计算机程序中当前被调用函数的相关信息的一种数据结构,该数据结构以栈的形式被组织的。具体相关信息可以参考wiki上的介绍。
2.函数说明
这篇博客主要讲述如何使用API输出函数调用栈。
#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);
#include <cxxabi.h>
char* __cxa_demangle(const char* __mangled_name, char* __output_buffer, size_t* __length, int* __status);
#include <dlfcn.h>
int dladdr(void *addr, Dl_info *info);
int backtrace(void **buffer, int size)
该函数回溯程序的函数调用,并将值存储在buffer中,buffer中的每一个元素都是相应栈帧的返回地址, size指定buffer最多能够存储元素的个数。
如果回溯的结果大于size,那么buffer将会存储最近调用的size个栈帧;
否则,存储全部栈帧。
在获得这些地址之后,我们需要把它们转换成可读的形式,调用如下两个函数:
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
第一个函数将地址转换成可读的字符串,并将字符串写入到有malloc分配的内存空间中,并返回给调用函数。
第二个函数和第一个功能一样,不过将字符串写入由fd指定的文件中。
因此在调用第一个函数时,需要显示的释放(调用free)其返回值指向的内存。
返回值:
backtrace
返回buffer中地址的个数。
backtrace_symbols
如果成功返回一个由malloc分配的char*指针;否则返回NULL;
对于C程序来说,编译器不会对程序的名字进行修改;
但是C++存在mangle,所以我么还需要调用abi的函数进行demangle操作。
函数声明如下:
char* __cxa_demangle(const char* __mangled_name, char* __output_buffer, size_t* __length, int* __status);
其中,
__mangled_name是被mangle之后名字的字符,
__output_buffer是输出的结果,
__length是__output_buffer的大小,
__status指示该函数执行的结果。
3.代码实现
/**
* date: 2016.1.17
* author: zhouxiangxiang
*/
#include <execinfo.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <cxxabi.h>
#ifndef __DUMPSTACK_ZH__
#define __DUMPSTACK_ZH__
#define BACKTRACE_SIZE 100
/**
* gcc/clang
*/
char* dumpCxxFun(char* pname, char* buffer, size_t* len, int* status){
if ( ( == strcmp(pname, "main")) ||
( == strcmp(pname, "__libc_start_main")) ) {
return pname;
}
char* ret = abi::__cxa_demangle(pname, buffer, len, status);
if ( == *status) {
return ret;
}
else if (- == *status) {
printf("memory allocation failure:");
return NULL;
}
else if (- == *status) {
printf("invalid mangled name:");
return NULL;
}
else if (- == *status) {
printf("invalid arguments: ");
return NULL;
}
}
void parseFuncName(char* str) {
char buffer[BACKTRACE_SIZE];
size_t len = BACKTRACE_SIZE;
int status = ;
char *ret = NULL;
char* ptr_begin = NULL;
char* ptr_offset = NULL;
char* ptr_offend = NULL;
for (char* ptr = str; *ptr; ++ptr) {
if ('(' == *ptr) {
ptr_begin = ptr;
}
else if ('+' == *ptr) {
ptr_offset = ptr;
}
else if (')' == *ptr && ptr_begin < ptr_offset) {
ptr_offend = ptr;
}
}
if (ptr_begin &&
ptr_offset &&
ptr_offend &&
ptr_begin < ptr_offset) {
*ptr_begin++ = '\0';
*ptr_offset++ = '\0';
*ptr_offend++ = '\0';
ret = dumpCxxFun(ptr_begin, buffer, &len, &status);
if (NULL != ret) {
printf("\t%s : %s \n", ret, ptr_offset); // name, address
}
else {
printf("\t%s : %s \n", ptr_begin, ptr_offset); // name, address
}
}
else {
printf("\t%s \n", str); // name, address
}
}
void dumpStack() {
printf("[backtrace] \n");
int i = ;
int btNum = ;
void *btArray[BACKTRACE_SIZE];
char** str = NULL;
btNum = backtrace(btArray, BACKTRACE_SIZE);
str = backtrace_symbols(btArray, BACKTRACE_SIZE);
if (NULL == str) {
perror("backtrace_symbols fialed");
exit(EXIT_FAILURE);
}
for (i = ; i < btNum; ++i) {
#ifdef __cplusplus
parseFuncName(str[i]); // cxx
#else
printf("%s \n", str[i]);
#endif
}
free(str);
}
#endif //
4. 实例
#include "./dumpStack.h"
/**
* gcc/clang
*/
void fun(int n) {
if (n > ) {
fun(n - );
}
else {
dumpStack();
}
}
int main(int argc, char **argv) {
if (argc != ) {
perror("usage: command num");
exit(EXIT_SUCCESS);
}
fun(atoi(argv[]));
return ;
}
编译:
g++ b.cpp -rdynamic
执行:
./a.out 2