2 * Copyright 2015 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // This is heavily inspired by the signal handler from google-glog
19 #include <folly/experimental/symbolizer/SignalHandler.h>
21 #include <sys/types.h>
22 #include <sys/syscall.h>
31 #include <glog/logging.h>
33 #include <folly/Conv.h>
34 #include <folly/FileUtil.h>
35 #include <folly/Portability.h>
36 #include <folly/ScopeGuard.h>
37 #include <folly/experimental/symbolizer/Symbolizer.h>
39 namespace folly { namespace symbolizer {
44 * Fatal signal handler registry.
46 class FatalSignalCallbackRegistry {
48 FatalSignalCallbackRegistry();
50 void add(SignalCallback func);
55 std::atomic<bool> installed_;
57 std::vector<SignalCallback> handlers_;
60 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
64 void FatalSignalCallbackRegistry::add(SignalCallback func) {
65 std::lock_guard<std::mutex> lock(mutex_);
67 << "FatalSignalCallbackRegistry::add may not be used "
68 "after installing the signal handlers.";
69 handlers_.push_back(func);
72 void FatalSignalCallbackRegistry::markInstalled() {
73 std::lock_guard<std::mutex> lock(mutex_);
74 CHECK(!installed_.exchange(true))
75 << "FatalSignalCallbackRegistry::markInstalled must be called "
79 void FatalSignalCallbackRegistry::run() {
84 for (auto& fn : handlers_) {
89 // Leak it so we don't have to worry about destruction order
90 FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry =
91 new FatalSignalCallbackRegistry;
96 struct sigaction oldAction;
98 { SIGSEGV, "SIGSEGV" },
100 { SIGFPE, "SIGFPE" },
101 { SIGABRT, "SIGABRT" },
102 { SIGBUS, "SIGBUS" },
103 { SIGTERM, "SIGTERM" },
107 void callPreviousSignalHandler(int signum) {
108 // Restore disposition to old disposition, then kill ourselves with the same
109 // signal. The signal will be blocked until we return from our handler,
110 // then it will invoke the default handler and abort.
111 for (auto p = kFatalSignals; p->name; ++p) {
112 if (p->number == signum) {
113 sigaction(signum, &p->oldAction, nullptr);
119 // Not one of the signals we know about. Oh well. Reset to default.
121 memset(&sa, 0, sizeof(sa));
122 sa.sa_handler = SIG_DFL;
123 sigaction(signum, &sa, nullptr);
127 constexpr size_t kDefaultCapacity = 500;
129 // Note: not thread-safe, but that's okay, as we only let one thread
130 // in our signal handler at a time.
132 // Leak it so we don't have to worry about destruction order
133 auto gSignalSafeElfCache = new SignalSafeElfCache(kDefaultCapacity);
135 // Buffered writer (using a fixed-size buffer). We try to write only once
136 // to prevent interleaving with messages written from other threads.
138 // Leak it so we don't have to worry about destruction order.
139 auto gPrinter = new FDSymbolizePrinter(STDERR_FILENO,
140 SymbolizePrinter::COLOR_IF_TTY,
141 size_t(64) << 10); // 64KiB
143 // Flush gPrinter, also fsync, in case we're about to crash again...
146 fsyncNoInt(STDERR_FILENO);
149 void printDec(uint64_t val) {
151 uint32_t n = uint64ToBufferUnsafe(val, buf);
152 gPrinter->print(StringPiece(buf, n));
155 const char kHexChars[] = "0123456789abcdef";
156 void printHex(uint64_t val) {
157 // TODO(tudorb): Add this to folly/Conv.h
158 char buf[2 + 2 * sizeof(uint64_t)]; // "0x" prefix, 2 digits for each byte
160 char* end = buf + sizeof(buf);
163 *--p = kHexChars[val & 0x0f];
169 gPrinter->print(StringPiece(p, end));
172 void print(StringPiece sp) {
176 void dumpTimeInfo() {
177 SCOPE_EXIT { flush(); };
178 time_t now = time(nullptr);
179 print("*** Aborted at ");
181 print(" (Unix time, try 'date -d @");
186 void dumpSignalInfo(int signum, siginfo_t* siginfo) {
187 SCOPE_EXIT { flush(); };
188 // Get the signal name, if possible.
189 const char* name = nullptr;
190 for (auto p = kFatalSignals; p->name; ++p) {
191 if (p->number == signum) {
197 print("*** Signal ");
206 printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
207 print(") received by PID ");
209 print(" (pthread TID ");
210 printHex((uint64_t)pthread_self());
211 print(") (linux TID ");
212 printDec(syscall(__NR_gettid));
213 print("), stack trace: ***\n");
216 FOLLY_NOINLINE void dumpStackTrace(bool symbolize);
218 void dumpStackTrace(bool symbolize) {
219 SCOPE_EXIT { flush(); };
220 // Get and symbolize stack trace
221 constexpr size_t kMaxStackTraceDepth = 100;
222 FrameArray<kMaxStackTraceDepth> addresses;
224 // Skip the getStackTrace frame
225 if (!getStackTraceSafe(addresses)) {
226 print("(error retrieving stack trace)\n");
227 } else if (symbolize) {
228 Symbolizer symbolizer(gSignalSafeElfCache);
229 symbolizer.symbolize(addresses);
231 // Skip the top 2 frames:
233 // dumpStackTrace (here)
235 // Leaving signalHandler on the stack for clarity, I think.
236 gPrinter->println(addresses, 2);
238 print("(safe mode, symbolizer not available)\n");
239 AddressFormatter formatter;
240 for (size_t i = 0; i < addresses.frameCount; ++i) {
241 print(formatter.format(addresses.addresses[i]));
247 // On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
248 // take to indicate "no thread in the signal handler".
250 // POSIX defines PTHREAD_NULL for this purpose, but that's not available.
251 constexpr pthread_t kInvalidThreadId = 0;
253 std::atomic<pthread_t> gSignalThread(kInvalidThreadId);
254 std::atomic<bool> gInRecursiveSignalHandler(false);
257 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
258 // First, let's only let one thread in here at a time.
259 pthread_t myId = pthread_self();
261 pthread_t prevSignalThread = kInvalidThreadId;
262 while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {
263 if (pthread_equal(prevSignalThread, myId)) {
264 // First time here. Try to dump the stack trace without symbolization.
265 // If we still fail, well, we're mightily screwed, so we do nothing the
267 if (!gInRecursiveSignalHandler.exchange(true)) {
268 print("Entered fatal signal handler recursively. We're in trouble.\n");
269 dumpStackTrace(false); // no symbolization
274 // Wait a while, try again.
277 ts.tv_nsec = 100L * 1000 * 1000; // 100ms
278 nanosleep(&ts, nullptr);
280 prevSignalThread = kInvalidThreadId;
284 dumpSignalInfo(signum, info);
285 dumpStackTrace(true); // with symbolization
287 // Run user callbacks
288 gFatalSignalCallbackRegistry->run();
291 void signalHandler(int signum, siginfo_t* info, void* uctx) {
292 SCOPE_EXIT { flush(); };
293 innerSignalHandler(signum, info, uctx);
295 gSignalThread = kInvalidThreadId;
296 // Kill ourselves with the previous handler.
297 callPreviousSignalHandler(signum);
302 void addFatalSignalCallback(SignalCallback cb) {
303 gFatalSignalCallbackRegistry->add(cb);
306 void installFatalSignalCallbacks() {
307 gFatalSignalCallbackRegistry->markInstalled();
312 std::atomic<bool> gAlreadyInstalled;
316 void installFatalSignalHandler() {
317 if (gAlreadyInstalled.exchange(true)) {
323 memset(&sa, 0, sizeof(sa));
324 sigemptyset(&sa.sa_mask);
325 sa.sa_flags |= SA_SIGINFO;
326 sa.sa_sigaction = &signalHandler;
328 for (auto p = kFatalSignals; p->name; ++p) {
329 CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));