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