1 | /* |
2 | * Copyright 2012-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 <folly/experimental/exception_tracer/ExceptionTracerLib.h> |
18 | |
19 | #include <dlfcn.h> |
20 | |
21 | #include <vector> |
22 | |
23 | #include <folly/Indestructible.h> |
24 | #include <folly/Portability.h> |
25 | #include <folly/SharedMutex.h> |
26 | #include <folly/Synchronized.h> |
27 | |
28 | namespace __cxxabiv1 { |
29 | |
30 | extern "C" { |
31 | void __cxa_throw( |
32 | void* thrownException, |
33 | std::type_info* type, |
34 | void (*destructor)(void*)) __attribute__((__noreturn__)); |
35 | void* __cxa_begin_catch(void* excObj) throw(); |
36 | void __cxa_rethrow(void) __attribute__((__noreturn__)); |
37 | void __cxa_end_catch(void); |
38 | } |
39 | |
40 | } // namespace __cxxabiv1 |
41 | |
42 | using namespace folly::exception_tracer; |
43 | |
44 | namespace { |
45 | |
46 | template <typename Function> |
47 | class CallbackHolder { |
48 | public: |
49 | void registerCallback(Function f) { |
50 | callbacks_.wlock()->push_back(std::move(f)); |
51 | } |
52 | |
53 | // always inline to enforce kInternalFramesNumber |
54 | template <typename... Args> |
55 | FOLLY_ALWAYS_INLINE void invoke(Args... args) { |
56 | auto callbacksLock = callbacks_.rlock(); |
57 | for (auto& cb : *callbacksLock) { |
58 | cb(args...); |
59 | } |
60 | } |
61 | |
62 | private: |
63 | folly::Synchronized<std::vector<Function>> callbacks_; |
64 | }; |
65 | |
66 | } // namespace |
67 | |
68 | namespace folly { |
69 | namespace exception_tracer { |
70 | |
71 | #define DECLARE_CALLBACK(NAME) \ |
72 | CallbackHolder<NAME##Type>& get##NAME##Callbacks() { \ |
73 | static Indestructible<CallbackHolder<NAME##Type>> Callbacks; \ |
74 | return *Callbacks; \ |
75 | } \ |
76 | void register##NAME##Callback(NAME##Type callback) { \ |
77 | get##NAME##Callbacks().registerCallback(callback); \ |
78 | } |
79 | |
80 | DECLARE_CALLBACK(CxaThrow) |
81 | DECLARE_CALLBACK(CxaBeginCatch) |
82 | DECLARE_CALLBACK(CxaRethrow) |
83 | DECLARE_CALLBACK(CxaEndCatch) |
84 | DECLARE_CALLBACK(RethrowException) |
85 | |
86 | } // namespace exception_tracer |
87 | } // namespace folly |
88 | |
89 | // Clang is smart enough to understand that the symbols we're loading |
90 | // are [[noreturn]], but GCC is not. In order to be able to build with |
91 | // -Wunreachable-code enable for Clang, these __builtin_unreachable() |
92 | // calls need to go away. Everything else is messy though, so just |
93 | // #define it to an empty macro under Clang and be done with it. |
94 | #ifdef __clang__ |
95 | #define __builtin_unreachable() |
96 | #endif |
97 | |
98 | namespace __cxxabiv1 { |
99 | |
100 | void __cxa_throw( |
101 | void* thrownException, |
102 | std::type_info* type, |
103 | void (*destructor)(void*)) { |
104 | static auto orig_cxa_throw = |
105 | reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, "__cxa_throw" )); |
106 | getCxaThrowCallbacks().invoke(thrownException, type, destructor); |
107 | orig_cxa_throw(thrownException, type, destructor); |
108 | __builtin_unreachable(); // orig_cxa_throw never returns |
109 | } |
110 | |
111 | void __cxa_rethrow() { |
112 | // __cxa_rethrow leaves the current exception on the caught stack, |
113 | // and __cxa_begin_catch recognizes that case. We could do the same, but |
114 | // we'll implement something simpler (and slower): we pop the exception from |
115 | // the caught stack, and push it back onto the active stack; this way, our |
116 | // implementation of __cxa_begin_catch doesn't have to do anything special. |
117 | static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>( |
118 | dlsym(RTLD_NEXT, "__cxa_rethrow" )); |
119 | getCxaRethrowCallbacks().invoke(); |
120 | orig_cxa_rethrow(); |
121 | __builtin_unreachable(); // orig_cxa_rethrow never returns |
122 | } |
123 | |
124 | void* __cxa_begin_catch(void* excObj) throw() { |
125 | // excObj is a pointer to the unwindHeader in __cxa_exception |
126 | static auto orig_cxa_begin_catch = |
127 | reinterpret_cast<decltype(&__cxa_begin_catch)>( |
128 | dlsym(RTLD_NEXT, "__cxa_begin_catch" )); |
129 | getCxaBeginCatchCallbacks().invoke(excObj); |
130 | return orig_cxa_begin_catch(excObj); |
131 | } |
132 | |
133 | void __cxa_end_catch() { |
134 | static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>( |
135 | dlsym(RTLD_NEXT, "__cxa_end_catch" )); |
136 | getCxaEndCatchCallbacks().invoke(); |
137 | orig_cxa_end_catch(); |
138 | } |
139 | |
140 | } // namespace __cxxabiv1 |
141 | |
142 | namespace std { |
143 | |
144 | void rethrow_exception(std::exception_ptr ep) { |
145 | // Mangled name for std::rethrow_exception |
146 | // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr |
147 | // is typedef'ed to a type in namespace __exception_ptr |
148 | static auto orig_rethrow_exception = |
149 | reinterpret_cast<decltype(&rethrow_exception)>(dlsym( |
150 | RTLD_NEXT, |
151 | "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE" )); |
152 | getRethrowExceptionCallbacks().invoke(ep); |
153 | orig_rethrow_exception(ep); |
154 | __builtin_unreachable(); // orig_rethrow_exception never returns |
155 | } |
156 | |
157 | } // namespace std |
158 | |