403 lines
13 KiB
C++
403 lines
13 KiB
C++
// 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_
|