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