2 * Copyright 2016 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.
17 #include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
23 #include <folly/Indestructible.h>
24 #include <folly/Portability.h>
25 #include <folly/SharedMutex.h>
26 #include <folly/Synchronized.h>
28 namespace __cxxabiv1 {
31 [[noreturn]] void __cxa_throw(
32 void* thrownException,
34 void (*destructor)(void*));
35 void* __cxa_begin_catch(void* excObj) throw();
36 [[noreturn]] void __cxa_rethrow(void);
37 void __cxa_end_catch(void);
40 } // namespace __cxxabiv1
42 using namespace folly::exception_tracer;
46 template <typename Function>
47 class CallbackHolder {
49 void registerCallback(Function f) {
50 SYNCHRONIZED(callbacks_) { callbacks_.push_back(std::move(f)); }
53 // always inline to enforce kInternalFramesNumber
54 template <typename... Args>
55 FOLLY_ALWAYS_INLINE void invoke(Args... args) {
56 SYNCHRONIZED_CONST(callbacks_) {
57 for (auto& cb : callbacks_) {
64 folly::Synchronized<std::vector<Function>> callbacks_;
70 namespace exception_tracer {
72 #define DECLARE_CALLBACK(NAME) \
73 CallbackHolder<NAME##Type>& get##NAME##Callbacks() { \
74 static Indestructible<CallbackHolder<NAME##Type>> Callbacks; \
77 void register##NAME##Callback(NAME##Type callback) { \
78 get##NAME##Callbacks().registerCallback(callback); \
81 DECLARE_CALLBACK(CxaThrow);
82 DECLARE_CALLBACK(CxaBeginCatch);
83 DECLARE_CALLBACK(CxaRethrow);
84 DECLARE_CALLBACK(CxaEndCatch);
85 DECLARE_CALLBACK(RethrowException);
90 namespace __cxxabiv1 {
92 [[noreturn]] void __cxa_throw(void* thrownException,
94 void (*destructor)(void*)) {
95 static auto orig_cxa_throw =
96 reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, "__cxa_throw"));
97 getCxaThrowCallbacks().invoke(thrownException, type, destructor);
98 orig_cxa_throw(thrownException, type, destructor);
99 __builtin_unreachable();
102 [[noreturn]] void __cxa_rethrow() {
103 // __cxa_rethrow leaves the current exception on the caught stack,
104 // and __cxa_begin_catch recognizes that case. We could do the same, but
105 // we'll implement something simpler (and slower): we pop the exception from
106 // the caught stack, and push it back onto the active stack; this way, our
107 // implementation of __cxa_begin_catch doesn't have to do anything special.
108 static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>(
109 dlsym(RTLD_NEXT, "__cxa_rethrow"));
110 getCxaRethrowCallbacks().invoke();
112 __builtin_unreachable();
115 void* __cxa_begin_catch(void* excObj) throw() {
116 // excObj is a pointer to the unwindHeader in __cxa_exception
117 static auto orig_cxa_begin_catch =
118 reinterpret_cast<decltype(&__cxa_begin_catch)>(
119 dlsym(RTLD_NEXT, "__cxa_begin_catch"));
120 getCxaBeginCatchCallbacks().invoke(excObj);
121 return orig_cxa_begin_catch(excObj);
124 void __cxa_end_catch() {
125 static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>(
126 dlsym(RTLD_NEXT, "__cxa_end_catch"));
127 getCxaEndCatchCallbacks().invoke();
128 orig_cxa_end_catch();
131 } // namespace __cxxabiv1
135 void rethrow_exception(std::exception_ptr ep) {
136 // Mangled name for std::rethrow_exception
137 // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
138 // is typedef'ed to a type in namespace __exception_ptr
139 static auto orig_rethrow_exception =
140 reinterpret_cast<decltype(&rethrow_exception)>(
142 "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE"));
143 getRethrowExceptionCallbacks().invoke(ep);
144 orig_rethrow_exception(ep);
145 // Clang knows this is unreachable, but GCC doesn't.
147 __builtin_unreachable();