Codemod: use #include angle brackets in folly and thrift
[folly.git] / folly / experimental / exception_tracer / ExceptionTracerLib.cpp
1 /*
2  * Copyright 2014 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 <dlfcn.h>
18 #include <pthread.h>
19 #include <stdlib.h>
20
21 #include <glog/logging.h>
22
23 #include <folly/Portability.h>
24 #include <folly/experimental/exception_tracer/StackTrace.h>
25 #include <folly/experimental/exception_tracer/ExceptionAbi.h>
26 #include <folly/experimental/exception_tracer/ExceptionTracer.h>
27 #include <folly/experimental/symbolizer/Symbolizer.h>
28
29 namespace __cxxabiv1 {
30
31 extern "C" {
32 FOLLY_NORETURN void __cxa_throw(void* thrownException,
33                  std::type_info* type, void (*destructor)(void));
34 void* __cxa_begin_catch(void* excObj);
35 FOLLY_NORETURN 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 FOLLY_TLS bool invalid;
46 FOLLY_TLS StackTraceStack activeExceptions;
47 FOLLY_TLS StackTraceStack caughtExceptions;
48 pthread_once_t initialized = PTHREAD_ONCE_INIT;
49
50 extern "C" {
51 FOLLY_NORETURN typedef void (*CxaThrowType)(void*, std::type_info*,
52                                             void (*)(void));
53 typedef void* (*CxaBeginCatchType)(void*);
54 FOLLY_NORETURN typedef void (*CxaRethrowType)(void);
55 typedef void (*CxaEndCatchType)(void);
56
57 CxaThrowType orig_cxa_throw;
58 CxaBeginCatchType orig_cxa_begin_catch;
59 CxaRethrowType orig_cxa_rethrow;
60 CxaEndCatchType orig_cxa_end_catch;
61 }  // extern "C"
62
63 FOLLY_NORETURN typedef void (*RethrowExceptionType)(std::exception_ptr);
64 RethrowExceptionType orig_rethrow_exception;
65
66 void initialize() {
67   orig_cxa_throw = (CxaThrowType)dlsym(RTLD_NEXT, "__cxa_throw");
68   orig_cxa_begin_catch =
69     (CxaBeginCatchType)dlsym(RTLD_NEXT, "__cxa_begin_catch");
70   orig_cxa_rethrow =
71     (CxaRethrowType)dlsym(RTLD_NEXT, "__cxa_rethrow");
72   orig_cxa_end_catch = (CxaEndCatchType)dlsym(RTLD_NEXT, "__cxa_end_catch");
73   // Mangled name for std::rethrow_exception
74   // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
75   // is typedef'ed to a type in namespace __exception_ptr
76   orig_rethrow_exception =
77     (RethrowExceptionType)dlsym(
78         RTLD_NEXT,
79         "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE");
80
81   if (!orig_cxa_throw || !orig_cxa_begin_catch || !orig_cxa_rethrow ||
82       !orig_cxa_end_catch || !orig_rethrow_exception) {
83     abort();  // what else can we do?
84   }
85 }
86
87 }  // namespace
88
89 // This function is exported and may be found via dlsym(RTLD_NEXT, ...)
90 extern "C" StackTraceStack* getExceptionStackTraceStack() {
91   return invalid ? nullptr : &caughtExceptions;
92 }
93
94 namespace {
95
96 // Make sure we're counting stack frames correctly, don't inline.
97 FOLLY_NOINLINE void addActiveException();
98
99 void addActiveException() {
100   pthread_once(&initialized, initialize);
101   // Capture stack trace
102   if (!invalid) {
103     if (!activeExceptions.pushCurrent()) {
104       activeExceptions.clear();
105       caughtExceptions.clear();
106       invalid = true;
107     }
108   }
109 }
110
111 void moveTopException(StackTraceStack& from, StackTraceStack& to) {
112   if (invalid) {
113     return;
114   }
115   if (!to.moveTopFrom(from)) {
116     from.clear();
117     to.clear();
118     invalid = true;
119   }
120 }
121
122 }  // namespace
123
124 namespace __cxxabiv1 {
125
126 void __cxa_throw(void* thrownException, std::type_info* type,
127                  void (*destructor)(void)) {
128   addActiveException();
129   orig_cxa_throw(thrownException, type, destructor);
130 }
131
132 void __cxa_rethrow() {
133   // __cxa_rethrow leaves the current exception on the caught stack,
134   // and __cxa_begin_catch recognizes that case.  We could do the same, but
135   // we'll implement something simpler (and slower): we pop the exception from
136   // the caught stack, and push it back onto the active stack; this way, our
137   // implementation of __cxa_begin_catch doesn't have to do anything special.
138   moveTopException(caughtExceptions, activeExceptions);
139   orig_cxa_rethrow();
140 }
141
142 void* __cxa_begin_catch(void *excObj) {
143   // excObj is a pointer to the unwindHeader in __cxa_exception
144   moveTopException(activeExceptions, caughtExceptions);
145   return orig_cxa_begin_catch(excObj);
146 }
147
148 void __cxa_end_catch() {
149   if (!invalid) {
150     __cxa_exception* top = __cxa_get_globals_fast()->caughtExceptions;
151     // This is gcc specific and not specified in the ABI:
152     // abs(handlerCount) is the number of active handlers, it's negative
153     // for rethrown exceptions and positive (always 1) for regular exceptions.
154     // In the rethrow case, we've already popped the exception off the
155     // caught stack, so we don't do anything here.
156     if (top->handlerCount == 1) {
157       if (!caughtExceptions.pop()) {
158         activeExceptions.clear();
159         invalid = true;
160       }
161     }
162   }
163   orig_cxa_end_catch();
164 }
165
166 }  // namespace __cxxabiv1
167
168 namespace std {
169
170 void rethrow_exception(std::exception_ptr ep) {
171   addActiveException();
172   orig_rethrow_exception(ep);
173 }
174
175 }  // namespace std
176
177
178 namespace {
179
180 struct Initializer {
181   Initializer() {
182     try {
183       ::folly::exception_tracer::installHandlers();
184     } catch (...) {
185     }
186   }
187 };
188
189 Initializer initializer;
190
191 }  // namespace