Always use an EventBaseManager with ScopedEventBaseThread
[folly.git] / folly / experimental / exception_tracer / ExceptionTracerLib.cpp
1 /*
2  * Copyright 2016 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 [[noreturn]] void __cxa_throw(
32     void* thrownException,
33     std::type_info* type,
34     void (*destructor)(void*));
35 void* __cxa_begin_catch(void* excObj) throw();
36 [[noreturn]] void __cxa_rethrow(void);
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     SYNCHRONIZED(callbacks_) { callbacks_.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     SYNCHRONIZED_CONST(callbacks_) {
57       for (auto& cb : callbacks_) {
58         cb(args...);
59       }
60     }
61   }
62
63  private:
64   folly::Synchronized<std::vector<Function>> callbacks_;
65 };
66
67 } // namespace
68
69 namespace folly {
70 namespace exception_tracer {
71
72 #define DECLARE_CALLBACK(NAME)                                   \
73   CallbackHolder<NAME##Type>& get##NAME##Callbacks() {           \
74     static Indestructible<CallbackHolder<NAME##Type>> Callbacks; \
75     return *Callbacks;                                           \
76   }                                                              \
77   void register##NAME##Callback(NAME##Type callback) {           \
78     get##NAME##Callbacks().registerCallback(callback);           \
79   }
80
81 DECLARE_CALLBACK(CxaThrow);
82 DECLARE_CALLBACK(CxaBeginCatch);
83 DECLARE_CALLBACK(CxaRethrow);
84 DECLARE_CALLBACK(CxaEndCatch);
85 DECLARE_CALLBACK(RethrowException);
86
87 } // exception_tracer
88 } // folly
89
90 namespace __cxxabiv1 {
91
92 [[noreturn]] void __cxa_throw(void* thrownException,
93                               std::type_info* type,
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();
100 }
101
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();
111   orig_cxa_rethrow();
112   __builtin_unreachable();
113 }
114
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);
122 }
123
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();
129 }
130
131 } // namespace __cxxabiv1
132
133 namespace std {
134
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)>(
141           dlsym(RTLD_NEXT,
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.
146 #ifndef __clang__
147   __builtin_unreachable();
148 #endif
149 }
150
151 } // namespace std