X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2Fexperimental%2Fsymbolizer%2FSignalHandler.cpp;h=91a4ae163dc4f2ec9286c4e8e048fd7aef82d13d;hp=4a17ee0443276bd1e45fafda54fb0f37cdcdeeb0;hb=52dc008c3f191850cc1d0466e71459dad33e835f;hpb=2ff7b2379ddc5760c8b70f24e31394418c9a37e5 diff --git a/folly/experimental/symbolizer/SignalHandler.cpp b/folly/experimental/symbolizer/SignalHandler.cpp index 4a17ee04..91a4ae16 100644 --- a/folly/experimental/symbolizer/SignalHandler.cpp +++ b/folly/experimental/symbolizer/SignalHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2013-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,26 +16,29 @@ // This is heavily inspired by the signal handler from google-glog -#include "folly/experimental/symbolizer/SignalHandler.h" +#include +#include #include + +#include #include #include #include -#include -#include -#include #include #include -#include "folly/Conv.h" -#include "folly/FileUtil.h" -#include "folly/Portability.h" -#include "folly/ScopeGuard.h" -#include "folly/experimental/symbolizer/Symbolizer.h" +#include +#include +#include +#include +#include +#include +#include -namespace folly { namespace symbolizer { +namespace folly { +namespace symbolizer { namespace { @@ -57,22 +60,20 @@ class FatalSignalCallbackRegistry { }; FatalSignalCallbackRegistry::FatalSignalCallbackRegistry() - : installed_(false) { -} + : installed_(false) {} void FatalSignalCallbackRegistry::add(SignalCallback func) { std::lock_guard lock(mutex_); - CHECK(!installed_) - << "FatalSignalCallbackRegistry::add may not be used " - "after installing the signal handlers."; + CHECK(!installed_) << "FatalSignalCallbackRegistry::add may not be used " + "after installing the signal handlers."; handlers_.push_back(func); } void FatalSignalCallbackRegistry::markInstalled() { std::lock_guard lock(mutex_); CHECK(!installed_.exchange(true)) - << "FatalSignalCallbackRegistry::markInstalled must be called " - << "at most once"; + << "FatalSignalCallbackRegistry::markInstalled must be called " + << "at most once"; } void FatalSignalCallbackRegistry::run() { @@ -87,20 +88,20 @@ void FatalSignalCallbackRegistry::run() { // Leak it so we don't have to worry about destruction order FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry = - new FatalSignalCallbackRegistry; + new FatalSignalCallbackRegistry; struct { int number; const char* name; struct sigaction oldAction; } kFatalSignals[] = { - { SIGSEGV, "SIGSEGV" }, - { SIGILL, "SIGILL" }, - { SIGFPE, "SIGFPE" }, - { SIGABRT, "SIGABRT" }, - { SIGBUS, "SIGBUS" }, - { SIGTERM, "SIGTERM" }, - { 0, nullptr } + {SIGSEGV, "SIGSEGV", {}}, + {SIGILL, "SIGILL", {}}, + {SIGFPE, "SIGFPE", {}}, + {SIGABRT, "SIGABRT", {}}, + {SIGBUS, "SIGBUS", {}}, + {SIGTERM, "SIGTERM", {}}, + {0, nullptr, {}}, }; void callPreviousSignalHandler(int signum) { @@ -123,38 +124,22 @@ void callPreviousSignalHandler(int signum) { raise(signum); } -constexpr size_t kDefaultCapacity = 500; - // Note: not thread-safe, but that's okay, as we only let one thread // in our signal handler at a time. // // Leak it so we don't have to worry about destruction order -auto gSignalSafeElfCache = new SignalSafeElfCache(kDefaultCapacity); - -// Buffered writer (using a fixed-size buffer). We try to write only once -// to prevent interleaving with messages written from other threads. -// -// Leak it so we don't have to worry about destruction order. -auto gPrinter = new FDSymbolizePrinter(STDERR_FILENO, - SymbolizePrinter::COLOR_IF_TTY, - size_t(64) << 10); // 64KiB - -// Flush gPrinter, also fsync, in case we're about to crash again... -void flush() { - gPrinter->flush(); - fsyncNoInt(STDERR_FILENO); -} +StackTracePrinter* gStackTracePrinter = new StackTracePrinter(); void printDec(uint64_t val) { char buf[20]; uint32_t n = uint64ToBufferUnsafe(val, buf); - gPrinter->print(StringPiece(buf, n)); + gStackTracePrinter->print(StringPiece(buf, n)); } const char kHexChars[] = "0123456789abcdef"; void printHex(uint64_t val) { // TODO(tudorb): Add this to folly/Conv.h - char buf[2 + 2 * sizeof(uint64_t)]; // "0x" prefix, 2 digits for each byte + char buf[2 + 2 * sizeof(uint64_t)]; // "0x" prefix, 2 digits for each byte char* end = buf + sizeof(buf); char* p = end; @@ -165,15 +150,21 @@ void printHex(uint64_t val) { *--p = 'x'; *--p = '0'; - gPrinter->print(StringPiece(p, end)); + gStackTracePrinter->print(StringPiece(p, end)); } void print(StringPiece sp) { - gPrinter->print(sp); + gStackTracePrinter->print(sp); +} + +void flush() { + gStackTracePrinter->flush(); } void dumpTimeInfo() { - SCOPE_EXIT { flush(); }; + SCOPE_EXIT { + flush(); + }; time_t now = time(nullptr); print("*** Aborted at "); printDec(now); @@ -182,8 +173,162 @@ void dumpTimeInfo() { print("') ***\n"); } +const char* sigill_reason(int si_code) { + switch (si_code) { + case ILL_ILLOPC: + return "illegal opcode"; + case ILL_ILLOPN: + return "illegal operand"; + case ILL_ILLADR: + return "illegal addressing mode"; + case ILL_ILLTRP: + return "illegal trap"; + case ILL_PRVOPC: + return "privileged opcode"; + case ILL_PRVREG: + return "privileged register"; + case ILL_COPROC: + return "coprocessor error"; + case ILL_BADSTK: + return "internal stack error"; + + default: + return nullptr; + } +} + +const char* sigfpe_reason(int si_code) { + switch (si_code) { + case FPE_INTDIV: + return "integer divide by zero"; + case FPE_INTOVF: + return "integer overflow"; + case FPE_FLTDIV: + return "floating-point divide by zero"; + case FPE_FLTOVF: + return "floating-point overflow"; + case FPE_FLTUND: + return "floating-point underflow"; + case FPE_FLTRES: + return "floating-point inexact result"; + case FPE_FLTINV: + return "floating-point invalid operation"; + case FPE_FLTSUB: + return "subscript out of range"; + + default: + return nullptr; + } +} + +const char* sigsegv_reason(int si_code) { + switch (si_code) { + case SEGV_MAPERR: + return "address not mapped to object"; + case SEGV_ACCERR: + return "invalid permissions for mapped object"; + + default: + return nullptr; + } +} + +const char* sigbus_reason(int si_code) { + switch (si_code) { + case BUS_ADRALN: + return "invalid address alignment"; + case BUS_ADRERR: + return "nonexistent physical address"; + case BUS_OBJERR: + return "object-specific hardware error"; + + // MCEERR_AR and MCEERR_AO: in sigaction(2) but not in headers. + + default: + return nullptr; + } +} + +const char* sigtrap_reason(int si_code) { + switch (si_code) { + case TRAP_BRKPT: + return "process breakpoint"; + case TRAP_TRACE: + return "process trace trap"; + + // TRAP_BRANCH and TRAP_HWBKPT: in sigaction(2) but not in headers. + + default: + return nullptr; + } +} + +const char* sigchld_reason(int si_code) { + switch (si_code) { + case CLD_EXITED: + return "child has exited"; + case CLD_KILLED: + return "child was killed"; + case CLD_DUMPED: + return "child terminated abnormally"; + case CLD_TRAPPED: + return "traced child has trapped"; + case CLD_STOPPED: + return "child has stopped"; + case CLD_CONTINUED: + return "stopped child has continued"; + + default: + return nullptr; + } +} + +const char* sigio_reason(int si_code) { + switch (si_code) { + case POLL_IN: + return "data input available"; + case POLL_OUT: + return "output buffers available"; + case POLL_MSG: + return "input message available"; + case POLL_ERR: + return "I/O error"; + case POLL_PRI: + return "high priority input available"; + case POLL_HUP: + return "device disconnected"; + + default: + return nullptr; + } +} + +const char* signal_reason(int signum, int si_code) { + switch (signum) { + case SIGILL: + return sigill_reason(si_code); + case SIGFPE: + return sigfpe_reason(si_code); + case SIGSEGV: + return sigsegv_reason(si_code); + case SIGBUS: + return sigbus_reason(si_code); + case SIGTRAP: + return sigtrap_reason(si_code); + case SIGCHLD: + return sigchld_reason(si_code); + case SIGIO: + return sigio_reason(si_code); // aka SIGPOLL + + default: + return nullptr; + } +} + void dumpSignalInfo(int signum, siginfo_t* siginfo) { - SCOPE_EXIT { flush(); }; + SCOPE_EXIT { + flush(); + }; // Get the signal name, if possible. const char* name = nullptr; for (auto p = kFatalSignals; p->name; ++p) { @@ -205,40 +350,27 @@ void dumpSignalInfo(int signum, siginfo_t* siginfo) { printHex(reinterpret_cast(siginfo->si_addr)); print(") received by PID "); printDec(getpid()); - print(" (TID "); + print(" (pthread TID "); printHex((uint64_t)pthread_self()); - print("), stack trace: ***\n"); -} + print(") (linux TID "); + printDec(syscall(__NR_gettid)); + + // Kernel-sourced signals don't give us useful info for pid/uid. + if (siginfo->si_code != SI_KERNEL) { + print(") (maybe from PID "); + printDec(siginfo->si_pid); + print(", UID "); + printDec(siginfo->si_uid); + } -FOLLY_NOINLINE void dumpStackTrace(bool symbolize); - -void dumpStackTrace(bool symbolize) { - SCOPE_EXIT { flush(); }; - // Get and symbolize stack trace - constexpr size_t kMaxStackTraceDepth = 100; - FrameArray addresses; - - // Skip the getStackTrace frame - if (!getStackTraceSafe(addresses)) { - print("(error retrieving stack trace)\n"); - } else if (symbolize) { - Symbolizer symbolizer(gSignalSafeElfCache); - symbolizer.symbolize(addresses); - - // Skip the top 2 frames: - // getStackTraceSafe - // dumpStackTrace (here) - // - // Leaving signalHandler on the stack for clarity, I think. - gPrinter->println(addresses, 2); - } else { - print("(safe mode, symbolizer not available)\n"); - AddressFormatter formatter; - for (ssize_t i = 0; i < addresses.frameCount; ++i) { - print(formatter.format(addresses.addresses[i])); - print("\n"); - } + auto reason = signal_reason(signum, siginfo->si_code); + + if (reason != nullptr) { + print(") (code: "); + print(reason); } + + print("), stack trace: ***\n"); } // On Linux, pthread_t is a pointer, so 0 is an invalid value, which we @@ -251,7 +383,7 @@ std::atomic gSignalThread(kInvalidThreadId); std::atomic gInRecursiveSignalHandler(false); // Here be dragons. -void innerSignalHandler(int signum, siginfo_t* info, void* uctx) { +void innerSignalHandler(int signum, siginfo_t* info, void* /* uctx */) { // First, let's only let one thread in here at a time. pthread_t myId = pthread_self(); @@ -263,7 +395,7 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) { // next time around. if (!gInRecursiveSignalHandler.exchange(true)) { print("Entered fatal signal handler recursively. We're in trouble.\n"); - dumpStackTrace(false); // no symbolization + gStackTracePrinter->printStackTrace(false); // no symbolization } return; } @@ -271,7 +403,7 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) { // Wait a while, try again. timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 100L * 1000 * 1000; // 100ms + ts.tv_nsec = 100L * 1000 * 1000; // 100ms nanosleep(&ts, nullptr); prevSignalThread = kInvalidThreadId; @@ -279,14 +411,16 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) { dumpTimeInfo(); dumpSignalInfo(signum, info); - dumpStackTrace(true); // with symbolization + gStackTracePrinter->printStackTrace(true); // with symbolization // Run user callbacks gFatalSignalCallbackRegistry->run(); } void signalHandler(int signum, siginfo_t* info, void* uctx) { - SCOPE_EXIT { flush(); }; + SCOPE_EXIT { + flush(); + }; innerSignalHandler(signum, info, uctx); gSignalThread = kInvalidThreadId; @@ -294,7 +428,7 @@ void signalHandler(int signum, siginfo_t* info, void* uctx) { callPreviousSignalHandler(signum); } -} // namespace +} // namespace void addFatalSignalCallback(SignalCallback cb) { gFatalSignalCallbackRegistry->add(cb); @@ -308,7 +442,7 @@ namespace { std::atomic gAlreadyInstalled; -} // namespace +} // namespace void installFatalSignalHandler() { if (gAlreadyInstalled.exchange(true)) { @@ -319,12 +453,17 @@ void installFatalSignalHandler() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); - sa.sa_flags |= SA_SIGINFO; + // By default signal handlers are run on the signaled thread's stack. + // In case of stack overflow running the SIGSEGV signal handler on + // the same stack leads to another SIGSEGV and crashes the program. + // Use SA_ONSTACK, so alternate stack is used (only if configured via + // sigaltstack). + sa.sa_flags |= SA_SIGINFO | SA_ONSTACK; sa.sa_sigaction = &signalHandler; for (auto p = kFatalSignals; p->name; ++p) { CHECK_ERR(sigaction(p->number, &sa, &p->oldAction)); } } - -}} // namespaces +} // namespace symbolizer +} // namespace folly