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