| // 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; |
| } |
| #else |
| (void)ucontext_in_void; |
| #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()); |
| } |
| |
| // TOOD(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); |
| #else |
| (void)ucontext; |
| #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); |
| # else |
| (void)signal_info; |
| # endif |
| // Dump the stack traces. |
| for (int i = 0; i < depth; ++i) { |
| DumpStackFrameInfo(" ", stack[i]); |
| } |
| #else |
| (void)signal_info; |
| #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_ |