1/*
2 * Copyright 2016-present 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#include <exception>
18
19#include <folly/experimental/exception_tracer/ExceptionAbi.h>
20#include <folly/experimental/exception_tracer/ExceptionTracer.h>
21#include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
22#include <folly/experimental/exception_tracer/StackTrace.h>
23#include <folly/experimental/symbolizer/Symbolizer.h>
24
25using namespace folly::exception_tracer;
26
27namespace {
28
29// If we somehow ended up in an invalid state, we don't want to print any stack
30// trace at all because in could be bogus
31FOLLY_TLS bool invalid;
32
33FOLLY_TLS StackTraceStack activeExceptions;
34FOLLY_TLS StackTraceStack caughtExceptions;
35
36} // namespace
37
38// This function is exported and may be found via dlsym(RTLD_NEXT, ...)
39extern "C" StackTraceStack* getExceptionStackTraceStack() {
40 return invalid ? nullptr : &caughtExceptions;
41}
42
43namespace {
44
45void addActiveException() {
46 // Capture stack trace
47 if (!invalid) {
48 if (!activeExceptions.pushCurrent()) {
49 activeExceptions.clear();
50 caughtExceptions.clear();
51 invalid = true;
52 }
53 }
54}
55
56void moveTopException(StackTraceStack& from, StackTraceStack& to) {
57 if (invalid) {
58 return;
59 }
60 if (!to.moveTopFrom(from)) {
61 from.clear();
62 to.clear();
63 invalid = true;
64 }
65}
66
67struct Initializer {
68 Initializer() {
69 registerCxaThrowCallback(
70 [](void*, std::type_info*, void (*)(void*)) noexcept {
71 addActiveException();
72 });
73
74 registerCxaBeginCatchCallback([](void*) noexcept {
75 moveTopException(activeExceptions, caughtExceptions);
76 });
77
78 registerCxaRethrowCallback([]() noexcept {
79 moveTopException(caughtExceptions, activeExceptions);
80 });
81
82 registerCxaEndCatchCallback([]() noexcept {
83 if (invalid) {
84 return;
85 }
86
87 __cxxabiv1::__cxa_exception* top =
88 __cxxabiv1::__cxa_get_globals_fast()->caughtExceptions;
89 // This is gcc specific and not specified in the ABI:
90 // abs(handlerCount) is the number of active handlers, it's negative
91 // for rethrown exceptions and positive (always 1) for regular
92 // exceptions.
93 // In the rethrow case, we've already popped the exception off the
94 // caught stack, so we don't do anything here.
95 // For Lua interop, we see the handlerCount = 0
96 if ((top->handlerCount == 1) || (top->handlerCount == 0)) {
97 if (!caughtExceptions.pop()) {
98 activeExceptions.clear();
99 invalid = true;
100 }
101 }
102 });
103
104 registerRethrowExceptionCallback(
105 [](std::exception_ptr) noexcept { addActiveException(); });
106
107 try {
108 ::folly::exception_tracer::installHandlers();
109 } catch (...) {
110 }
111 }
112};
113
114Initializer initializer;
115
116} // namespace
117