/// @brief Invokes addr2line utility to determine the function name /// and the line information from an address in the code segment. static char *addr2line(const char *image, void *addr, bool color_output, char** memory) { int pipefd[2]; if (pipe(pipefd) != 0) { safe_abort(); } pid_t pid = fork(); if (pid == 0) { close(pipefd[0]); dup2(pipefd[1], STDOUT_FILENO); dup2(pipefd[1], STDERR_FILENO); if (execlp("addr2line", "addr2line", Safe::ptoa(addr, *memory), "-f", "-C", "-e", image, reinterpret_cast<void*>(NULL)) == -1) { safe_abort(); } } close(pipefd[1]); const int line_max_length = 4096; char* line = *memory; *memory += line_max_length; ssize_t len = read(pipefd[0], line, line_max_length); close(pipefd[0]); if (len == 0) { safe_abort(); } line[len] = 0; if (waitpid(pid, NULL, 0) != pid) { safe_abort(); } if (line[0] == '?') { char* straddr = Safe::ptoa(addr, *memory); if (color_output) { strcpy(line, "\033[32;1m"); // NOLINT(runtime/printf) } strcat(line, straddr); // NOLINT(runtime/printf) if (color_output) { strcat(line, "\033[0m"); // NOLINT(runtime/printf) } strcat(line, " at "); // NOLINT(runtime/printf) strcat(line, image); // NOLINT(runtime/printf) strcat(line, " "); // NOLINT(runtime/printf) } else { if (*(strstr(line, "\n") + 1) == '?') { char* straddr = Safe::ptoa(addr, *memory); strcpy(strstr(line, "\n") + 1, image); // NOLINT(runtime/printf) strcat(line, ":"); // NOLINT(runtime/printf) strcat(line, straddr); // NOLINT(runtime/printf) strcat(line, "\n"); // NOLINT(runtime/printf) } } return line; }
void DeathHandler::HandleSignal(int sig, void * /* info */, void *secret) { // Stop all other running threads by forking pid_t forkedPid = fork(); if (forkedPid != 0) { int status; if (thread_safe_) { // Freeze the original process, until it's child prints the stack trace kill(getpid(), SIGSTOP); // Wait for the child without blocking and exit as soon as possible, // so that no zombies are left. waitpid(forkedPid, &status, WNOHANG); } else { // Wait for the child, blocking only the current thread. // All other threads will continue to run, potentially crashing the parent. waitpid(forkedPid, &status, 0); } #ifdef QUICK_EXIT if (quick_exit_) { ::quick_exit(EXIT_FAILURE); } #endif if (generate_core_dump_) { struct sigaction sa; sigaction(SIGABRT, NULL, &sa); sa.sa_handler = SIG_DFL; sigaction(SIGABRT, &sa, NULL); abort(); } else { if (cleanup_) { exit(EXIT_FAILURE); } else { _Exit(EXIT_FAILURE); } } } ucontext_t *uc = reinterpret_cast<ucontext_t *>(secret); if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) { // redirect stdout to stderr print("Failed to redirect stdout to stderr\n"); } char* memory = memory_; { char* msg = memory; const int msg_max_length = 128; if (color_output_) { // \033[31;1mSegmentation fault\033[0m \033[33;1m(%i)\033[0m\n strcpy(msg, "\033[31;1m"); // NOLINT(runtime/printf) } else { msg[0] = '\0'; } switch (sig) { case SIGSEGV: strcat(msg, "Segmentation fault"); // NOLINT(runtime/printf) break; case SIGABRT: strcat(msg, "Aborted"); // NOLINT(runtime/printf) break; case SIGFPE: strcat(msg, "Floating point exception"); // NOLINT(runtime/printf) break; default: strcat(msg, "Caught signal "); // NOLINT(runtime/printf) strcat(msg, Safe::itoa(sig, msg + msg_max_length)); // NOLINT(*) break; } if (color_output_) { strcat(msg, "\033[0m"); // NOLINT(runtime/printf) } strcat(msg, " (thread "); // NOLINT(runtime/printf) if (color_output_) { strcat(msg, "\033[33;1m"); // NOLINT(runtime/printf) } #ifndef __APPLE__ strcat(msg, Safe::utoa(pthread_self(), msg + msg_max_length)); // NOLINT(*) #else strcat(msg, Safe::ptoa(pthread_self(), msg + msg_max_length)); // NOLINT(*) #endif if (color_output_) { strcat(msg, "\033[0m"); // NOLINT(runtime/printf) } strcat(msg, ", pid "); // NOLINT(runtime/printf) if (color_output_) { strcat(msg, "\033[33;1m"); // NOLINT(runtime/printf) } strcat(msg, Safe::itoa(getppid(), msg + msg_max_length)); // NOLINT(*) if (color_output_) { strcat(msg, "\033[0m"); // NOLINT(runtime/printf) } strcat(msg, ")"); // NOLINT(runtime/printf) print(msg); } print("\nStack trace:\n"); void **trace = reinterpret_cast<void**>(memory); memory += (frames_count_ + 2) * sizeof(void*); // Workaround malloc() inside backtrace() heap_trap_active_ = true; int trace_size = backtrace(trace, frames_count_ + 2); heap_trap_active_ = false; if (trace_size <= 2) { safe_abort(); } // Overwrite sigaction with caller's address #ifdef __linux__ #if defined(__arm__) trace[1] = reinterpret_cast<void *>(uc->uc_mcontext.arm_pc); #elif defined(__aarch64__) trace[1] = reinterpret_cast<void *>(uc->uc_mcontext.pc); #else #if !defined(__i386__) && !defined(__x86_64__) #error Only ARM, AARCH64, x86 and x86-64 are supported #endif #if defined(__x86_64__) trace[1] = reinterpret_cast<void *>(uc->uc_mcontext.gregs[REG_RIP]); #else trace[1] = reinterpret_cast<void *>(uc->uc_mcontext.gregs[REG_EIP]); #endif #endif const int path_max_length = 2048; char* name_buf = memory; ssize_t name_buf_length = readlink("/proc/self/exe", name_buf, path_max_length - 1); if (name_buf_length < 1) { safe_abort(); } name_buf[name_buf_length] = 0; memory += name_buf_length + 1; char* cwd = memory; if (getcwd(cwd, path_max_length) == NULL) { safe_abort(); } strcat(cwd, "/"); // NOLINT(runtime/printf) memory += strlen(cwd) + 1; char* prev_memory = memory; int stackOffset = trace[2] == trace[1]? 2 : 1; for (int i = stackOffset; i < trace_size; i++) { memory = prev_memory; char *line; Dl_info dlinf; if (dladdr(trace[i], &dlinf) == 0 || dlinf.dli_fname[0] != '/' || !strcmp(name_buf, dlinf.dli_fname)) { line = addr2line(name_buf, trace[i], color_output_, &memory); } else { line = addr2line(dlinf.dli_fname, reinterpret_cast<void *>( reinterpret_cast<char *>(trace[i]) - reinterpret_cast<char *>(dlinf.dli_fbase)), color_output_, &memory); } char *function_name_end = strstr(line, "\n"); if (function_name_end != NULL) { *function_name_end = 0; { // "\033[34;1m[%s]\033[0m \033[33;1m(%i)\033[0m\n char* msg = memory; const int msg_max_length = 512; if (color_output_) { strcpy(msg, "\033[34;1m"); // NOLINT(runtime/printf) } else { msg[0] = 0; } strcat(msg, "["); // NOLINT(runtime/printf) strcat(msg, line); // NOLINT(runtime/printf) strcat(msg, "]"); // NOLINT(runtime/printf) if (append_pid_) { if (color_output_) { strcat(msg, "\033[0m\033[33;1m"); // NOLINT(runtime/printf) } strcat(msg, " ("); // NOLINT(runtime/printf) strcat(msg, Safe::itoa(getppid(), msg + msg_max_length)); // NOLINT(*) strcat(msg, ")"); // NOLINT(runtime/printf) if (color_output_) { strcat(msg, "\033[0m"); // NOLINT(runtime/printf) } strcat(msg, "\n"); // NOLINT(runtime/printf) } else { if (color_output_) { strcat(msg, "\033[0m"); // NOLINT(runtime/printf) } strcat(msg, "\n"); // NOLINT(runtime/printf) } print(msg); } line = function_name_end + 1; // Remove the common path root if (cut_common_path_root_) { int cpi; for (cpi = 0; cwd[cpi] == line[cpi]; cpi++) {}; if (line[cpi - 1] != '/') { for (; line[cpi - 1] != '/'; cpi--) {}; } if (cpi > 1) { line = line + cpi; } } // Remove relative path root if (cut_relative_paths_) { char *path_cut_pos = strstr(line, "../"); if (path_cut_pos != NULL) { path_cut_pos += 3; while (!strncmp(path_cut_pos, "../", 3)) { path_cut_pos += 3; } line = path_cut_pos; } } // Mark line number if (color_output_) { char* number_pos = strstr(line, ":"); if (number_pos != NULL) { char* line_number = memory; // 128 strcpy(line_number, number_pos); // NOLINT(runtime/printf) // Overwrite the new line char line_number[strlen(line_number) - 1] = 0; // \033[32;1m%s\033[0m\n strcpy(number_pos, "\033[32;1m"); // NOLINT(runtime/printf) strcat(line, line_number); // NOLINT(runtime/printf) strcat(line, "\033[0m\n"); // NOLINT(runtime/printf) } } } // Overwrite the new line char line[strlen(line) - 1] = 0; // Append pid if (append_pid_) { // %s\033[33;1m(%i)\033[0m\n strcat(line, " "); // NOLINT(runtime/printf) if (color_output_) { strcat(line, "\033[33;1m"); // NOLINT(runtime/printf) } strcat(line, "("); // NOLINT(runtime/printf) strcat(line, Safe::itoa(getppid(), memory)); // NOLINT(runtime/printf) strcat(line, ")"); // NOLINT(runtime/printf) if (color_output_) { strcat(line, "\033[0m"); // NOLINT(runtime/printf) } } strcat(line, "\n"); // NOLINT(runtime/printf) print(line); } // Write '\0' to indicate the end of the output char end = '\0'; write(STDERR_FILENO, &end, 1); #elif defined(__APPLE__) for (int i = 0; i < trace_size; i++) { Safe::ptoa(trace[i], memory); strcat(memory, "\n"); print(memory); } #endif if (thread_safe_) { // Resume the parent process kill(getppid(), SIGCONT); } // This is called in the child process _Exit(EXIT_SUCCESS); }
/// @brief Invokes addr2line utility to determine the function name /// and the line information from an address in the code segment. static char *addr2line(const char *image, void *addr, bool color_output, char** memory) { int pipefd[2]; if (pipe(pipefd) != 0) { safe_abort(); } char* line = *memory; pid_t pid = fork(); if (pid == 0) { close(pipefd[0]); dup2(pipefd[1], STDOUT_FILENO); dup2(pipefd[1], STDERR_FILENO); if (execlp("addr2line", "addr2line", Safe::ptoa(addr, *memory), "-f", "-C", "-e", image, reinterpret_cast<void*>(NULL)) == -1) { char* result = (char*)"??\n??:0\n"; int32_t totalBytesWritten = 0; while (totalBytesWritten < 9) { int32_t bytesWritten = write(pipefd[1], result, 9);; if(bytesWritten <= 0) { if(bytesWritten == -1 && errno == EINTR) continue; else safe_abort(); } totalBytesWritten += bytesWritten; } safe_abort(); } } else if(pid > 0) { close(pipefd[1]); const int line_max_length = 4096; *memory += line_max_length; ssize_t len = read(pipefd[0], line, line_max_length); close(pipefd[0]); if (len == 0) { safe_abort(); } line[len] = 0; if (waitpid(pid, NULL, 0) != pid) { safe_abort(); } if (line[0] == '?') { char* straddr = Safe::ptoa(addr, *memory); if (color_output) { strcpy(line, "\033[32;1m"); // NOLINT(runtime/printf) } strcat(line, straddr); // NOLINT(runtime/printf) if (color_output) { strcat(line, "\033[0m"); // NOLINT(runtime/printf) } strcat(line, " at "); // NOLINT(runtime/printf) strcat(line, image); // NOLINT(runtime/printf) strcat(line, " "); // NOLINT(runtime/printf) } else { if (*(strstr(line, "\n") + 1) == '?') { char* straddr = Safe::ptoa(addr, *memory); strcpy(strstr(line, "\n") + 1, image); // NOLINT(runtime/printf) strcat(line, ":"); // NOLINT(runtime/printf) strcat(line, straddr); // NOLINT(runtime/printf) strcat(line, "\n"); // NOLINT(runtime/printf) } } } return line; }