/thirdparty/breakpad/third_party/glog/src/signalhandler.cc
C++ | 350 lines | 203 code | 36 blank | 111 comment | 24 complexity | 0348512354612e95608832dd69f98913 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, GPL-3.0, GPL-2.0
- // 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" },
- { SIGBUS, "SIGBUS" },
- { SIGTERM, "SIGTERM" },
- };
- // 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 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());
- }
- // 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 (int 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());
- }
- // 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) {
- 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);
- }
- // 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.
- void FailureSignalHandler(int signal_number,
- siginfo_t *signal_info,
- void *ucontext) {
- // 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();
- // Get the program counter from ucontext.
- void *pc = GetPC(ucontext);
- DumpStackFrameInfo("PC: ", pc);
- #ifdef HAVE_STACKTRACE
- // Get the stack traces.
- void *stack[32];
- // +1 to exclude this function.
- const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
- DumpSignalInfo(signal_number, signal_info);
- // 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
- void InstallFailureSignalHandler() {
- // 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 (int i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
- CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL));
- }
- }
- void InstallFailureWriter(void (*writer)(const char* data, int size)) {
- g_failure_writer = writer;
- }
- _END_GOOGLE_NAMESPACE_