2 * Copyright 2012 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "folly/experimental/exception_tracer/ExceptionTracer.h"
22 #include <glog/logging.h>
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"
32 const StackTraceStack* getExceptionStackTraceStack(void) __attribute__((weak));
33 typedef const StackTraceStack* (*GetExceptionStackTraceStackType)(void);
34 GetExceptionStackTraceStackType getExceptionStackTraceStackFn;
39 using namespace ::folly::symbolizer;
40 using namespace __cxxabiv1;
43 namespace exception_tracer {
45 std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
46 out << "Exception type: ";
48 out << folly::demangle(*info.type);
50 out << "(unknown type)";
52 out << " (" << info.frames.size()
53 << (info.frames.size() == 1 ? " frame" : " frames")
56 Symbolizer symbolizer;
57 folly::StringPiece symbolName;
58 Dwarf::LocationInfo location;
59 for (auto ip : info.frames) {
60 // Symbolize the previous address because the IP might be in the
61 // next function, per glog/src/signalhandler.cc
62 symbolizer.symbolize(ip-1, symbolName, location);
63 Symbolizer::write(out, ip, symbolName, location);
65 } catch (const std::exception& e) {
66 out << "\n !! caught " << folly::exceptionStr(e) << "\n";
68 out << "\n !!! caught unexpected exception\n";
76 * Is this a standard C++ ABI exception?
78 * Dependent exceptions (thrown via std::rethrow_exception) aren't --
79 * exc doesn't actually point to a __cxa_exception structure, but
80 * the offset of unwindHeader is correct, so exc->unwindHeader actually
81 * returns a _Unwind_Exception object. Yeah, it's ugly like that.
83 bool isAbiCppException(const __cxa_exception* exc) {
84 // The least significant four bytes must be "C++\0"
85 static const uint64_t cppClass =
86 ((uint64_t)'C' << 24) |
87 ((uint64_t)'+' << 16) |
89 return (exc->unwindHeader.exception_class & 0xffffffff) == cppClass;
94 std::vector<ExceptionInfo> getCurrentExceptions() {
97 // See if linked in with us (getExceptionStackTraceStack is weak)
98 getExceptionStackTraceStackFn = getExceptionStackTraceStack;
100 if (!getExceptionStackTraceStackFn) {
101 // Nope, see if it's in a shared library
102 getExceptionStackTraceStackFn =
103 (GetExceptionStackTraceStackType)dlsym(
104 RTLD_NEXT, "getExceptionStackTraceStack");
110 std::vector<ExceptionInfo> exceptions;
111 auto currentException = __cxa_get_globals()->caughtExceptions;
112 if (!currentException) {
116 bool hasTraceStack = false;
117 const StackTraceStack* traceStack = nullptr;
118 if (!getExceptionStackTraceStackFn) {
119 static bool logged = false;
122 << "Exception tracer library not linked, stack traces not available";
125 } else if ((traceStack = getExceptionStackTraceStackFn()) == nullptr) {
126 static bool logged = false;
129 << "Exception stack trace invalid, stack traces not available";
133 hasTraceStack = true;
136 while (currentException) {
138 // Dependent exceptions (thrown via std::rethrow_exception) aren't
139 // standard ABI __cxa_exception objects, and are correctly labeled as
140 // such in the exception_class field. We could try to extract the
141 // primary exception type in horribly hacky ways, but, for now, NULL.
143 isAbiCppException(currentException) ?
144 currentException->exceptionType :
147 CHECK(traceStack) << "Invalid trace stack!";
149 traceStack->trace.frameIPs,
150 traceStack->trace.frameIPs + traceStack->trace.frameCount);
151 traceStack = traceStack->next;
153 currentException = currentException->nextException;
154 exceptions.push_back(std::move(info));
157 CHECK(!traceStack) << "Invalid trace stack!";
164 std::terminate_handler origTerminate = abort;
165 std::unexpected_handler origUnexpected = abort;
167 void dumpExceptionStack(const char* prefix) {
168 auto exceptions = getCurrentExceptions();
169 if (exceptions.empty()) {
172 LOG(ERROR) << prefix << ", exception stack follows";
173 for (auto& exc : exceptions) {
174 LOG(ERROR) << exc << "\n";
176 LOG(ERROR) << "exception stack complete";
179 void terminateHandler() {
180 dumpExceptionStack("terminate() called");
184 void unexpectedHandler() {
185 dumpExceptionStack("Unexpected exception");
191 void installHandlers() {
194 origTerminate = std::set_terminate(terminateHandler);
195 origUnexpected = std::set_unexpected(unexpectedHandler);
201 } // namespace exception_tracer