#include <folly/debugging/symbolizer/SignalHandler.h>
#include <signal.h>
#include <sys/types.h>
#include <algorithm>
#include <atomic>
#include <cerrno>
#include <ctime>
#include <mutex>
#include <vector>
#include <glog/logging.h>
#include <folly/ScopeGuard.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <folly/lang/ToAscii.h>
#include <folly/portability/SysSyscall.h>
#include <folly/portability/Unistd.h>
namespace folly {
namespace symbolizer {
#ifndef _WIN32
const unsigned long kAllFatalSignals = …;
#endif
namespace {
class FatalSignalCallbackRegistry { … };
FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
: … { … }
void FatalSignalCallbackRegistry::add(SignalCallback func) { … }
void FatalSignalCallbackRegistry::markInstalled() { … }
void FatalSignalCallbackRegistry::run() { … }
std::atomic<FatalSignalCallbackRegistry*> gFatalSignalCallbackRegistry{ … };
FatalSignalCallbackRegistry* getFatalSignalCallbackRegistry() { … }
}
void addFatalSignalCallback(SignalCallback cb) { … }
void installFatalSignalCallbacks() { … }
#ifndef _WIN32
namespace {
struct { … } kFatalSignals[] = …;
[[maybe_unused]] void callPreviousSignalHandler(int signum) { … }
#if FOLLY_USE_SYMBOLIZER
SafeStackTracePrinter* gStackTracePrinter;
void print(StringPiece sp) {
gStackTracePrinter->print(sp);
}
void flush() {
gStackTracePrinter->flush();
}
void printDec(uint64_t val) {
char buf[to_ascii_size_max_decimal<uint64_t>];
size_t n = to_ascii_decimal(buf, val);
gStackTracePrinter->print(StringPiece(buf, n));
}
void printHex(uint64_t val) {
char buf[2 + to_ascii_size_max<16, uint64_t>];
auto out = buf + 0;
*out++ = '0';
*out++ = 'x';
out += to_ascii_lower<16>(out, buf + sizeof(buf), val);
gStackTracePrinter->print(StringPiece(buf, out - buf));
}
void dumpTimeInfo() {
SCOPE_EXIT {
flush();
};
time_t now = time(nullptr);
print("*** Aborted at ");
printDec(now);
print(" (Unix time, try 'date -d @");
printDec(now);
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";
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";
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);
default:
return nullptr;
}
}
void dumpSignalInfo(int signum, siginfo_t* siginfo) {
SCOPE_EXIT {
flush();
};
const char* name = nullptr;
for (auto p = kFatalSignals; p->name; ++p) {
if (p->number == signum) {
name = p->name;
break;
}
}
print("*** Signal ");
printDec(signum);
if (name) {
print(" (");
print(name);
print(")");
}
print(" (");
printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
print(") received by PID ");
printDec(getpid());
print(" (pthread TID ");
printHex((uint64_t)pthread_self());
#if defined(__linux__)
print(") (linux TID ");
printDec(syscall(__NR_gettid));
#elif defined(__FreeBSD__)
long tid = 0;
syscall(432, &tid);
print(") (freebsd TID ");
printDec(tid);
#endif
if (siginfo->si_code <= 0) {
print(") (maybe from PID ");
printDec(siginfo->si_pid);
print(", UID ");
printDec(siginfo->si_uid);
}
auto reason = signal_reason(signum, siginfo->si_code);
print(") (code: ");
if (reason != nullptr) {
print(reason);
} else {
if (siginfo->si_code < 0) {
print("-");
printDec(-siginfo->si_code);
} else {
printDec(siginfo->si_code);
}
}
print("), stack trace: ***\n");
}
constexpr pthread_t kInvalidThreadId = 0;
std::atomic<pthread_t> gSignalThread(kInvalidThreadId);
std::atomic<bool> gInRecursiveSignalHandler(false);
void innerSignalHandler(int signum, siginfo_t* info, void* ) {
pthread_t myId = pthread_self();
pthread_t prevSignalThread = kInvalidThreadId;
while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {
if (pthread_equal(prevSignalThread, myId)) {
if (!gInRecursiveSignalHandler.exchange(true)) {
print("Entered fatal signal handler recursively. We're in trouble.\n");
gStackTracePrinter->printStackTrace(false);
}
return;
}
timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100L * 1000 * 1000;
nanosleep(&ts, nullptr);
prevSignalThread = kInvalidThreadId;
}
dumpTimeInfo();
dumpSignalInfo(signum, info);
gStackTracePrinter->printStackTrace(true);
auto callbacks = gFatalSignalCallbackRegistry.load(std::memory_order_acquire);
if (callbacks) {
callbacks->run();
}
}
namespace {
std::atomic<bool> gFatalSignalReceived{false};
}
void signalHandler(int signum, siginfo_t* info, void* uctx) {
gFatalSignalReceived.store(true, std::memory_order_relaxed);
int savedErrno = errno;
SCOPE_EXIT {
flush();
errno = savedErrno;
};
innerSignalHandler(signum, info, uctx);
gSignalThread = kInvalidThreadId;
callPreviousSignalHandler(signum);
}
#endif
constexpr size_t kSmallSigAltStackSize = …;
[[maybe_unused]] bool isSmallSigAltStackEnabled() { … }
}
#endif
namespace {
std::atomic<bool> gAlreadyInstalled;
}
void installFatalSignalHandler(std::bitset<64> signals) { … }
bool fatalSignalReceived() { … }
}
}