static void abortHandler(int signo, siginfo_t *info, void *ctx) { pid_t pid = getpid(); char messageBuf[1024]; #ifdef LIBC_HAS_BACKTRACE_FUNC void *backtraceStore[512]; backtraceStore[0] = '\0'; // Don't let gdb print uninitialized contents. #endif char *end = messageBuf; end = appendText(end, "[ pid="); end = appendULL(end, (unsigned long long) pid); end = appendText(end, ", timestamp="); end = appendULL(end, (unsigned long long) time(NULL)); end = appendText(end, " ] Process aborted! signo="); end = appendSignalName(end, signo); end = appendText(end, ", reason="); end = appendSignalReason(end, info); // It is important that writing the message and the backtrace are two // seperate operations because it's not entirely clear whether the // latter is async signal safe and thus can crash. #ifdef LIBC_HAS_BACKTRACE_FUNC end = appendText(end, ", backtrace available.\n"); #else end = appendText(end, "\n"); #endif write(STDERR_FILENO, messageBuf, end - messageBuf); #ifdef LIBC_HAS_BACKTRACE_FUNC /* For some reason, it would appear that fatal signal * handlers have a deadline on some systems: the process will * be killed if the signal handler doesn't finish in time. * This killing appears to be triggered at some system calls, * including but not limited to nanosleep(). * backtrace() might be slow and running crash-watch is * definitely slow, so we do our work in a child process * in order not to be affected by the deadline. But preferably * we don't fork because forking will cause us to lose * thread information. */ #ifdef __linux__ bool hasDeadline = false; #else // Mac OS X has a deadline. Not sure about other systems. bool hasDeadline = true; #endif if (!hasDeadline || fork() == 0) { int frames = backtrace(backtraceStore, sizeof(backtraceStore) / sizeof(void *)); end = messageBuf; end = appendText(end, "--------------------------------------\n"); end = appendText(end, "[ pid="); end = appendULL(end, (unsigned long long) pid); end = appendText(end, " ] Backtrace with "); end = appendULL(end, (unsigned long long) frames); end = appendText(end, " frames:\n"); write(STDERR_FILENO, messageBuf, end - messageBuf); backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO); end = messageBuf; end = appendText(end, "--------------------------------------\n"); end = appendText(end, "[ pid="); end = appendULL(end, (unsigned long long) pid); end = appendText(end, " ] Dumping a more detailed backtrace with crash-watch " "('gem install crash-watch' if you don't have it)...\n"); write(STDERR_FILENO, messageBuf, end - messageBuf); end = messageBuf; end = appendText(end, "crash-watch --dump "); end = appendULL(end, (unsigned long long) getpid()); *end = '\0'; system(messageBuf); _exit(1); } #endif // Run default signal handler. kill(getpid(), signo); }
static void abortHandler(int signo, siginfo_t *info, void *ctx) { AbortHandlerState state; state.pid = getpid(); state.signo = signo; state.info = info; pid_t child; time_t t = time(NULL); char crashLogFile[256]; abortHandlerCalled++; if (abortHandlerCalled > 1) { // The abort handler itself crashed! char *end = state.messageBuf; end = appendText(end, "[ origpid="); end = appendULL(end, (unsigned long long) state.pid); end = appendText(end, ", pid="); end = appendULL(end, (unsigned long long) getpid()); end = appendText(end, ", timestamp="); end = appendULL(end, (unsigned long long) t); if (abortHandlerCalled == 2) { // This is the first time it crashed. end = appendText(end, " ] Abort handler crashed! signo="); end = appendSignalName(end, state.signo); end = appendText(end, ", reason="); end = appendSignalReason(end, state.info); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); // Run default signal handler. raise(signo); } else { // This is the second time it crashed, meaning it failed to // invoke the default signal handler to abort the process! end = appendText(end, " ] Abort handler crashed again! Force exiting this time. signo="); end = appendSignalName(end, state.signo); end = appendText(end, ", reason="); end = appendSignalReason(end, state.info); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); _exit(1); } return; } if (emergencyPipe1[0] != -1) { close(emergencyPipe1[0]); } if (emergencyPipe1[1] != -1) { close(emergencyPipe1[1]); } if (emergencyPipe2[0] != -1) { close(emergencyPipe2[0]); } if (emergencyPipe2[1] != -1) { close(emergencyPipe2[1]); } emergencyPipe1[0] = emergencyPipe1[1] = -1; emergencyPipe2[0] = emergencyPipe2[1] = -1; /* We want to dump the entire crash log to both stderr and a log file. * We use 'tee' for this. */ if (createCrashLogFile(crashLogFile, t)) { forkAndRedirectToTee(crashLogFile); } char *end = state.messagePrefix; end = appendText(end, "[ pid="); end = appendULL(end, (unsigned long long) state.pid); *end = '\0'; end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, ", timestamp="); end = appendULL(end, (unsigned long long) t); end = appendText(end, " ] Process aborted! signo="); end = appendSignalName(end, state.signo); end = appendText(end, ", reason="); end = appendSignalReason(end, state.info); end = appendText(end, ", randomSeed="); end = appendULL(end, (unsigned long long) randomSeed); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); end = state.messageBuf; if (*crashLogFile != '\0') { end = appendText(end, state.messagePrefix); end = appendText(end, " ] Crash log dumped to "); end = appendText(end, crashLogFile); end = appendText(end, "\n"); } else { end = appendText(end, state.messagePrefix); end = appendText(end, " ] Could not create crash log file, so dumping to stderr only.\n"); } write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); if (beepOnAbort) { end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, " ] PASSENGER_BEEP_ON_ABORT on, executing beep...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); child = asyncFork(); if (child == 0) { closeAllFileDescriptors(2, true); #ifdef __APPLE__ execlp("osascript", "osascript", "-e", "beep 2", (const char * const) 0); safePrintErr("Cannot execute 'osascript' command\n"); #else execlp("beep", "beep", (const char * const) 0); safePrintErr("Cannot execute 'beep' command\n"); #endif _exit(1); } else if (child == -1) { int e = errno; end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, " ] Could fork a child process for invoking a beep: fork() failed with errno="); end = appendULL(end, e); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); } } if (stopOnAbort) { end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, " ] PASSENGER_STOP_ON_ABORT on, so process stopped. Send SIGCONT when you want to continue.\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); raise(SIGSTOP); } // It isn't safe to call any waiting functions in this signal handler, // not even read() and waitpid() even though they're async signal safe. // So we fork a child process and let it dump as much diagnostics as possible // instead of doing it in this process. child = asyncFork(); if (child == 0) { // Sleep for a short while to allow the parent process to raise SIGSTOP. // usleep() and nanosleep() aren't async signal safe so we use select() // instead. struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; select(0, NULL, NULL, NULL, &tv); resetSignalHandlersAndMask(); child = asyncFork(); if (child == 0) { // OS X: for some reason the SIGPIPE handler may be reset to default after forking. // Later in this program we're going to pipe backtrace_symbols_fd() into the backtrace // sanitizer, which may fail, and we don't want the diagnostics process to crash // with SIGPIPE as a result, so we ignore SIGPIPE again. ignoreSigpipe(); dumpDiagnostics(state); // The child process may or may or may not resume the original process. // We do it ourselves just to be sure. kill(state.pid, SIGCONT); _exit(0); } else if (child == -1) { int e = errno; end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, "] Could fork a child process for dumping diagnostics: fork() failed with errno="); end = appendULL(end, e); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); _exit(1); } else { // Exit immediately so that child process is adopted by init process. _exit(0); } } else if (child == -1) { int e = errno; end = state.messageBuf; end = appendText(end, state.messagePrefix); end = appendText(end, " ] Could fork a child process for dumping diagnostics: fork() failed with errno="); end = appendULL(end, e); end = appendText(end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf); } else { raise(SIGSTOP); // Will continue after the child process has done its job. } // Run default signal handler. raise(signo); }