folly copyright 2015 -> copyright 2016
[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/Portability.h>
24 #include <folly/SharedMutex.h>
25 #include <folly/Synchronized.h>
26
27 namespace __cxxabiv1 {
28
29 extern "C" {
30 FOLLY_NORETURN void __cxa_throw(void* thrownException,
31                                 std::type_info* type,
32                                 void (*destructor)(void*));
33 void* __cxa_begin_catch(void* excObj) throw();
34 FOLLY_NORETURN void __cxa_rethrow(void);
35 void __cxa_rethrow(void);
36 void __cxa_end_catch(void);
37 }
38
39 } // namespace __cxxabiv1
40
41 using namespace folly::exception_tracer;
42
43 namespace {
44
45 template <typename Function>
46 class CallbackHolder {
47  public:
48   void registerCallback(Function f) {
49     SYNCHRONIZED(callbacks_) { callbacks_.push_back(std::move(f)); }
50   }
51
52   // always inline to enforce kInternalFramesNumber
53   template <typename... Args>
54   FOLLY_ALWAYS_INLINE void invoke(Args... args) {
55     SYNCHRONIZED_CONST(callbacks_) {
56       for (auto& cb : callbacks_) {
57         cb(args...);
58       }
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 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 } // exception_tracer
87 } // folly
88
89 namespace __cxxabiv1 {
90
91 void __cxa_throw(void* thrownException,
92                  std::type_info* type,
93                  void (*destructor)(void*)) {
94   static auto orig_cxa_throw =
95       reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, "__cxa_throw"));
96   getCxaThrowCallbacks().invoke(thrownException, type, destructor);
97   orig_cxa_throw(thrownException, type, destructor);
98   __builtin_unreachable(); // orig_cxa_throw never returns
99 }
100
101 void __cxa_rethrow() {
102   // __cxa_rethrow leaves the current exception on the caught stack,
103   // and __cxa_begin_catch recognizes that case.  We could do the same, but
104   // we'll implement something simpler (and slower): we pop the exception from
105   // the caught stack, and push it back onto the active stack; this way, our
106   // implementation of __cxa_begin_catch doesn't have to do anything special.
107   static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>(
108       dlsym(RTLD_NEXT, "__cxa_rethrow"));
109   getCxaRethrowCallbacks().invoke();
110   orig_cxa_rethrow();
111   __builtin_unreachable(); // orig_cxa_rethrow never returns
112 }
113
114 void* __cxa_begin_catch(void* excObj) throw() {
115   // excObj is a pointer to the unwindHeader in __cxa_exception
116   static auto orig_cxa_begin_catch =
117       reinterpret_cast<decltype(&__cxa_begin_catch)>(
118           dlsym(RTLD_NEXT, "__cxa_begin_catch"));
119   getCxaBeginCatchCallbacks().invoke(excObj);
120   return orig_cxa_begin_catch(excObj);
121 }
122
123 void __cxa_end_catch() {
124   static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>(
125       dlsym(RTLD_NEXT, "__cxa_end_catch"));
126   getCxaEndCatchCallbacks().invoke();
127   orig_cxa_end_catch();
128 }
129
130 } // namespace __cxxabiv1
131
132 namespace std {
133
134 void rethrow_exception(std::exception_ptr ep) {
135   // Mangled name for std::rethrow_exception
136   // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
137   // is typedef'ed to a type in namespace __exception_ptr
138   static auto orig_rethrow_exception =
139       reinterpret_cast<decltype(&rethrow_exception)>(
140           dlsym(RTLD_NEXT,
141                 "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE"));
142   getRethrowExceptionCallbacks().invoke(ep);
143   orig_rethrow_exception(ep);
144   __builtin_unreachable(); // orig_rethrow_exception never returns
145 }
146
147 } // namespace std