1.应用场景
ARM开发过程中经常进程运行着出现段错误,这时候单纯靠加日志打log效率太低。使用gdb的话,由于APP进程太多,生成的core的文件特别大,而且gdb在arm板子也不好单步调试,不太友好还是pass掉。 目前使用段错误捕捉SIGSEGV信号,通过backtrace和backtrace_symbols函数进行堆栈信息定位,再使用addr2line工具将指令的地址和可执行映像转换成文件名、函数名和源代码行数。
root@chenwr-pc:/home/workspace/chenwr/study/test# ./out
./out
[2020/07/21 09:38:15.249517 INFO]: [libcwr.c:0193]:
=========>>>catch signal 11
fd = popen(cmd, "r");
if (NULL == fd) {
perror("popen error\n");
break;
}
while (NULL != fgets(result, len, fd)) {
offset = strlen(result);
result += offset;
}
rc = pclose(fd);
if (-1 == rc) {
perror("pclose error\n");
break;
}
if (!WIFEXITED(rc)) {
perror("Run command failed\n");
break;
} else {
ret = WEXITSTATUS(rc);
}
} while (0);
if (NULL == fd || -1 == rc) {
strncpy(result, strerror(errno), len);
//FK_TRACE_INFO("errno = %s\n", strerror(errno));
}
fd = NULL;
return ret;
}
/*******************************************************************
** 函数名: get_curprocess_name
** 函数描述: 获取当前进程名字
** 参数: [out] process_name: 进程名
** 返回: 成功返回0,失败返回-1。
********************************************************************/
INT32S get_curprocess_name(INT8S *process_name)
{
FILE *fd = NULL;
INT8S proc_pid_path[100] = {0};
INT8S buf[100] = {0};
//获取进程名字
snprintf(proc_pid_path, sizeof(proc_pid_path), "/proc/%d/status", getpid());
do {
fd = fopen(proc_pid_path, "r");
if (NULL == fd) {
FK_TRACE_ERROR("fopen %s fail\n", proc_pid_path);
break;
}
if (NULL == fgets(buf, sizeof(buf)-1, fd)) {
fclose(fd);
FK_TRACE_ERROR("get current process name fail\n");
break;
}
sscanf(buf, "%*s %s", process_name);
fclose(fd);
return EXIT_OK;
} while(0);
return EXIT_ERROR;
}
/*******************************************************************
** 函数名: print_coredumped_msg
** 函数描述: 输出段错误详细信息,具体到函数行数。
** 参数: [in] symbols: 符号信息 (backtrace_symbols的返回值);[in] catch_num: 捕获信息元素个数(backtrace的返回值)
** 返回: 成功返回0,失败返回-1。
** 说明:
** int backtrace(void **buffer, int size);
** 该函数获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组,参数size用来指定buffer中可以保存多少个void*元素。
** 函数的返回值是实际返回的void*元素个数。buffer中的void*元素实际是从堆栈中获取的返回地址。
**
** char **backtrace_symbols(void *const *buffer, int size);
** 该函数将backtrace函数获取的信息转化为一个字符串数组,参数buffer是backtrace获取的堆栈指针,size是backtrace返回值。
** 函数返回值是一个指向字符串数组的指针,它包含char*元素个数为size。
** 每个字符串包含了一个相对于buffer中对应元素的可打印信息,包括函数名、函数偏移地址和实际返回地址。
********************************************************************/
INT32S print_coredumped_msg(INT8S **symbols, INT32S catch_num)
{
INT8S tools[100] = "addr2line";
INT8S process_name[100] = {0};
INT8S cmd[100] = {0};
INT8S buf[100] = {0};
INT8S result[MAXBUF_SIZE] = {0};
INT8S *front_position = NULL;
INT8S *back_position = NULL;
INT8S *tmp = NULL;
INT32S ret, i;
//判断addr2line工具是否存在
ret = run_shell_cmd(tools, result, MAXBUF_SIZE);
if (127 == ret) {
FK_TRACE_ERROR("%s is not exist\n", tools);
return EXIT_ERROR;
}
//获取当前进程名
ret = get_curprocess_name(process_name);
if (ret) {
return EXIT_ERROR;
}
//打印段错误详细行数
for (i = 0; i
return EXIT_ERROR;
}
//结果包含??全部过滤掉
tmp = strstr(result, "??");
if (!tmp) {
FK_TRACE_INFO("%s\n", result);
}
}
front_position = NULL;
back_position = NULL;
tmp = NULL;
return EXIT_OK;
}
/*******************************************************************
** 函数名: catch_core_dump
** 函数描述: 捕捉段错误信息
** 参数: [in] signo: 信号
** 返回: 无
********************************************************************/
void catch_core_dump(INT32S signo)
{
INT32S catch_num;
void *buf[MAXBUF_SIZE] = {0};
INT8S **symbols = NULL;
FK_TRACE_INFO("\n=========>>>catch signal %d |