解析动态库libtest.so和可执行文件m:
#include
void *array[32] = {0};
size_t size;
char **strings;
size = backtrace (array, 32);
strings = backtrace_symbols (array, size);
for (int i = 1; i < size -2; i++)
{
printf("%s ",strings[i]);
}
其实很简单,先得到原栈信息,然后
addr2line -Cif -e [可执行文件或者.so文件名] [地址,如果是.so文件要计算相对地址]
就可以啦.
下面是一个例子,我开了.h里面的调试开关的.
先看最下面的,对 ./m(main+0x107) [0x400d27]的解析就是直接用的命令addr2line -Cif -e ./m 0x400d27
然后看动态库的解析,./libtest.so(_Z7fun_funv+0x9) [0x7f85cf46a615] => addr2line -Cif -e ./libtest.so 0x615
0x615 = 0x7f85cf46a615 - 0x7f85cf46a000,0x7f85cf46a000是怎么知道的?
用/proc/self_pid/maps命令得到,图片里面上面部分的打印都是这个命令的输出.带 r-xp项的.
debug.c
#include
#include
#include
#include
#include
#include
#include
#include "debug.h"
/*内部结构体*/
typedef struct {
char not_care[4][32];
char library_path[128];
char *offset_start;
char *offset_end;
}library_maps_t;
typedef struct{
char exe[128];
char* offset;
}bt_t;
static int max_lib = 0;
static library_maps_t lib[MAX_LIB_NUM];
/***********************************************************
ret = NULL || len = 0 : 直接printf打印结果
ret != NULL && len > 0 : 结果保存到 char ret[len]
************************************************************/
static void output_addrline(bt_t *bt,char ret[],size_t len)
{
char cmd[256] = {0};
char line[2][256] = {0};
char addrline[32] = {0};
int idx = 0;
int i = 0;
FILE* file;
char* offset = bt->offset;
if(bt==NULL || bt->exe == NULL)
{
printf("[%s][%d]Error bt:%p,exe:%p\n",__FUNCTION__,__LINE__,bt,bt->exe);
return;
}
if(offset<lib[idx].offset_start || offset>lib[idx].offset_end)
{
idx++;
while(idx < max_lib && (offset<lib[idx].offset_start || offset>lib[idx].offset_end))
{
idx++;
}
if(idx == max_lib)
{
return;
}
offset = (char*)(offset - lib[idx].offset_start);
}
snprintf(cmd, sizeof(cmd), "addr2line -Cif -e %s %p ", bt->exe, offset);
#ifdef BT_MOD_DEBUG_ON
printf("{cmd:%s}",cmd);
#endif
file = popen(cmd, "r");
if(file == NULL) return;
while(i < 2 && NULL != fgets(line[i], 256, file)) i++;
pclose(file);
line[0][strlen(line[0])-1] = 0;
if(ret==NULL || len == 0)
{
#ifdef __cplusplus
printf("%s %s", line[0], line[1]);
#else
printf("%s() %s", line[0], line[1]);
#endif
}
else
{
#ifdef __cplusplus
snprintf(ret, len, "%s %s", line[0], line[1]);
#else
snprintf(ret, len, "%s() %s", line[0], line[1]);
#endif
}
return ;
}
/***********************************************************
return:
0:success
-1:false
************************************************************/
int dump_library_maps()
{
char cmd[64] = {0};
char maps_line[512];
char *last_library_path = NULL;
int num_exe = 0;
int maps_column_num;
library_maps_t* temp = &lib[0];
FILE* fd_maps;
memset(&lib, 0, sizeof(lib));
memset(maps_line,0,sizeof(maps_line));
/* 1 get maps info to file*/
snprintf(cmd, sizeof(cmd), "/proc/%d/maps", getpid());
fd_maps=fopen(cmd,"r");
if(fd_maps == NULL)
{
printf("ERROR\n");
return -1;
}
/* 2 save maps info to lib*/
while(NULL!=fgets(maps_line,sizeof(maps_line),fd_maps))
{
maps_column_num = sscanf(maps_line,"%p-%p\t%s\t%s\t%s\t%s\t%s"
,&temp->offset_start
,&temp->offset_end
,temp->not_care[0]
,temp->not_care[1]
,temp->not_care[2]
,temp->not_care[3]
,temp->library_path);
#ifdef BT_MOD_DEBUG_ON
printf("%p-%p\t%s\t%s\t%s\t%s\t%s\n",temp->offset_start,temp->offset_end,temp->not_care[0],
temp->not_care[1],temp->not_care[2],temp->not_care[3],temp->library_path);
#endif
if(maps_column_num == 7 &&( (num_exe == 0 && 0==strcmp("r-xp",temp->not_care[0]))
|| strcmp(temp->library_path,temp[-1].library_path)) )
{
if(num_exe == MAX_LIB_NUM)
{
printf("Error MAX_LIB_NUM is %d!!!\n",MAX_LIB_NUM);
break;
}
temp++;
num_exe++;
}
else
{ /*so文件 是记录[min_offset, r-xp:offset_end]*/
if( 0==strcmp("r-xp",temp->not_care[0])
&& 0==strcmp(temp->library_path,temp[-1].library_path))
{
temp[-1].offset_end = temp->offset_end;
}
memset(temp, 0, sizeof(*temp));
}
memset(maps_line, 0, sizeof(maps_line));
}
fclose(fd_maps);
max_lib = num_exe;
printf("[%s][%d]debug_backtrace_init success,num_exe:%d\n",__FUNCTION__,__LINE__,num_exe);
#ifdef BT_MOD_DEBUG_ON
num_exe = 0;
printf("\n============= so lib info ===========\n");
while(num_exe < max_lib)
{
printf("%p-%p\t%s\t%s\n",lib[num_exe].offset_start,lib[num_exe].offset_end,temp->not_care[0],lib[num_exe].library_path);
num_exe++;
}
printf("============= end ===========\n");
#endif
return 0;
}
/***********************************************************
return:
0:success
-1:false
************************************************************/
void dump_backtrace(void)
{
void *array[32] = {0};
size_t size,i;
char **strings;
int num;
bt_t bt;
size = backtrace (array, 32);
strings = backtrace_symbols (array, size);
if (NULL == strings)
{
printf("[%s][%d]Error at backtrace_symbols()\n",__FUNCTION__,__LINE__);
return ;
}
printf("\n##################backtrace###################\n");
for (i = 1; i < size -2; i++)
{
printf("%s ",strings[i]);
num = sscanf(strings[i],"%[^(]%*[^ ] [%p]",bt.exe,&bt.offset);
#ifdef BT_MOD_DEBUG_ON
printf("{%d,%s,%p}\n",num,bt.exe,bt.offset);
#endif
output_addrline(&bt, NULL, 0);
}
printf("################################################\n");
free(strings);
}
static void signal_exit(int dunno)
{
const char* signal_str = "";
char dunno_str[10] = {0};
sprintf(dunno_str, "%d", dunno);
switch (dunno)
{
case 1:
signal_str = "SIGHUP(1)";
break;
case 2:
signal_str = "SIGINT(2:CTRL_C)";
break;
case 3:
signal_str = "SIGQUIT(3)";
break;
case 6:
{
signal_str = "SIGABRT(6)";
dump_backtrace();
}
break;
case 9:
signal_str = "SIGKILL(9)";
break;
case 15:
signal_str = "SIGTERM(15 KILL)";
break;
case 11:
{
signal_str = "SIGSEGV(11)";
dump_backtrace();
}
break;
default:
signal_str = "OTHER";
break;
}
printf("Error: signal_str:%s\n",signal_str);
exit(0);
}
void debug_backtrace_init()
{
signal(SIGHUP, signal_exit);
signal(SIGINT, signal_exit);
signal(SIGQUIT, signal_exit);
signal(SIGABRT, signal_exit);
signal(SIGKILL, signal_exit);
signal(SIGTERM, signal_exit);
signal(SIGSEGV, signal_exit);
dump_library_maps();
}
debug.h
/**********************************************************
说明:
1.编译时用了 -O1/2/3优化的,backtrace信息将不准确,建议用-O0
2.本模块基于backtrace,backtrace_symbols,line2addr实现
3.动态库编译加 -g -rdynamic:
gcc test.c -g -rdynamic -fPIC -shared -o libtest.so
或者先.o再.so
g++ test.c -g -rdynamic -fPIC -shared -o libtest.o
g++ libtest.o -shared -funwind-tables -rdynamic -o libtest.so
4.执行文件编译需要加 -g
5.为了兼容c/c++,打印方式使用printf
使用:
1.基于线程,模块init :debug_backtrace_init();工作于当前线程
可以修改该函数(有些信号是测试的时候用的)
2.指针校验:ASSERT(p) 当p为NULL时,调用dump_backtrace(),打印函数调用栈
3.dump_backtrace() 打印函数调用栈
4.检测段错误,数组越界等问题,发生时调自动用dump_backtrace()
***********************************************************/
#ifndef _DEBUG_BACKTRACE_
#define _DEBUG_BACKTRACE_
#ifdef __cplusplus
extern "C" {
#endif
/*本模块的调试开关*/
/*1 放开下面注释*/
//#define BT_MOD_DEBUG_ON
/*2 或者,编译时候加 -D BT_MOD_DEBUG_ON*/
#define MAX_LIB_NUM (20)
#define ASSERT(p) do{if(p==0) {dump_backtrace();}}while(0)
/*模块init*/
extern void debug_backtrace_init();
extern void dump_backtrace(void);
#ifdef __cplusplus
}
#endif
#endif
这里运行的环境为虚拟机,如果是开发版,命令可能需要变一下.
arm-xxxxxx-gcc
arm-xxxxxxxxx-addr2line
见头文件
/**********************************************************
说明:
1.编译时用了 -O1/2/3优化的,backtrace信息将不准确,建议用-O0
2.本模块基于backtrace,backtrace_symbols,line2addr实现
3.动态库编译加 -g -rdynamic:
gcc test.c -g -rdynamic -fPIC -shared -o libtest.so
或者先.o再.so
g++ test.c -g -rdynamic -fPIC -shared -o libtest.o
g++ libtest.o -shared -funwind-tables -rdynamic -o libtest.so
4.执行文件编译需要加 -g
5.为了兼容c/c++,打印方式使用printf
使用:
1.基于线程,模块init :debug_backtrace_init();工作于当前线程
可以修改该函数(有些信号是测试的时候用的)
2.指针校验:ASSERT§ 当p为NULL时,调用dump_backtrace(),打印函数调用栈
3.dump_backtrace() 打印函数调用栈
4.检测段错误,数组越界等问题,发生时调自动用dump_backtrace()
***********************************************************/
核心命令:
地址信息=>函数名+代码行数
addr2line -Cif -e a.out 0x1234
maps表信息,包括动态库内存表
/proc/self/maps