Build up signal handler message before writing
[folly.git] / folly / experimental / symbolizer / SignalHandler.cpp
1 /*
2  * Copyright 2014 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   FatalSignalCallbackRegistry();
48
49   void add(SignalCallback func);
50   void markInstalled();
51   void run();
52
53  private:
54   std::atomic<bool> installed_;
55   std::mutex mutex_;
56   std::vector<SignalCallback> handlers_;
57 };
58
59 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
60   : installed_(false) {
61 }
62
63 void FatalSignalCallbackRegistry::add(SignalCallback func) {
64   std::lock_guard<std::mutex> lock(mutex_);
65   CHECK(!installed_)
66     << "FatalSignalCallbackRegistry::add may not be used "
67        "after installing the signal handlers.";
68   handlers_.push_back(func);
69 }
70
71 void FatalSignalCallbackRegistry::markInstalled() {
72   std::lock_guard<std::mutex> lock(mutex_);
73   CHECK(!installed_.exchange(true))
74     << "FatalSignalCallbackRegistry::markInstalled must be called "
75     << "at most once";
76 }
77
78 void FatalSignalCallbackRegistry::run() {
79   if (!installed_) {
80     return;
81   }
82
83   for (auto& fn : handlers_) {
84     fn();
85   }
86 }
87
88 // Leak it so we don't have to worry about destruction order
89 FatalSignalCallbackRegistry* gFatalSignalCallbackRegistry =
90   new FatalSignalCallbackRegistry;
91
92 struct {
93   int number;
94   const char* name;
95   struct sigaction oldAction;
96 } kFatalSignals[] = {
97   { SIGSEGV, "SIGSEGV" },
98   { SIGILL,  "SIGILL"  },
99   { SIGFPE,  "SIGFPE"  },
100   { SIGABRT, "SIGABRT" },
101   { SIGBUS,  "SIGBUS"  },
102   { SIGTERM, "SIGTERM" },
103   { 0,       nullptr   }
104 };
105
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);
113       raise(signum);
114       return;
115     }
116   }
117
118   // Not one of the signals we know about. Oh well. Reset to default.
119   struct sigaction sa;
120   memset(&sa, 0, sizeof(sa));
121   sa.sa_handler = SIG_DFL;
122   sigaction(signum, &sa, nullptr);
123   raise(signum);
124 }
125
126 constexpr size_t kDefaultCapacity = 500;
127
128 // Note: not thread-safe, but that's okay, as we only let one thread
129 // in our signal handler at a time.
130 //
131 // Leak it so we don't have to worry about destruction order
132 auto gSignalSafeElfCache = new SignalSafeElfCache(kDefaultCapacity);
133
134 // Buffered writer (using a fixed-size buffer). We try to write only once
135 // to prevent interleaving with messages written from other threads.
136 //
137 // Leak it so we don't have to worry about destruction order.
138 auto gPrinter = new FDSymbolizePrinter(STDERR_FILENO,
139                                        SymbolizePrinter::COLOR_IF_TTY,
140                                        size_t(64) << 10);  // 64KiB
141
142 // Flush gPrinter, also fsync, in case we're about to crash again...
143 void flush() {
144   gPrinter->flush();
145   fsyncNoInt(STDERR_FILENO);
146 }
147
148 void printDec(uint64_t val) {
149   char buf[20];
150   uint32_t n = uint64ToBufferUnsafe(val, buf);
151   gPrinter->print(StringPiece(buf, n));
152 }
153
154 const char kHexChars[] = "0123456789abcdef";
155 void printHex(uint64_t val) {
156   // TODO(tudorb): Add this to folly/Conv.h
157   char buf[2 + 2 * sizeof(uint64_t)];  // "0x" prefix, 2 digits for each byte
158
159   char* end = buf + sizeof(buf);
160   char* p = end;
161   do {
162     *--p = kHexChars[val & 0x0f];
163     val >>= 4;
164   } while (val != 0);
165   *--p = 'x';
166   *--p = '0';
167
168   gPrinter->print(StringPiece(p, end));
169 }
170
171 void print(StringPiece sp) {
172   gPrinter->print(sp);
173 }
174
175 void dumpTimeInfo() {
176   SCOPE_EXIT { flush(); };
177   time_t now = time(nullptr);
178   print("*** Aborted at ");
179   printDec(now);
180   print(" (Unix time, try 'date -d @");
181   printDec(now);
182   print("') ***\n");
183 }
184
185 void dumpSignalInfo(int signum, siginfo_t* siginfo) {
186   SCOPE_EXIT { flush(); };
187   // Get the signal name, if possible.
188   const char* name = nullptr;
189   for (auto p = kFatalSignals; p->name; ++p) {
190     if (p->number == signum) {
191       name = p->name;
192       break;
193     }
194   }
195
196   print("*** Signal ");
197   printDec(signum);
198   if (name) {
199     print(" (");
200     print(name);
201     print(")");
202   }
203
204   print(" (");
205   printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));
206   print(") received by PID ");
207   printDec(getpid());
208   print(" (TID ");
209   printHex((uint64_t)pthread_self());
210   print("), stack trace: ***\n");
211 }
212
213 FOLLY_NOINLINE void dumpStackTrace(bool symbolize);
214
215 void dumpStackTrace(bool symbolize) {
216   SCOPE_EXIT { flush(); };
217   // Get and symbolize stack trace
218   constexpr size_t kMaxStackTraceDepth = 100;
219   FrameArray<kMaxStackTraceDepth> addresses;
220
221   // Skip the getStackTrace frame
222   if (!getStackTraceSafe(addresses)) {
223     print("(error retrieving stack trace)\n");
224   } else if (symbolize) {
225     Symbolizer symbolizer(gSignalSafeElfCache);
226     symbolizer.symbolize(addresses);
227
228     // Skip the top 2 frames:
229     // getStackTraceSafe
230     // dumpStackTrace (here)
231     //
232     // Leaving signalHandler on the stack for clarity, I think.
233     gPrinter->println(addresses, 2);
234   } else {
235     print("(safe mode, symbolizer not available)\n");
236     AddressFormatter formatter;
237     for (ssize_t i = 0; i < addresses.frameCount; ++i) {
238       print(formatter.format(addresses.addresses[i]));
239       print("\n");
240     }
241   }
242 }
243
244 // On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
245 // take to indicate "no thread in the signal handler".
246 //
247 // POSIX defines PTHREAD_NULL for this purpose, but that's not available.
248 constexpr pthread_t kInvalidThreadId = 0;
249
250 std::atomic<pthread_t> gSignalThread(kInvalidThreadId);
251 std::atomic<bool> gInRecursiveSignalHandler(false);
252
253 // Here be dragons.
254 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
255   // First, let's only let one thread in here at a time.
256   pthread_t myId = pthread_self();
257
258   pthread_t prevSignalThread = kInvalidThreadId;
259   while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {
260     if (pthread_equal(prevSignalThread, myId)) {
261       // First time here. Try to dump the stack trace without symbolization.
262       // If we still fail, well, we're mightily screwed, so we do nothing the
263       // next time around.
264       if (!gInRecursiveSignalHandler.exchange(true)) {
265         print("Entered fatal signal handler recursively. We're in trouble.\n");
266         dumpStackTrace(false);  // no symbolization
267       }
268       return;
269     }
270
271     // Wait a while, try again.
272     timespec ts;
273     ts.tv_sec = 0;
274     ts.tv_nsec = 100L * 1000 * 1000;  // 100ms
275     nanosleep(&ts, nullptr);
276
277     prevSignalThread = kInvalidThreadId;
278   }
279
280   dumpTimeInfo();
281   dumpSignalInfo(signum, info);
282   dumpStackTrace(true);  // with symbolization
283
284   // Run user callbacks
285   gFatalSignalCallbackRegistry->run();
286 }
287
288 void signalHandler(int signum, siginfo_t* info, void* uctx) {
289   SCOPE_EXIT { flush(); };
290   innerSignalHandler(signum, info, uctx);
291
292   gSignalThread = kInvalidThreadId;
293   // Kill ourselves with the previous handler.
294   callPreviousSignalHandler(signum);
295 }
296
297 }  // namespace
298
299 void addFatalSignalCallback(SignalCallback cb) {
300   gFatalSignalCallbackRegistry->add(cb);
301 }
302
303 void installFatalSignalCallbacks() {
304   gFatalSignalCallbackRegistry->markInstalled();
305 }
306
307 namespace {
308
309 std::atomic<bool> gAlreadyInstalled;
310
311 }  // namespace
312
313 void installFatalSignalHandler() {
314   if (gAlreadyInstalled.exchange(true)) {
315     // Already done.
316     return;
317   }
318
319   struct sigaction sa;
320   memset(&sa, 0, sizeof(sa));
321   sigemptyset(&sa.sa_mask);
322   sa.sa_flags |= SA_SIGINFO;
323   sa.sa_sigaction = &signalHandler;
324
325   for (auto p = kFatalSignals; p->name; ++p) {
326     CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));
327   }
328 }
329
330 }}  // namespaces