2 * Copyright 2014 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>
30 #include <glog/logging.h>
32 #include "folly/Conv.h"
33 #include "folly/FileUtil.h"
34 #include "folly/Portability.h"
35 #include "folly/ScopeGuard.h"
36 #include "folly/experimental/symbolizer/Symbolizer.h"
38 namespace folly { namespace symbolizer {
43 * Fatal signal handler registry.
45 class FatalSignalCallbackRegistry {
47 FatalSignalCallbackRegistry();
49 void add(SignalCallback func);
54 std::atomic<bool> installed_;
56 std::vector<SignalCallback> handlers_;
59 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
63 void FatalSignalCallbackRegistry::add(SignalCallback func) {
64 std::lock_guard<std::mutex> lock(mutex_);
66 << "FatalSignalCallbackRegistry::add may not be used "
67 "after installing the signal handlers.";
68 handlers_.push_back(func);
71 void FatalSignalCallbackRegistry::markInstalled() {
72 std::lock_guard<std::mutex> lock(mutex_);
73 CHECK(!installed_.exchange(true))
74 << "FatalSignalCallbackRegistry::markInstalled must be called "
78 void FatalSignalCallbackRegistry::run() {
83 for (auto& fn : handlers_) {
88 // Leak it so we don't have to worry about destruction order
89 FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry =
90 new FatalSignalCallbackRegistry;
95 struct sigaction oldAction;
97 { SIGSEGV, "SIGSEGV" },
100 { SIGABRT, "SIGABRT" },
101 { SIGBUS, "SIGBUS" },
102 { SIGTERM, "SIGTERM" },
106 void callPreviousSignalHandler(int signum) {
107 // Restore disposition to old disposition, then kill ourselves with the same
108 // signal. The signal will be blocked until we return from our handler,
109 // then it will invoke the default handler and abort.
110 for (auto p = kFatalSignals; p->name; ++p) {
111 if (p->number == signum) {
112 sigaction(signum, &p->oldAction, nullptr);
118 // Not one of the signals we know about. Oh well. Reset to default.
120 memset(&sa, 0, sizeof(sa));
121 sa.sa_handler = SIG_DFL;
122 sigaction(signum, &sa, nullptr);
126 void printDec(uint64_t val) {
128 uint32_t n = uint64ToBufferUnsafe(val, buf);
129 writeFull(STDERR_FILENO, buf, n);
132 const char kHexChars[] = "0123456789abcdef";
133 void printHex(uint64_t val) {
134 // TODO(tudorb): Add this to folly/Conv.h
135 char buf[2 + 2 * sizeof(uint64_t)]; // "0x" prefix, 2 digits for each byte
137 char* end = buf + sizeof(buf);
140 *--p = kHexChars[val & 0x0f];
146 writeFull(STDERR_FILENO, p, end - p);
149 void print(StringPiece sp) {
150 writeFull(STDERR_FILENO, sp.data(), sp.size());
153 void dumpTimeInfo() {
154 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
155 time_t now = time(nullptr);
156 print("*** Aborted at ");
158 print(" (Unix time, try 'date -d @");
163 void dumpSignalInfo(int signum, siginfo_t* siginfo) {
164 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
165 // Get the signal name, if possible.
166 const char* name = nullptr;
167 for (auto p = kFatalSignals; p->name; ++p) {
168 if (p->number == signum) {
174 print("*** Signal ");
183 printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
184 print(") received by PID ");
187 printHex((uint64_t)pthread_self());
188 print("), stack trace: ***\n");
192 constexpr size_t kDefaultCapacity = 500;
194 // Note: not thread-safe, but that's okay, as we only let one thread
195 // in our signal handler at a time.
197 // Leak it so we don't have to worry about destruction order
198 auto gSignalSafeElfCache = new SignalSafeElfCache(kDefaultCapacity);
201 void dumpStackTrace(bool symbolize) __attribute__((noinline));
203 void dumpStackTrace(bool symbolize) {
204 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
205 // Get and symbolize stack trace
206 constexpr size_t kMaxStackTraceDepth = 100;
207 FrameArray<kMaxStackTraceDepth> addresses;
209 // Skip the getStackTrace frame
210 if (!getStackTraceSafe(addresses)) {
211 print("(error retrieving stack trace)\n");
212 } else if (symbolize) {
213 Symbolizer symbolizer(gSignalSafeElfCache);
214 symbolizer.symbolize(addresses);
216 FDSymbolizePrinter printer(STDERR_FILENO, SymbolizePrinter::COLOR_IF_TTY);
218 // Skip the top 2 frames:
220 // dumpStackTrace (here)
222 // Leaving signalHandler on the stack for clarity, I think.
223 printer.println(addresses, 2);
225 print("(safe mode, symbolizer not available)\n");
226 AddressFormatter formatter;
227 for (ssize_t i = 0; i < addresses.frameCount; ++i) {
228 print(formatter.format(addresses.addresses[i]));
234 // On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
235 // take to indicate "no thread in the signal handler".
237 // POSIX defines PTHREAD_NULL for this purpose, but that's not available.
238 constexpr pthread_t kInvalidThreadId = 0;
240 std::atomic<pthread_t> gSignalThread(kInvalidThreadId);
241 std::atomic<bool> gInRecursiveSignalHandler(false);
244 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
245 // First, let's only let one thread in here at a time.
246 pthread_t myId = pthread_self();
248 pthread_t prevSignalThread = kInvalidThreadId;
249 while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {
250 if (pthread_equal(prevSignalThread, myId)) {
251 // First time here. Try to dump the stack trace without symbolization.
252 // If we still fail, well, we're mightily screwed, so we do nothing the
254 if (!gInRecursiveSignalHandler.exchange(true)) {
255 print("Entered fatal signal handler recursively. We're in trouble.\n");
256 dumpStackTrace(false); // no symbolization
261 // Wait a while, try again.
264 ts.tv_nsec = 100L * 1000 * 1000; // 100ms
265 nanosleep(&ts, nullptr);
267 prevSignalThread = kInvalidThreadId;
271 dumpSignalInfo(signum, info);
272 dumpStackTrace(true); // with symbolization
274 // Run user callbacks
275 gFatalSignalCallbackRegistry->run();
278 void signalHandler(int signum, siginfo_t* info, void* uctx) {
279 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
280 innerSignalHandler(signum, info, uctx);
282 gSignalThread = kInvalidThreadId;
283 // Kill ourselves with the previous handler.
284 callPreviousSignalHandler(signum);
289 void addFatalSignalCallback(SignalCallback cb) {
290 gFatalSignalCallbackRegistry->add(cb);
293 void installFatalSignalCallbacks() {
294 gFatalSignalCallbackRegistry->markInstalled();
299 std::atomic<bool> gAlreadyInstalled;
303 void installFatalSignalHandler() {
304 if (gAlreadyInstalled.exchange(true)) {
310 memset(&sa, 0, sizeof(sa));
311 sigemptyset(&sa.sa_mask);
312 sa.sa_flags |= SA_SIGINFO;
313 sa.sa_sigaction = &signalHandler;
315 for (auto p = kFatalSignals; p->name; ++p) {
316 CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));