blob: 23e84319f80a4dc6fe924c683b3961df6471d170 [file] [log] [blame]
/*
* mbtk_debug.c
*
* Generate application exception information.
*
*/
/******************************************************************************
EDIT HISTORY FOR FILE
WHEN WHO WHAT,WHERE,WHY
-------- -------- -------------------------------------------------------
2024/6/5 LiuBin Initial version
******************************************************************************/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <ucontext.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>
#include <execinfo.h>
#include <cutils/properties.h>
#include "mbtk_type.h"
#include "mbtk_log.h"
/* 纯C环境下,不定义宏NO_CPP_DEMANGLE */
#if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE))
#define NO_CPP_DEMANGLE
#endif
#ifndef NO_CPP_DEMANGLE
#include <cxxabi.h>
#ifdef __cplusplus
using __cxxabiv1::__cxa_demangle;
#endif
#endif
#if (defined __x86_64__)
#define REGFORMAT "%016lx"
#elif (defined __i386__)
#define REGFORMAT "%08x"
#elif (defined __arm__)
#define REGFORMAT "%lx"
#endif
#define MBTK_PROC_DUMP_DIR "persist.mbtk.dump_dir"
#define MBTK_PROC_DUMP_FILE_DEF "/etc/mbtk/mbtk_dump.log"
static char proc_name[100];
static char proc_dump_name[100 * 2];
static int proc_dump_fd = -1;
#ifdef HAS_ULSLIB
#include <uls/logger.h>
#define sigsegv_outp(x) sigsegv_outp(, gx)
#else
//#define sigsegv_outp(x, ...) fprintf(stderr, x"\n", ##__VA_ARGS__)
void sigsegv_outp(const char* format, ...)
{
if(proc_dump_fd > 0) {
char buf[1024] = {0};
va_list ap;
int length = 0;
va_start(ap, format);
length = vsnprintf(buf, sizeof(buf), format, ap);
if (length < 0 || 0 == length) {
return;
}
char *tmp = buf + length - 1;
while(tmp >= buf && (*tmp == '\r' || *tmp == '\n')) {
*tmp-- = '\0';
}
tmp++;
*tmp = '\n';
if(write(proc_dump_fd, buf, strlen(buf)) != strlen(buf)) {
// Do nothing.
}
va_end(ap);
}
}
#endif
static void print_reg(ucontext_t *uc)
{
#if (defined __x86_64__) || (defined __i386__)
int i;
for (i = 0; i < NGREG; i++)
{
sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]);
}
#elif (defined __arm__)
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9);
sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10);
sigsegv_outp("FP = 0x"REGFORMAT, uc->uc_mcontext.arm_fp);
sigsegv_outp("IP = 0x"REGFORMAT, uc->uc_mcontext.arm_ip);
sigsegv_outp("SP = 0x"REGFORMAT, uc->uc_mcontext.arm_sp);
sigsegv_outp("LR = 0x"REGFORMAT, uc->uc_mcontext.arm_lr);
sigsegv_outp("PC = 0x"REGFORMAT, uc->uc_mcontext.arm_pc);
sigsegv_outp("CPSR = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr);
sigsegv_outp("Fault Address = 0x"REGFORMAT, uc->uc_mcontext.fault_address);
sigsegv_outp("Trap no = 0x"REGFORMAT, uc->uc_mcontext.trap_no);
sigsegv_outp("Err Code = 0x"REGFORMAT, uc->uc_mcontext.error_code);
sigsegv_outp("Old Mask = 0x"REGFORMAT, uc->uc_mcontext.oldmask);
#endif
}
static void print_call_link(ucontext_t *uc)
{
int i = 0;
void **frame_pointer = (void **)NULL;
void *return_address = NULL;
Dl_info dl_info = { 0 };
#if (defined __i386__)
frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP];
return_address = (void *)uc->uc_mcontext.gregs[REG_EIP];
#elif (defined __x86_64__)
frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP];
return_address = (void *)uc->uc_mcontext.gregs[REG_RIP];
#elif (defined __arm__)
/* sigcontext_t on ARM:
unsigned long trap_no;
unsigned long error_code;
unsigned long oldmask;
unsigned long arm_r0;
...
unsigned long arm_r10;
unsigned long arm_fp;
unsigned long arm_ip;
unsigned long arm_sp;
unsigned long arm_lr;
unsigned long arm_pc;
unsigned long arm_cpsr;
unsigned long fault_address;
*/
frame_pointer = (void **)uc->uc_mcontext.arm_fp;
return_address = (void *)uc->uc_mcontext.arm_pc;
#endif
sigsegv_outp("\nStack trace:");
while (frame_pointer && return_address)
{
if (!dladdr(return_address, &dl_info)) break;
const char *sname = dl_info.dli_sname;
#ifndef NO_CPP_DEMANGLE
int status;
char *tmp = __cxa_demangle(sname, NULL, 0, &status);
if (status == 0 && tmp)
{
sname = tmp;
}
#endif
/* No: return address <sym-name + offset> (filename) */
sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname,
(unsigned long)return_address - (unsigned long)dl_info.dli_saddr,
dl_info.dli_fname);
#ifndef NO_CPP_DEMANGLE
if (tmp) free(tmp);
#endif
if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main"))
{
break;
}
#if (defined __x86_64__) || (defined __i386__)
return_address = frame_pointer[1];
frame_pointer = frame_pointer[0];
#elif (defined __arm__)
return_address = frame_pointer[-1];
frame_pointer = (void **)frame_pointer[-3];
#endif
}
sigsegv_outp("Stack trace end.");
}
static void proc_maps_print() {
char file[64] = {0x00};
sprintf(file,"/proc/%d/maps", getpid());
FILE *fptr = fopen(file, "r");
char line[1024];
if(fptr)
{
memset(line, 0, sizeof(line));
while(fgets(line, sizeof(line), fptr)) {
// printf("Line : %s", line);
if(strstr(line, "libmbtk_") || strstr(line, "liblynq_") || strstr(line, "libql_")) {
sigsegv_outp("%s", line);
}
memset(line, 0, sizeof(line));
}
fclose(fptr);
}
}
static void sigsegv_handler(int signo, siginfo_t *info, void *context)
{
printf("sigsegv_handler - %d\n", signo);
if (context)
{
struct timeval log_time;
char tmp[50] = {0};
gettimeofday(&log_time, NULL);
struct tm* tm_t = localtime(&(log_time.tv_sec));
strftime(tmp, 50, "%F %T", tm_t);
snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ".%d", (int)(log_time.tv_usec / 1000));
sigsegv_outp("----------------------------%s----------------------------", tmp);
proc_maps_print();
ucontext_t *uc = (ucontext_t *)context;
char proc[100] = {0};
int fd = open("/proc/self/cmdline", O_RDONLY);
if(fd > 0)
{
if(read(fd, proc, sizeof(proc)) > 0) {
sigsegv_outp("Segmentation Fault:%s", proc);
} else {
sigsegv_outp("Segmentation Fault:%s", "Unknown");
}
close(fd);
} else {
sigsegv_outp("Segmentation Fault:%s", "Unknown");
}
sigsegv_outp("info.si_signo = %d", signo);
sigsegv_outp("info.si_errno = %d", info->si_errno);
sigsegv_outp("info.si_code = %d (%s)", info->si_code,
(info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR");
sigsegv_outp("info.si_addr = 0x%x(%p)\n", info->si_addr, info->si_addr);
print_reg(uc);
print_call_link(uc);
signal(signo, SIG_DFL);
raise(signo);
printf("Segmentation Fault, refer to the log file:%s\n", proc_dump_name);
}
exit(0);
}
#if 1
#define BACKTRACE_SIZE 32
void sigsegv_handler_with_thread(int signo,siginfo_t *info, void *context) {
struct timeval log_time;
char tmp[50] = {0};
gettimeofday(&log_time, NULL);
struct tm* tm_t = localtime(&(log_time.tv_sec));
strftime(tmp, 50, "%F %T", tm_t);
snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ".%d", (int)(log_time.tv_usec / 1000));
sigsegv_outp("----------------------------%s----------------------------", tmp);
proc_maps_print();
char proc[100] = {0};
int fd = open("/proc/self/cmdline", O_RDONLY);
if(fd > 0)
{
if(read(fd, proc, sizeof(proc)) > 0) {
sigsegv_outp("Segmentation Fault:%s", proc);
} else {
sigsegv_outp("Segmentation Fault:%s", "Unknown");
}
close(fd);
} else {
sigsegv_outp("Segmentation Fault:%s", "Unknown");
}
sigsegv_outp("info.si_signo = %d", signo);
int j, nptrs;
void *buffer[BACKTRACE_SIZE];
char **strings;
nptrs = backtrace(buffer, BACKTRACE_SIZE);
//printf("backtrace() returned %d addresses\n", nptrs);
strings = backtrace_symbols(buffer, nptrs);
if(strings == NULL){
//perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
sigsegv_outp("\nStack trace:");
if(nptrs > 0) {
for (j = 0; j < nptrs; j++) {
sigsegv_outp("%02d: %s", j, strings[j]);
}
}
free(strings);
sigsegv_outp("Stack trace end.");
signal(signo, SIG_DFL);
raise(signo);
printf("Segmentation Fault, refer to the log file:%s\n", proc_dump_name);
exit(0);
}
#endif
#define SETSIG(sa, sig, fun, flags) \
do { \
sa.sa_sigaction = fun; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while(0)
// arm-openwrt-linux-addr2line -e out/bin/mbtk_gnssd 0x12ca8
void mbtk_debug_open(const char *log_file, bool thread_support)
{
struct sigaction sa;
#if 1
if(thread_support) {
SETSIG(sa, SIGSEGV, sigsegv_handler_with_thread, 0);
SETSIG(sa, SIGABRT, sigsegv_handler_with_thread, 0);
} else {
SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO);
SETSIG(sa, SIGABRT, sigsegv_handler, SA_SIGINFO);
}
#else
SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO);
#endif
memset(proc_name, 0, sizeof(proc_name));
memset(proc_dump_name, 0, sizeof(proc_dump_name));
int fd = open("/proc/self/cmdline", O_RDONLY);
if(fd > 0)
{
if(read(fd, proc_name, sizeof(proc_name)) <= 0) {
LOGE("Get PROC name fail.");
}
close(fd);
}
// Redirect stderr to log_file.
if(log_file) {
memcpy(proc_dump_name, log_file, strlen(log_file));
} else {
property_get(MBTK_PROC_DUMP_DIR, proc_dump_name, "");
if(strlen(proc_dump_name) > 0) {
snprintf(proc_dump_name + strlen(proc_dump_name),sizeof(proc_dump_name) - strlen(proc_dump_name), "/%s", proc_name);
} else {
memcpy(proc_dump_name, MBTK_PROC_DUMP_FILE_DEF, strlen(MBTK_PROC_DUMP_FILE_DEF));
}
}
#if 0
if(freopen(proc_dump_name, "a", stderr) == NULL) {
LOGE("reopen stderr to %s fail.[%d]", proc_dump_name, errno);
}
#else
proc_dump_fd = open(proc_dump_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
if(proc_dump_fd < 0) {
LOGE("Open(%s) fail:%d.", proc_dump_name, errno);
}
#endif
}