f66d013373aa47cda6a9d42c442ff29c39609d73
[folly.git] / folly / experimental / symbolizer / SignalHandler.cpp
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // This is heavily inspired by the signal handler from google-glog
18
19 #include "folly/experimental/symbolizer/SignalHandler.h"
20
21 #include <sys/types.h>
22 #include <atomic>
23 #include <ctime>
24 #include <mutex>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <vector>
29
30 #include <glog/logging.h>
31
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"
37
38 namespace folly { namespace symbolizer {
39
40 namespace {
41
42 /**
43  * Fatal signal handler registry.
44  */
45 class FatalSignalCallbackRegistry {
46  public:
47   typedef std::function<void()> Func;
48
49   FatalSignalCallbackRegistry();
50
51   void add(Func func);
52   void markInstalled();
53   void run();
54
55  private:
56   std::atomic<bool> installed_;
57   std::mutex mutex_;
58   std::vector<Func> handlers_;
59 };
60
61 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
62   : installed_(false) {
63 }
64
65 void FatalSignalCallbackRegistry::add(Func func) {
66   std::lock_guard<std::mutex> lock(mutex_);
67   CHECK(!installed_)
68     << "FatalSignalCallbackRegistry::add may not be used "
69        "after installing the signal handlers.";
70   handlers_.push_back(std::move(func));
71 }
72
73 void FatalSignalCallbackRegistry::markInstalled() {
74   std::lock_guard<std::mutex> lock(mutex_);
75   CHECK(!installed_.exchange(true))
76     << "FatalSignalCallbackRegistry::markInstalled must be called "
77     << "at most once";
78 }
79
80 void FatalSignalCallbackRegistry::run() {
81   if (!installed_) {
82     return;  // Shouldn't happen
83   }
84
85   for (auto& fn : handlers_) {
86     fn();
87   }
88 }
89
90 // Leak it so we don't have to worry about destruction order
91 FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry =
92   new FatalSignalCallbackRegistry;
93
94 struct {
95   int number;
96   const char* name;
97   struct sigaction oldAction;
98 } kFatalSignals[] = {
99   { SIGSEGV, "SIGSEGV" },
100   { SIGILL,  "SIGILL"  },
101   { SIGFPE,  "SIGFPE"  },
102   { SIGABRT, "SIGABRT" },
103   { SIGBUS,  "SIGBUS"  },
104   { SIGTERM, "SIGTERM" },
105   { 0,       nullptr   }
106 };
107
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);
115       return;
116     }
117   }
118
119   // Not one of the signals we know about. Oh well. Reset to default.
120   struct sigaction sa;
121   memset(&sa, 0, sizeof(sa));
122   sa.sa_handler = SIG_DFL;
123   sigaction(signum, &sa, nullptr);
124   raise(signum);
125 }
126
127 void printDec(uint64_t val) {
128   char buf[20];
129   uint32_t n = uint64ToBufferUnsafe(val, buf);
130   writeFull(STDERR_FILENO, buf, n);
131 }
132
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
137
138   char* end = buf + sizeof(buf);
139   char* p = end;
140   do {
141     *--p = kHexChars[val & 0x0f];
142     val >>= 4;
143   } while (val != 0);
144   *--p = 'x';
145   *--p = '0';
146
147   writeFull(STDERR_FILENO, p, end - p);
148 }
149
150 void print(StringPiece sp) {
151   writeFull(STDERR_FILENO, sp.data(), sp.size());
152 }
153
154 void dumpTimeInfo() {
155   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
156   time_t now = time(nullptr);
157   print("*** Aborted at ");
158   printDec(now);
159   print(" (Unix time, try 'date -d @");
160   printDec(now);
161   print("') ***\n");
162 }
163
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) {
170       name = p->name;
171       break;
172     }
173   }
174
175   print("*** Signal ");
176   printDec(signum);
177   if (name) {
178     print(" (");
179     print(name);
180     print(")");
181   }
182
183   print(" (");
184   printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
185   print(") received by PID ");
186   printDec(getpid());
187   print(" (TID ");
188   printHex((uint64_t)pthread_self());
189   print("), stack trace: ***\n");
190 }
191
192 void dumpStackTrace() {
193   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
194   // Get and symbolize stack trace
195   constexpr size_t kMaxStackTraceDepth = 100;
196   AddressInfo addresses[kMaxStackTraceDepth];
197
198   // Skip the getStackTrace frame
199   ssize_t stackTraceDepth = getStackTrace(addresses, kMaxStackTraceDepth, 1);
200   if (stackTraceDepth < 0) {
201     print("(error retrieving stack trace)\n");
202   } else {
203     Symbolizer symbolizer;
204     symbolizer.symbolize(addresses, stackTraceDepth);
205
206     FDSymbolizePrinter printer(STDERR_FILENO);
207     printer.print(addresses, stackTraceDepth);
208   }
209 }
210
211 std::atomic<pthread_t*> gSignalThread;
212
213 // Here be dragons.
214 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
215   // First, let's only let one thread in here at a time.
216   pthread_t myId = pthread_self();
217
218   pthread_t* prevSignalThread = nullptr;
219   while (!gSignalThread.compare_exchange_strong(prevSignalThread, &myId)) {
220     if (pthread_equal(*prevSignalThread, myId)) {
221       print("Entered fatal signal handler recursively. We're in trouble.\n");
222       return;
223     }
224
225     // Wait a while, try again.
226     timespec ts;
227     ts.tv_sec = 0;
228     ts.tv_nsec = 100L * 1000 * 1000;  // 100ms
229     nanosleep(&ts, nullptr);
230
231     prevSignalThread = nullptr;
232   }
233
234   dumpTimeInfo();
235   dumpSignalInfo(signum, info);
236   dumpStackTrace();
237
238   // Run user callbacks
239   gFatalSignalCallbackRegistry->run();
240 }
241
242 void signalHandler(int signum, siginfo_t* info, void* uctx) {
243   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
244   try {
245     innerSignalHandler(signum, info, uctx);
246   } catch (...) {
247     // Ignore any exceptions. What? Exceptions?
248     print("Exception in innerSignalHandler!\n");
249   }
250
251   gSignalThread = nullptr;
252   // Kill ourselves with the previous handler.
253   callPreviousSignalHandler(signum);
254 }
255
256 }  // namespace
257
258 void addFatalSignalCallback(std::function<void()> handler) {
259   gFatalSignalCallbackRegistry->add(std::move(handler));
260 }
261
262 namespace {
263
264 std::atomic<bool> gAlreadyInstalled;
265
266 }  // namespace
267
268 void installFatalSignalHandler() {
269   if (gAlreadyInstalled.exchange(true)) {
270     // Already done.
271     return;
272   }
273
274   gFatalSignalCallbackRegistry->markInstalled();
275
276   struct sigaction sa;
277   memset(&sa, 0, sizeof(sa));
278   sigemptyset(&sa.sa_mask);
279   sa.sa_flags |= SA_SIGINFO;
280   sa.sa_sigaction = &signalHandler;
281
282   for (auto p = kFatalSignals; p->name; ++p) {
283     CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));
284   }
285 }
286
287 }}  // namespaces
288