2 * Copyright 2013 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 typedef std::function<void()> Func;
49 FatalSignalCallbackRegistry();
56 std::atomic<bool> installed_;
58 std::vector<Func> handlers_;
61 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
65 void FatalSignalCallbackRegistry::add(Func func) {
66 std::lock_guard<std::mutex> lock(mutex_);
68 << "FatalSignalCallbackRegistry::add may not be used "
69 "after installing the signal handlers.";
70 handlers_.push_back(std::move(func));
73 void FatalSignalCallbackRegistry::markInstalled() {
74 std::lock_guard<std::mutex> lock(mutex_);
75 CHECK(!installed_.exchange(true))
76 << "FatalSignalCallbackRegistry::markInstalled must be called "
80 void FatalSignalCallbackRegistry::run() {
82 return; // Shouldn't happen
85 for (auto& fn : handlers_) {
90 // Leak it so we don't have to worry about destruction order
91 FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry =
92 new FatalSignalCallbackRegistry;
97 struct sigaction oldAction;
99 { SIGSEGV, "SIGSEGV" },
100 { SIGILL, "SIGILL" },
101 { SIGFPE, "SIGFPE" },
102 { SIGABRT, "SIGABRT" },
103 { SIGBUS, "SIGBUS" },
104 { SIGTERM, "SIGTERM" },
108 void callPreviousSignalHandler(int signum) {
109 // Restore disposition to old disposition, then kill ourselves with the same
110 // signal. The signal will be blocked until we return from our handler,
111 // then it will invoke the default handler and abort.
112 for (auto p = kFatalSignals; p->name; ++p) {
113 if (p->number == signum) {
114 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 void printDec(uint64_t val) {
129 uint32_t n = uint64ToBufferUnsafe(val, buf);
130 writeFull(STDERR_FILENO, buf, n);
133 const char kHexChars[] = "0123456789abcdef";
134 void printHex(uint64_t val) {
135 // TODO(tudorb): Add this to folly/Conv.h
136 char buf[2 + 2 * sizeof(uint64_t)]; // "0x" prefix, 2 digits for each byte
138 char* end = buf + sizeof(buf);
141 *--p = kHexChars[val & 0x0f];
147 writeFull(STDERR_FILENO, p, end - p);
150 void print(StringPiece sp) {
151 writeFull(STDERR_FILENO, sp.data(), sp.size());
154 void dumpTimeInfo() {
155 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
156 time_t now = time(nullptr);
157 print("*** Aborted at ");
159 print(" (Unix time, try 'date -d @");
164 void dumpSignalInfo(int signum, siginfo_t* siginfo) {
165 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
166 // Get the signal name, if possible.
167 const char* name = nullptr;
168 for (auto p = kFatalSignals; p->name; ++p) {
169 if (p->number == signum) {
175 print("*** Signal ");
184 printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
185 print(") received by PID ");
188 printHex((uint64_t)pthread_self());
189 print("), stack trace: ***\n");
192 void dumpStackTrace() {
193 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
194 // Get and symbolize stack trace
195 constexpr size_t kMaxStackTraceDepth = 100;
196 FrameArray<kMaxStackTraceDepth> addresses;
198 // Skip the getStackTrace frame
199 if (!getStackTrace(addresses)) {
200 print("(error retrieving stack trace)\n");
202 Symbolizer symbolizer;
203 symbolizer.symbolize(addresses);
205 FDSymbolizePrinter printer(STDERR_FILENO);
206 printer.print(addresses);
210 std::atomic<pthread_t*> gSignalThread;
213 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
214 // First, let's only let one thread in here at a time.
215 pthread_t myId = pthread_self();
217 pthread_t* prevSignalThread = nullptr;
218 while (!gSignalThread.compare_exchange_strong(prevSignalThread, &myId)) {
219 if (pthread_equal(*prevSignalThread, myId)) {
220 print("Entered fatal signal handler recursively. We're in trouble.\n");
224 // Wait a while, try again.
227 ts.tv_nsec = 100L * 1000 * 1000; // 100ms
228 nanosleep(&ts, nullptr);
230 prevSignalThread = nullptr;
234 dumpSignalInfo(signum, info);
237 // Run user callbacks
238 gFatalSignalCallbackRegistry->run();
241 void signalHandler(int signum, siginfo_t* info, void* uctx) {
242 SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
244 innerSignalHandler(signum, info, uctx);
246 // Ignore any exceptions. What? Exceptions?
247 print("Exception in innerSignalHandler!\n");
250 gSignalThread = nullptr;
251 // Kill ourselves with the previous handler.
252 callPreviousSignalHandler(signum);
257 void addFatalSignalCallback(std::function<void()> handler) {
258 gFatalSignalCallbackRegistry->add(std::move(handler));
263 std::atomic<bool> gAlreadyInstalled;
267 void installFatalSignalHandler() {
268 if (gAlreadyInstalled.exchange(true)) {
273 gFatalSignalCallbackRegistry->markInstalled();
276 memset(&sa, 0, sizeof(sa));
277 sigemptyset(&sa.sa_mask);
278 sa.sa_flags |= SA_SIGINFO;
279 sa.sa_sigaction = &signalHandler;
281 for (auto p = kFatalSignals; p->name; ++p) {
282 CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));