dcee56921e918d1c17bb173e6db2ab2f7d034be2
[folly.git] / folly / experimental / exception_tracer / ExceptionTracerLib.cpp
1 /*
2  * Copyright 2017 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     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 } // namespace exception_tracer
88 } // namespace folly
89
90 // Clang is smart enough to understand that the symbols we're loading
91 // are [[noreturn]], but GCC is not. In order to be able to build with
92 // -Wunreachable-code enable for Clang, these __builtin_unreachable()
93 // calls need to go away. Everything else is messy though, so just
94 // #define it to an empty macro under Clang and be done with it.
95 #ifdef __clang__
96 # define __builtin_unreachable()
97 #endif
98
99 namespace __cxxabiv1 {
100
101 void __cxa_throw(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)>(
150           dlsym(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