c2fd2b9f566d2d6df997c0e4f297324a7014c382
[folly.git] / folly / experimental / exception_tracer / ExceptionTracer.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
18 #include "folly/experimental/exception_tracer/ExceptionTracer.h"
19
20 #include <dlfcn.h>
21 #include <exception>
22 #include <iostream>
23 #include <glog/logging.h>
24
25 #include "folly/experimental/exception_tracer/ExceptionAbi.h"
26 #include "folly/experimental/exception_tracer/StackTrace.h"
27 #include "folly/experimental/symbolizer/Symbolizer.h"
28 #include "folly/String.h"
29
30 namespace {
31
32 using namespace ::folly::exception_tracer;
33 using namespace ::folly::symbolizer;
34 using namespace __cxxabiv1;
35
36 extern "C" {
37 StackTraceStack* getExceptionStackTraceStack(void) __attribute__((weak));
38 typedef StackTraceStack* (*GetExceptionStackTraceStackType)(void);
39 GetExceptionStackTraceStackType getExceptionStackTraceStackFn;
40 }
41
42 }  // namespace
43
44 namespace folly {
45 namespace exception_tracer {
46
47 std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
48   out << "Exception type: ";
49   if (info.type) {
50     out << folly::demangle(*info.type);
51   } else {
52     out << "(unknown type)";
53   }
54   out << " (" << info.frames.size()
55       << (info.frames.size() == 1 ? " frame" : " frames")
56       << ")\n";
57   try {
58     ssize_t frameCount = info.frames.size();
59     // Skip our own internal frames
60     static constexpr size_t skip = 3;
61
62     if (frameCount > skip) {
63       auto addresses = info.frames.data() + skip;
64       frameCount -= skip;
65
66       std::vector<SymbolizedFrame> frames;
67       frames.resize(frameCount);
68
69       Symbolizer symbolizer;
70       symbolizer.symbolize(addresses, frames.data(), frameCount);
71
72       OStreamSymbolizePrinter osp(out);
73       osp.println(addresses, frames.data(), frameCount);
74     }
75   } catch (const std::exception& e) {
76     out << "\n !! caught " << folly::exceptionStr(e) << "\n";
77   } catch (...) {
78     out << "\n !!! caught unexpected exception\n";
79   }
80   return out;
81 }
82
83 namespace {
84
85 /**
86  * Is this a standard C++ ABI exception?
87  *
88  * Dependent exceptions (thrown via std::rethrow_exception) aren't --
89  * exc doesn't actually point to a __cxa_exception structure, but
90  * the offset of unwindHeader is correct, so exc->unwindHeader actually
91  * returns a _Unwind_Exception object.  Yeah, it's ugly like that.
92  */
93 bool isAbiCppException(const __cxa_exception* exc) {
94   // The least significant four bytes must be "C++\0"
95   static const uint64_t cppClass =
96     ((uint64_t)'C' << 24) |
97     ((uint64_t)'+' << 16) |
98     ((uint64_t)'+' << 8);
99   return (exc->unwindHeader.exception_class & 0xffffffff) == cppClass;
100 }
101
102 }  // namespace
103
104 std::vector<ExceptionInfo> getCurrentExceptions() {
105   struct Once {
106     Once() {
107       // See if linked in with us (getExceptionStackTraceStack is weak)
108       getExceptionStackTraceStackFn = getExceptionStackTraceStack;
109
110       if (!getExceptionStackTraceStackFn) {
111         // Nope, see if it's in a shared library
112         getExceptionStackTraceStackFn =
113           (GetExceptionStackTraceStackType)dlsym(
114               RTLD_NEXT, "getExceptionStackTraceStack");
115       }
116     }
117   };
118   static Once once;
119
120   std::vector<ExceptionInfo> exceptions;
121   auto currentException = __cxa_get_globals()->caughtExceptions;
122   if (!currentException) {
123     return exceptions;
124   }
125
126   StackTraceStack* traceStack = nullptr;
127   if (!getExceptionStackTraceStackFn) {
128     static bool logged = false;
129     if (!logged) {
130       LOG(WARNING)
131         << "Exception tracer library not linked, stack traces not available";
132       logged = true;
133     }
134   } else if ((traceStack = getExceptionStackTraceStackFn()) == nullptr) {
135     static bool logged = false;
136     if (!logged) {
137       LOG(WARNING)
138         << "Exception stack trace invalid, stack traces not available";
139       logged = true;
140     }
141   }
142
143   StackTrace* trace = traceStack ? traceStack->top() : nullptr;
144   while (currentException) {
145     ExceptionInfo info;
146     // Dependent exceptions (thrown via std::rethrow_exception) aren't
147     // standard ABI __cxa_exception objects, and are correctly labeled as
148     // such in the exception_class field.  We could try to extract the
149     // primary exception type in horribly hacky ways, but, for now, NULL.
150     info.type =
151       isAbiCppException(currentException) ?
152       currentException->exceptionType :
153       nullptr;
154     if (traceStack) {
155       CHECK(trace) << "Invalid trace stack!";
156       info.frames.assign(trace->addresses,
157                          trace->addresses + trace->frameCount);
158       trace = traceStack->next(trace);
159     }
160     currentException = currentException->nextException;
161     exceptions.push_back(std::move(info));
162   }
163   CHECK(!trace) << "Invalid trace stack!";
164
165   return exceptions;
166 }
167
168 namespace {
169
170 std::terminate_handler origTerminate = abort;
171 std::unexpected_handler origUnexpected = abort;
172
173 void dumpExceptionStack(const char* prefix) {
174   auto exceptions = getCurrentExceptions();
175   if (exceptions.empty()) {
176     return;
177   }
178   LOG(ERROR) << prefix << ", exception stack follows";
179   for (auto& exc : exceptions) {
180     LOG(ERROR) << exc << "\n";
181   }
182   LOG(ERROR) << "exception stack complete";
183 }
184
185 void terminateHandler() {
186   dumpExceptionStack("terminate() called");
187   origTerminate();
188 }
189
190 void unexpectedHandler() {
191   dumpExceptionStack("Unexpected exception");
192   origUnexpected();
193 }
194
195 }  // namespace
196
197 void installHandlers() {
198   struct Once {
199     Once() {
200       origTerminate = std::set_terminate(terminateHandler);
201       origUnexpected = std::set_unexpected(unexpectedHandler);
202     }
203   };
204   static Once once;
205 }
206
207 }  // namespace exception_tracer
208 }  // namespace folly
209