很久之前写的测试代码,研究利用backtrace配合addr2line打印用户态进程堆栈信息(•̀⌄•́)
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define EXCMD "addr2line -p -f -e ./test "
#define TESTINT 18888
#define _PRT(x) #x
#define PRT(x) _PRT(x)
void myfunc3(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
FILE *fp;
printf("### " PRT(TESTINT) " ###\n");
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
void *leftpos = NULL, *rightpos = NULL;
char cmd[512] = EXCMD, line[1024] = {0};
for (j = 0; j < nptrs; j++){
if (NULL == (void *)memmem(strings[j], strlen(strings[j]), "libc.so.6", strlen("libc.so.6"))){
leftpos = memchr(strings[j], '[', strlen(strings[j]));
rightpos = memchr(leftpos, ']', strlen(strings[j]) - ((char *)leftpos - strings[j]));
strncat(cmd, (char *)(leftpos+1), (char *)rightpos-(char *)leftpos-1);
// snprintf(cmd+strlen(cmd), rightpos-leftpos, leftpos+1);
fp = popen(cmd, "r");
while (fgets(line, 1024, fp) != NULL){
printf("level: %d, %s", j, line);
}
pclose(fp);
strcpy(cmd, EXCMD);
}
else{
printf("level: %d, %s\n", j, strings[j]);
}
}
free(strings);
#undef SIZE
return;
}
static void myfunc2(void)
{
myfunc3();
}
void myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "%s num-calls\n", argv[0]);
exit(EXIT_FAILURE);
}
myfunc(atoi(argv[1]));
// exit(EXIT_SUCCESS);
return 0;
}
addr2line可以将函数地址转换成易读的文件名、函数名和源代码行数等信息便于定位,EXCMD中定义的test即测试程序的名称,更好的方法是直接解析/proc/self/comm来获取当前进程的可执行文件名称。函数功能也很简单,堆栈打印实现在myfunc3函数中,就不做展开了。