Print (2 more) if stack trace truncated
[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   FrameArray<kMaxStackTraceDepth> addresses;
197
198   // Skip the getStackTrace frame
199   if (!getStackTrace(addresses)) {
200     print("(error retrieving stack trace)\n");
201   } else {
202     Symbolizer symbolizer;
203     symbolizer.symbolize(addresses);
204
205     FDSymbolizePrinter printer(STDERR_FILENO);
206     printer.print(addresses);
207   }
208 }
209
210 std::atomic<pthread_t*> gSignalThread;
211
212 // Here be dragons.
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();
216
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");
221       return;
222     }
223
224     // Wait a while, try again.
225     timespec ts;
226     ts.tv_sec = 0;
227     ts.tv_nsec = 100L * 1000 * 1000;  // 100ms
228     nanosleep(&ts, nullptr);
229
230     prevSignalThread = nullptr;
231   }
232
233   dumpTimeInfo();
234   dumpSignalInfo(signum, info);
235   dumpStackTrace();
236
237   // Run user callbacks
238   gFatalSignalCallbackRegistry->run();
239 }
240
241 void signalHandler(int signum, siginfo_t* info, void* uctx) {
242   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
243   try {
244     innerSignalHandler(signum, info, uctx);
245   } catch (...) {
246     // Ignore any exceptions. What? Exceptions?
247     print("Exception in innerSignalHandler!\n");
248   }
249
250   gSignalThread = nullptr;
251   // Kill ourselves with the previous handler.
252   callPreviousSignalHandler(signum);
253 }
254
255 }  // namespace
256
257 void addFatalSignalCallback(std::function<void()> handler) {
258   gFatalSignalCallbackRegistry->add(std::move(handler));
259 }
260
261 namespace {
262
263 std::atomic<bool> gAlreadyInstalled;
264
265 }  // namespace
266
267 void installFatalSignalHandler() {
268   if (gAlreadyInstalled.exchange(true)) {
269     // Already done.
270     return;
271   }
272
273   gFatalSignalCallbackRegistry->markInstalled();
274
275   struct sigaction sa;
276   memset(&sa, 0, sizeof(sa));
277   sigemptyset(&sa.sa_mask);
278   sa.sa_flags |= SA_SIGINFO;
279   sa.sa_sigaction = &signalHandler;
280
281   for (auto p = kFatalSignals; p->name; ++p) {
282     CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));
283   }
284 }
285
286 }}  // namespaces
287