2 * Copyright 2013 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"
23 #include <glog/logging.h>
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"
32 using namespace ::folly::exception_tracer;
33 using namespace ::folly::symbolizer;
34 using namespace __cxxabiv1;
37 StackTraceStack* getExceptionStackTraceStack(void) __attribute__((weak));
38 typedef StackTraceStack* (*GetExceptionStackTraceStackType)(void);
39 GetExceptionStackTraceStackType getExceptionStackTraceStackFn;
45 namespace exception_tracer {
47 std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
48 out << "Exception type: ";
50 out << folly::demangle(*info.type);
52 out << "(unknown type)";
54 out << " (" << info.frames.size()
55 << (info.frames.size() == 1 ? " frame" : " frames")
58 ssize_t frameCount = info.frames.size();
59 // Skip our own internal frames
60 static constexpr size_t skip = 3;
62 if (frameCount > skip) {
63 auto addresses = info.frames.data() + skip;
66 std::vector<SymbolizedFrame> frames;
67 frames.resize(frameCount);
69 Symbolizer symbolizer;
70 symbolizer.symbolize(addresses, frames.data(), frameCount);
72 OStreamSymbolizePrinter osp(out);
73 osp.println(addresses, frames.data(), frameCount);
75 } catch (const std::exception& e) {
76 out << "\n !! caught " << folly::exceptionStr(e) << "\n";
78 out << "\n !!! caught unexpected exception\n";
86 * Is this a standard C++ ABI exception?
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.
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) |
99 return (exc->unwindHeader.exception_class & 0xffffffff) == cppClass;
104 std::vector<ExceptionInfo> getCurrentExceptions() {
107 // See if linked in with us (getExceptionStackTraceStack is weak)
108 getExceptionStackTraceStackFn = getExceptionStackTraceStack;
110 if (!getExceptionStackTraceStackFn) {
111 // Nope, see if it's in a shared library
112 getExceptionStackTraceStackFn =
113 (GetExceptionStackTraceStackType)dlsym(
114 RTLD_NEXT, "getExceptionStackTraceStack");
120 std::vector<ExceptionInfo> exceptions;
121 auto currentException = __cxa_get_globals()->caughtExceptions;
122 if (!currentException) {
126 StackTraceStack* traceStack = nullptr;
127 if (!getExceptionStackTraceStackFn) {
128 static bool logged = false;
131 << "Exception tracer library not linked, stack traces not available";
134 } else if ((traceStack = getExceptionStackTraceStackFn()) == nullptr) {
135 static bool logged = false;
138 << "Exception stack trace invalid, stack traces not available";
143 StackTrace* trace = traceStack ? traceStack->top() : nullptr;
144 while (currentException) {
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.
151 isAbiCppException(currentException) ?
152 currentException->exceptionType :
155 CHECK(trace) << "Invalid trace stack!";
156 info.frames.assign(trace->addresses,
157 trace->addresses + trace->frameCount);
158 trace = traceStack->next(trace);
160 currentException = currentException->nextException;
161 exceptions.push_back(std::move(info));
163 CHECK(!trace) << "Invalid trace stack!";
170 std::terminate_handler origTerminate = abort;
171 std::unexpected_handler origUnexpected = abort;
173 void dumpExceptionStack(const char* prefix) {
174 auto exceptions = getCurrentExceptions();
175 if (exceptions.empty()) {
178 LOG(ERROR) << prefix << ", exception stack follows";
179 for (auto& exc : exceptions) {
180 LOG(ERROR) << exc << "\n";
182 LOG(ERROR) << "exception stack complete";
185 void terminateHandler() {
186 dumpExceptionStack("terminate() called");
190 void unexpectedHandler() {
191 dumpExceptionStack("Unexpected exception");
197 void installHandlers() {
200 origTerminate = std::set_terminate(terminateHandler);
201 origUnexpected = std::set_unexpected(unexpectedHandler);
207 } // namespace exception_tracer