| /* |
| * 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 |
| } |
| |