b.liu | bb59049 | 2024-06-13 16:42:08 +0800 | [diff] [blame] | 1 | /* |
| 2 | * mbtk_debug.c |
| 3 | * |
| 4 | * Generate application exception information. |
| 5 | * |
| 6 | */ |
| 7 | /****************************************************************************** |
| 8 | |
| 9 | EDIT HISTORY FOR FILE |
| 10 | |
| 11 | WHEN WHO WHAT,WHERE,WHY |
| 12 | -------- -------- ------------------------------------------------------- |
| 13 | 2024/6/5 LiuBin Initial version |
| 14 | |
| 15 | ******************************************************************************/ |
| 16 | #ifndef _GNU_SOURCE |
| 17 | #define _GNU_SOURCE |
| 18 | #endif |
| 19 | #include <stdio.h> |
| 20 | #include <dlfcn.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <signal.h> |
| 23 | #include <unistd.h> |
| 24 | #include <string.h> |
| 25 | #include <ucontext.h> |
| 26 | #include <fcntl.h> |
| 27 | #include <errno.h> |
| 28 | #include <time.h> |
| 29 | #include <sys/time.h> |
| 30 | #include <stdarg.h> |
| 31 | |
| 32 | #include "mbtk_type.h" |
| 33 | #include "mbtk_log.h" |
| 34 | |
| 35 | /* 纯C环境下,不定义宏NO_CPP_DEMANGLE */ |
| 36 | #if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE)) |
| 37 | #define NO_CPP_DEMANGLE |
| 38 | #endif |
| 39 | |
| 40 | #ifndef NO_CPP_DEMANGLE |
| 41 | #include <cxxabi.h> |
| 42 | #ifdef __cplusplus |
| 43 | using __cxxabiv1::__cxa_demangle; |
| 44 | #endif |
| 45 | #endif |
| 46 | |
| 47 | #if (defined __x86_64__) |
| 48 | #define REGFORMAT "%016lx" |
| 49 | #elif (defined __i386__) |
| 50 | #define REGFORMAT "%08x" |
| 51 | #elif (defined __arm__) |
| 52 | #define REGFORMAT "%lx" |
| 53 | #endif |
| 54 | |
| 55 | #define MBTK_PROC_DUMP_DIR "persist.mbtk.dump_dir" |
| 56 | #define MBTK_PROC_DUMP_FILE_DEF "/etc/mbtk/mbtk_dump.log" |
| 57 | |
| 58 | |
| 59 | static char proc_name[100]; |
| 60 | static char proc_dump_name[100]; |
| 61 | static int proc_dump_fd = -1; |
| 62 | |
| 63 | #ifdef HAS_ULSLIB |
| 64 | #include <uls/logger.h> |
| 65 | #define sigsegv_outp(x) sigsegv_outp(, gx) |
| 66 | #else |
| 67 | //#define sigsegv_outp(x, ...) fprintf(stderr, x"\n", ##__VA_ARGS__) |
| 68 | void sigsegv_outp(const char* format, ...) |
| 69 | { |
| 70 | if(proc_dump_fd > 0) { |
| 71 | char buf[1024] = {0}; |
| 72 | va_list ap; |
| 73 | int length = 0; |
| 74 | |
| 75 | va_start(ap, format); |
| 76 | length = vsnprintf(buf, sizeof(buf), format, ap); |
| 77 | if (length < 0 || 0 == length) { |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | char *tmp = buf + length - 1; |
| 82 | while(tmp >= buf && (*tmp == '\r' || *tmp == '\n')) { |
| 83 | *tmp-- = '\0'; |
| 84 | } |
| 85 | tmp++; |
| 86 | *tmp = '\n'; |
| 87 | |
| 88 | write(proc_dump_fd, buf, strlen(buf)); |
| 89 | |
| 90 | va_end(ap); |
| 91 | } |
| 92 | } |
| 93 | #endif |
| 94 | |
| 95 | static void print_reg(ucontext_t *uc) |
| 96 | { |
| 97 | #if (defined __x86_64__) || (defined __i386__) |
| 98 | int i; |
| 99 | for (i = 0; i < NGREG; i++) |
| 100 | { |
| 101 | sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]); |
| 102 | } |
| 103 | #elif (defined __arm__) |
| 104 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0); |
| 105 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1); |
| 106 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2); |
| 107 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3); |
| 108 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4); |
| 109 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5); |
| 110 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6); |
| 111 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7); |
| 112 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8); |
| 113 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9); |
| 114 | sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10); |
| 115 | sigsegv_outp("FP = 0x"REGFORMAT, uc->uc_mcontext.arm_fp); |
| 116 | sigsegv_outp("IP = 0x"REGFORMAT, uc->uc_mcontext.arm_ip); |
| 117 | sigsegv_outp("SP = 0x"REGFORMAT, uc->uc_mcontext.arm_sp); |
| 118 | sigsegv_outp("LR = 0x"REGFORMAT, uc->uc_mcontext.arm_lr); |
| 119 | sigsegv_outp("PC = 0x"REGFORMAT, uc->uc_mcontext.arm_pc); |
| 120 | sigsegv_outp("CPSR = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr); |
| 121 | sigsegv_outp("Fault Address = 0x"REGFORMAT, uc->uc_mcontext.fault_address); |
| 122 | sigsegv_outp("Trap no = 0x"REGFORMAT, uc->uc_mcontext.trap_no); |
| 123 | sigsegv_outp("Err Code = 0x"REGFORMAT, uc->uc_mcontext.error_code); |
| 124 | sigsegv_outp("Old Mask = 0x"REGFORMAT, uc->uc_mcontext.oldmask); |
| 125 | #endif |
| 126 | } |
| 127 | |
| 128 | static void print_call_link(ucontext_t *uc) |
| 129 | { |
| 130 | int i = 0; |
| 131 | void **frame_pointer = (void **)NULL; |
| 132 | void *return_address = NULL; |
| 133 | Dl_info dl_info = { 0 }; |
| 134 | |
| 135 | #if (defined __i386__) |
| 136 | frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP]; |
| 137 | return_address = (void *)uc->uc_mcontext.gregs[REG_EIP]; |
| 138 | #elif (defined __x86_64__) |
| 139 | frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP]; |
| 140 | return_address = (void *)uc->uc_mcontext.gregs[REG_RIP]; |
| 141 | #elif (defined __arm__) |
| 142 | /* sigcontext_t on ARM: |
| 143 | unsigned long trap_no; |
| 144 | unsigned long error_code; |
| 145 | unsigned long oldmask; |
| 146 | unsigned long arm_r0; |
| 147 | ... |
| 148 | unsigned long arm_r10; |
| 149 | unsigned long arm_fp; |
| 150 | unsigned long arm_ip; |
| 151 | unsigned long arm_sp; |
| 152 | unsigned long arm_lr; |
| 153 | unsigned long arm_pc; |
| 154 | unsigned long arm_cpsr; |
| 155 | unsigned long fault_address; |
| 156 | */ |
| 157 | frame_pointer = (void **)uc->uc_mcontext.arm_fp; |
| 158 | return_address = (void *)uc->uc_mcontext.arm_pc; |
| 159 | #endif |
| 160 | |
| 161 | sigsegv_outp("\nStack trace:"); |
| 162 | while (frame_pointer && return_address) |
| 163 | { |
| 164 | if (!dladdr(return_address, &dl_info)) break; |
| 165 | const char *sname = dl_info.dli_sname; |
| 166 | #ifndef NO_CPP_DEMANGLE |
| 167 | int status; |
| 168 | char *tmp = __cxa_demangle(sname, NULL, 0, &status); |
| 169 | if (status == 0 && tmp) |
| 170 | { |
| 171 | sname = tmp; |
| 172 | } |
| 173 | #endif |
| 174 | /* No: return address <sym-name + offset> (filename) */ |
| 175 | sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname, |
| 176 | (unsigned long)return_address - (unsigned long)dl_info.dli_saddr, |
| 177 | dl_info.dli_fname); |
| 178 | #ifndef NO_CPP_DEMANGLE |
| 179 | if (tmp) free(tmp); |
| 180 | #endif |
| 181 | if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main")) |
| 182 | { |
| 183 | break; |
| 184 | } |
| 185 | |
| 186 | #if (defined __x86_64__) || (defined __i386__) |
| 187 | return_address = frame_pointer[1]; |
| 188 | frame_pointer = frame_pointer[0]; |
| 189 | #elif (defined __arm__) |
| 190 | return_address = frame_pointer[-1]; |
| 191 | frame_pointer = (void **)frame_pointer[-3]; |
| 192 | #endif |
| 193 | } |
| 194 | sigsegv_outp("Stack trace end."); |
| 195 | } |
| 196 | |
| 197 | static void proc_maps_print() { |
| 198 | char file[64] = {0x00}; |
| 199 | sprintf(file,"/proc/%d/maps", getpid()); |
| 200 | FILE *fptr = fopen(file, "r"); |
| 201 | char line[1024]; |
| 202 | if(fptr) |
| 203 | { |
| 204 | memset(line, 0, sizeof(line)); |
| 205 | while(fgets(line, sizeof(line), fptr)) { |
| 206 | // printf("Line : %s", line); |
| 207 | if(strstr(line, "libmbtk_") || strstr(line, "liblynq_") || strstr(line, "libql_")) { |
| 208 | sigsegv_outp("%s", line); |
| 209 | } |
| 210 | memset(line, 0, sizeof(line)); |
| 211 | } |
| 212 | |
| 213 | fclose(fptr); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | static void sigsegv_handler(int signo, siginfo_t *info, void *context) |
| 218 | { |
| 219 | printf("sigsegv_handler - %d\n", signo); |
| 220 | if (context) |
| 221 | { |
| 222 | struct timeval log_time; |
| 223 | char tmp[50] = {0}; |
| 224 | gettimeofday(&log_time, NULL); |
| 225 | struct tm* tm_t = localtime(&(log_time.tv_sec)); |
| 226 | strftime(tmp, 50, "%F %T", tm_t); |
| 227 | snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ".%d", (int)(log_time.tv_usec / 1000)); |
| 228 | sigsegv_outp("----------------------------%s----------------------------", tmp); |
| 229 | |
| 230 | proc_maps_print(); |
| 231 | |
| 232 | ucontext_t *uc = (ucontext_t *)context; |
| 233 | char proc[100] = {0}; |
| 234 | int fd = open("/proc/self/cmdline", O_RDONLY); |
| 235 | if(fd > 0) |
| 236 | { |
| 237 | if(read(fd, proc, sizeof(proc)) > 0) { |
| 238 | sigsegv_outp("Segmentation Fault:%s", proc); |
| 239 | } else { |
| 240 | sigsegv_outp("Segmentation Fault:%s", "Unknown"); |
| 241 | } |
| 242 | close(fd); |
| 243 | } else { |
| 244 | sigsegv_outp("Segmentation Fault:%s", "Unknown"); |
| 245 | } |
| 246 | |
| 247 | sigsegv_outp("info.si_signo = %d", signo); |
| 248 | sigsegv_outp("info.si_errno = %d", info->si_errno); |
| 249 | sigsegv_outp("info.si_code = %d (%s)", info->si_code, |
| 250 | (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR"); |
| 251 | sigsegv_outp("info.si_addr = 0x%x(%p)\n", info->si_addr, info->si_addr); |
| 252 | |
| 253 | print_reg(uc); |
| 254 | print_call_link(uc); |
| 255 | |
| 256 | signal(signo, SIG_DFL); |
| 257 | raise(signo); |
| 258 | |
| 259 | printf("Segmentation Fault, refer to the log file:%s\n", proc_dump_name); |
| 260 | } |
| 261 | |
| 262 | exit(0); |
| 263 | } |
| 264 | |
| 265 | #if 1 |
| 266 | #define BACKTRACE_SIZE 32 |
| 267 | void sigsegv_handler_with_thread(int signo) { |
| 268 | struct timeval log_time; |
| 269 | char tmp[50] = {0}; |
| 270 | gettimeofday(&log_time, NULL); |
| 271 | struct tm* tm_t = localtime(&(log_time.tv_sec)); |
| 272 | strftime(tmp, 50, "%F %T", tm_t); |
| 273 | snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ".%d", (int)(log_time.tv_usec / 1000)); |
| 274 | sigsegv_outp("----------------------------%s----------------------------", tmp); |
| 275 | |
| 276 | proc_maps_print(); |
| 277 | |
| 278 | char proc[100] = {0}; |
| 279 | int fd = open("/proc/self/cmdline", O_RDONLY); |
| 280 | if(fd > 0) |
| 281 | { |
| 282 | if(read(fd, proc, sizeof(proc)) > 0) { |
| 283 | sigsegv_outp("Segmentation Fault:%s", proc); |
| 284 | } else { |
| 285 | sigsegv_outp("Segmentation Fault:%s", "Unknown"); |
| 286 | } |
| 287 | close(fd); |
| 288 | } else { |
| 289 | sigsegv_outp("Segmentation Fault:%s", "Unknown"); |
| 290 | } |
| 291 | |
| 292 | sigsegv_outp("info.si_signo = %d", signo); |
| 293 | |
| 294 | int j, nptrs; |
| 295 | void *buffer[BACKTRACE_SIZE]; |
| 296 | char **strings; |
| 297 | nptrs = backtrace(buffer, BACKTRACE_SIZE); |
| 298 | //printf("backtrace() returned %d addresses\n", nptrs); |
| 299 | strings = backtrace_symbols(buffer, nptrs); |
| 300 | if(strings == NULL){ |
| 301 | //perror("backtrace_symbols"); |
| 302 | exit(EXIT_FAILURE); |
| 303 | } |
| 304 | |
| 305 | sigsegv_outp("\nStack trace:"); |
| 306 | if(nptrs > 0) { |
| 307 | for (j = 0; j < nptrs; j++) { |
| 308 | sigsegv_outp("%02d: %s", j, strings[j]); |
| 309 | } |
| 310 | } |
| 311 | free(strings); |
| 312 | sigsegv_outp("Stack trace end."); |
| 313 | |
| 314 | signal(signo, SIG_DFL); |
| 315 | raise(signo); |
| 316 | |
| 317 | printf("Segmentation Fault, refer to the log file:%s\n", proc_dump_name); |
| 318 | exit(0); |
| 319 | } |
| 320 | #endif |
| 321 | |
| 322 | #define SETSIG(sa, sig, fun, flags) \ |
| 323 | do { \ |
| 324 | sa.sa_sigaction = fun; \ |
| 325 | sa.sa_flags = flags; \ |
| 326 | sigemptyset(&sa.sa_mask); \ |
| 327 | sigaction(sig, &sa, NULL); \ |
| 328 | } while(0) |
| 329 | |
| 330 | |
b.liu | ced8dd0 | 2024-06-28 13:28:29 +0800 | [diff] [blame] | 331 | // arm-openwrt-linux-addr2line -e out/bin/mbtk_gnssd 0x12ca8 |
b.liu | bb59049 | 2024-06-13 16:42:08 +0800 | [diff] [blame] | 332 | void mbtk_debug_open(const char *log_file, bool thread_support) |
| 333 | { |
| 334 | struct sigaction sa; |
| 335 | |
| 336 | #if 1 |
| 337 | if(thread_support) { |
| 338 | SETSIG(sa, SIGSEGV, sigsegv_handler_with_thread, 0); |
| 339 | SETSIG(sa, SIGABRT, sigsegv_handler_with_thread, 0); |
| 340 | } else { |
| 341 | SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO); |
| 342 | SETSIG(sa, SIGABRT, sigsegv_handler, SA_SIGINFO); |
| 343 | } |
| 344 | #else |
| 345 | SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO); |
| 346 | #endif |
| 347 | |
| 348 | memset(proc_name, 0, sizeof(proc_name)); |
| 349 | memset(proc_dump_name, 0, sizeof(proc_dump_name)); |
| 350 | int fd = open("/proc/self/cmdline", O_RDONLY); |
| 351 | if(fd > 0) |
| 352 | { |
| 353 | if(read(fd, proc_name, sizeof(proc_name)) <= 0) { |
| 354 | LOGE("Get PROC name fail."); |
| 355 | } |
| 356 | close(fd); |
| 357 | } |
| 358 | |
| 359 | // Redirect stderr to log_file. |
| 360 | if(log_file) { |
| 361 | memcpy(proc_dump_name, log_file, strlen(log_file)); |
| 362 | } else { |
| 363 | property_get(MBTK_PROC_DUMP_DIR, proc_dump_name, ""); |
| 364 | if(strlen(proc_dump_name) > 0) { |
| 365 | snprintf(proc_dump_name + strlen(proc_dump_name),sizeof(proc_dump_name) - strlen(proc_dump_name), "/%s", proc_name); |
| 366 | } else { |
| 367 | memcpy(proc_dump_name, MBTK_PROC_DUMP_FILE_DEF, strlen(MBTK_PROC_DUMP_FILE_DEF)); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | #if 0 |
| 372 | if(freopen(proc_dump_name, "a", stderr) == NULL) { |
| 373 | LOGE("reopen stderr to %s fail.[%d]", proc_dump_name, errno); |
| 374 | } |
| 375 | #else |
| 376 | proc_dump_fd = open(proc_dump_name, O_WRONLY | O_CREAT | O_APPEND, 0666); |
| 377 | if(proc_dump_fd < 0) { |
| 378 | LOGE("Open(%s) fail:%d.", proc_dump_name, errno); |
| 379 | } |
| 380 | #endif |
| 381 | } |
| 382 | |