// Copyright (c) 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: Satoru Takabayashi // // Implementation of InstallFailureSignalHandler(). #include "utilities.h" #include "stacktrace.h" #include "symbolize.h" #include "glog/logging.h" #include <signal.h> #include <time.h> #ifdef HAVE_UCONTEXT_H # include <ucontext.h> #endif #ifdef HAVE_SYS_UCONTEXT_H # include <sys/ucontext.h> #endif #include <algorithm> _START_GOOGLE_NAMESPACE_ namespace { // We'll install the failure signal handler for these signals. We could // use strsignal() to get signal names, but we don't use it to avoid // introducing yet another #ifdef complication. // // The list should be synced with the comment in signalhandler.h. const struct { int number; const char *name; } kFailureSignals[] = { { SIGSEGV, "SIGSEGV" }, { SIGILL, "SIGILL" }, { SIGFPE, "SIGFPE" }, { SIGABRT, "SIGABRT" }, #if !defined(OS_WINDOWS) { SIGBUS, "SIGBUS" }, #endif { SIGTERM, "SIGTERM" }, }; static bool kFailureSignalHandlerInstalled = false; // Returns the program counter from signal context, NULL if unknown. void* GetPC(void* ucontext_in_void) { #if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT) if (ucontext_in_void != NULL) { ucontext_t *context = reinterpret_cast<ucontext_t *>(ucontext_in_void); return (void*)context->PC_FROM_UCONTEXT; } #endif return NULL; } // The class is used for formatting error messages. We don't use printf() // as it's not async signal safe. class MinimalFormatter { public: MinimalFormatter(char *buffer, int size) : buffer_(buffer), cursor_(buffer), end_(buffer + size) { } // Returns the number of bytes written in the buffer. int num_bytes_written() const { return (int) (cursor_ - buffer_); } // Appends string from "str" and updates the internal cursor. void AppendString(const char* str) { int i = 0; while (str[i] != '\0' && cursor_ + i < end_) { cursor_[i] = str[i]; ++i; } cursor_ += i; } // Formats "number" in "radix" and updates the internal cursor. // Lowercase letters are used for 'a' - 'z'. void AppendUint64(uint64 number, int radix) { int i = 0; while (cursor_ + i < end_) { const int tmp = number % radix; number /= radix; cursor_[i] = (tmp < 10 ? '0' + tmp : 'a' + tmp - 10); ++i; if (number == 0) { break; } } // Reverse the bytes written. std::reverse(cursor_, cursor_ + i); cursor_ += i; } // Formats "number" as hexadecimal number, and updates the internal // cursor. Padding will be added in front if needed. void AppendHexWithPadding(uint64 number, int width) { char* start = cursor_; AppendString("0x"); AppendUint64(number, 16); // Move to right and add padding in front if needed. if (cursor_ < start + width) { const int64 delta = start + width - cursor_; std::copy(start, cursor_, start + delta); std::fill(start, start + delta, ' '); cursor_ = start + width; } } private: char *buffer_; char *cursor_; const char * const end_; }; // Writes the given data with the size to the standard error. void WriteToStderr(const char* data, int size) { if (write(STDERR_FILENO, data, size) < 0) { // Ignore errors. } } // The writer function can be changed by InstallFailureWriter(). void (*g_failure_writer)(const char* data, int size) = WriteToStderr; // Dumps time information. We don't dump human-readable time information // as localtime() is not guaranteed to be async signal safe. void DumpTimeInfo() { time_t time_in_sec = time(NULL); char buf[256]; // Big enough for time info. MinimalFormatter formatter(buf, sizeof(buf)); formatter.AppendString("*** Aborted at "); formatter.AppendUint64(time_in_sec, 10); formatter.AppendString(" (unix time)"); formatter.AppendString(" try \"date -d @"); formatter.AppendUint64(time_in_sec, 10); formatter.AppendString("\" if you are using GNU date ***\n"); g_failure_writer(buf, formatter.num_bytes_written()); } // TODO(hamaji): Use signal instead of sigaction? #ifdef HAVE_SIGACTION // Dumps information about the signal to STDERR. void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { // Get the signal name. const char* signal_name = NULL; for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { if (signal_number == kFailureSignals[i].number) { signal_name = kFailureSignals[i].name; } } char buf[256]; // Big enough for signal info. MinimalFormatter formatter(buf, sizeof(buf)); formatter.AppendString("*** "); if (signal_name) { formatter.AppendString(signal_name); } else { // Use the signal number if the name is unknown. The signal name // should be known, but just in case. formatter.AppendString("Signal "); formatter.AppendUint64(signal_number, 10); } formatter.AppendString(" (@0x"); formatter.AppendUint64(reinterpret_cast<uintptr_t>(siginfo->si_addr), 16); formatter.AppendString(")"); formatter.AppendString(" received by PID "); formatter.AppendUint64(getpid(), 10); formatter.AppendString(" (TID 0x"); // We assume pthread_t is an integral number or a pointer, rather // than a complex struct. In some environments, pthread_self() // returns an uint64 but in some other environments pthread_self() // returns a pointer. Hence we use C-style cast here, rather than // reinterpret/static_cast, to support both types of environments. formatter.AppendUint64((uintptr_t)pthread_self(), 16); formatter.AppendString(") "); // Only linux has the PID of the signal sender in si_pid. #ifdef OS_LINUX formatter.AppendString("from PID "); formatter.AppendUint64(siginfo->si_pid, 10); formatter.AppendString("; "); #endif formatter.AppendString("stack trace: ***\n"); g_failure_writer(buf, formatter.num_bytes_written()); } #endif // HAVE_SIGACTION // Dumps information about the stack frame to STDERR. void DumpStackFrameInfo(const char* prefix, void* pc) { // Get the symbol name. const char *symbol = "(unknown)"; char symbolized[1024]; // Big enough for a sane symbol. // Symbolizes the previous address of pc because pc may be in the // next function. if (Symbolize(reinterpret_cast<char *>(pc) - 1, symbolized, sizeof(symbolized))) { symbol = symbolized; } char buf[1024]; // Big enough for stack frame info. MinimalFormatter formatter(buf, sizeof(buf)); formatter.AppendString(prefix); formatter.AppendString("@ "); const int width = 2 * sizeof(void*) + 2; // + 2 for "0x". formatter.AppendHexWithPadding(reinterpret_cast<uintptr_t>(pc), width); formatter.AppendString(" "); formatter.AppendString(symbol); formatter.AppendString("\n"); g_failure_writer(buf, formatter.num_bytes_written()); } // Invoke the default signal handler. void InvokeDefaultSignalHandler(int signal_number) { #ifdef HAVE_SIGACTION struct sigaction sig_action; memset(&sig_action, 0, sizeof(sig_action)); sigemptyset(&sig_action.sa_mask); sig_action.sa_handler = SIG_DFL; sigaction(signal_number, &sig_action, NULL); kill(getpid(), signal_number); #elif defined(OS_WINDOWS) signal(signal_number, SIG_DFL); raise(signal_number); #endif } // This variable is used for protecting FailureSignalHandler() from // dumping stuff while another thread is doing it. Our policy is to let // the first thread dump stuff and let other threads wait. // See also comments in FailureSignalHandler(). static pthread_t* g_entered_thread_id_pointer = NULL; // Dumps signal and stack frame information, and invokes the default // signal handler once our job is done. #if defined(OS_WINDOWS) void FailureSignalHandler(int signal_number) #else void FailureSignalHandler(int signal_number, siginfo_t *signal_info, void *ucontext) #endif { // First check if we've already entered the function. We use an atomic // compare and swap operation for platforms that support it. For other // platforms, we use a naive method that could lead to a subtle race. // We assume pthread_self() is async signal safe, though it's not // officially guaranteed. pthread_t my_thread_id = pthread_self(); // NOTE: We could simply use pthread_t rather than pthread_t* for this, // if pthread_self() is guaranteed to return non-zero value for thread // ids, but there is no such guarantee. We need to distinguish if the // old value (value returned from __sync_val_compare_and_swap) is // different from the original value (in this case NULL). pthread_t* old_thread_id_pointer = glog_internal_namespace_::sync_val_compare_and_swap( &g_entered_thread_id_pointer, static_cast<pthread_t*>(NULL), &my_thread_id); if (old_thread_id_pointer != NULL) { // We've already entered the signal handler. What should we do? if (pthread_equal(my_thread_id, *g_entered_thread_id_pointer)) { // It looks the current thread is reentering the signal handler. // Something must be going wrong (maybe we are reentering by another // type of signal?). Kill ourself by the default signal handler. InvokeDefaultSignalHandler(signal_number); } // Another thread is dumping stuff. Let's wait until that thread // finishes the job and kills the process. while (true) { sleep(1); } } // This is the first time we enter the signal handler. We are going to // do some interesting stuff from here. // TODO(satorux): We might want to set timeout here using alarm(), but // mixing alarm() and sleep() can be a bad idea. // First dump time info. DumpTimeInfo(); #if !defined(OS_WINDOWS) // Get the program counter from ucontext. void *pc = GetPC(ucontext); DumpStackFrameInfo("PC: ", pc); #endif #ifdef HAVE_STACKTRACE // Get the stack traces. void *stack[32]; // +1 to exclude this function. const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1); # ifdef HAVE_SIGACTION DumpSignalInfo(signal_number, signal_info); # endif // Dump the stack traces. for (int i = 0; i < depth; ++i) { DumpStackFrameInfo(" ", stack[i]); } #endif // *** TRANSITION *** // // BEFORE this point, all code must be async-termination-safe! // (See WARNING above.) // // AFTER this point, we do unsafe things, like using LOG()! // The process could be terminated or hung at any time. We try to // do more useful things first and riskier things later. // Flush the logs before we do anything in case 'anything' // causes problems. FlushLogFilesUnsafe(0); // Kill ourself by the default signal handler. InvokeDefaultSignalHandler(signal_number); } } // namespace namespace glog_internal_namespace_ { bool IsFailureSignalHandlerInstalled() { #ifdef HAVE_SIGACTION // TODO(andschwa): Return kFailureSignalHandlerInstalled? struct sigaction sig_action; memset(&sig_action, 0, sizeof(sig_action)); sigemptyset(&sig_action.sa_mask); sigaction(SIGABRT, NULL, &sig_action); if (sig_action.sa_sigaction == &FailureSignalHandler) return true; #elif defined(OS_WINDOWS) return kFailureSignalHandlerInstalled; #endif // HAVE_SIGACTION return false; } } // namespace glog_internal_namespace_ void InstallFailureSignalHandler() { #ifdef HAVE_SIGACTION // Build the sigaction struct. struct sigaction sig_action; memset(&sig_action, 0, sizeof(sig_action)); sigemptyset(&sig_action.sa_mask); sig_action.sa_flags |= SA_SIGINFO; sig_action.sa_sigaction = &FailureSignalHandler; for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL)); } kFailureSignalHandlerInstalled = true; #elif defined(OS_WINDOWS) for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler), SIG_ERR); } kFailureSignalHandlerInstalled = true; #endif // HAVE_SIGACTION } void InstallFailureWriter(void (*writer)(const char* data, int size)) { #if defined(HAVE_SIGACTION) || defined(OS_WINDOWS) g_failure_writer = writer; #endif // HAVE_SIGACTION } _END_GOOGLE_NAMESPACE_