Fix folly::Function under C++17 exception specifier rules
[folly.git] / folly / experimental / exception_tracer / ExceptionStackTraceLib.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 <exception>
18
19 #include <folly/experimental/exception_tracer/ExceptionAbi.h>
20 #include <folly/experimental/exception_tracer/ExceptionTracer.h>
21 #include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
22 #include <folly/experimental/exception_tracer/StackTrace.h>
23 #include <folly/experimental/symbolizer/Symbolizer.h>
24
25 using namespace folly::exception_tracer;
26
27 namespace {
28
29 // If we somehow ended up in an invalid state, we don't want to print any stack
30 // trace at all because in could be bogus
31 FOLLY_TLS bool invalid;
32
33 FOLLY_TLS StackTraceStack activeExceptions;
34 FOLLY_TLS StackTraceStack caughtExceptions;
35
36 } // namespace
37
38 // This function is exported and may be found via dlsym(RTLD_NEXT, ...)
39 extern "C" StackTraceStack* getExceptionStackTraceStack() {
40   return invalid ? nullptr : &caughtExceptions;
41 }
42
43 namespace {
44
45 void addActiveException() {
46   // Capture stack trace
47   if (!invalid) {
48     if (!activeExceptions.pushCurrent()) {
49       activeExceptions.clear();
50       caughtExceptions.clear();
51       invalid = true;
52     }
53   }
54 }
55
56 void moveTopException(StackTraceStack& from, StackTraceStack& to) {
57   if (invalid) {
58     return;
59   }
60   if (!to.moveTopFrom(from)) {
61     from.clear();
62     to.clear();
63     invalid = true;
64   }
65 }
66
67 struct Initializer {
68   Initializer() {
69     registerCxaThrowCallback([](
70         void*, std::type_info*, void (*)(void*)) noexcept {
71       addActiveException();
72     });
73
74     registerCxaBeginCatchCallback([](void*) noexcept {
75       moveTopException(activeExceptions, caughtExceptions);
76     });
77
78     registerCxaRethrowCallback([]() noexcept {
79       moveTopException(caughtExceptions, activeExceptions);
80     });
81
82     registerCxaEndCatchCallback([]() noexcept {
83       if (invalid) {
84         return;
85       }
86
87       __cxxabiv1::__cxa_exception* top =
88           __cxxabiv1::__cxa_get_globals_fast()->caughtExceptions;
89       // This is gcc specific and not specified in the ABI:
90       // abs(handlerCount) is the number of active handlers, it's negative
91       // for rethrown exceptions and positive (always 1) for regular
92       // exceptions.
93       // In the rethrow case, we've already popped the exception off the
94       // caught stack, so we don't do anything here.
95       // For Lua interop, we see the handlerCount = 0
96       if ((top->handlerCount == 1) || (top->handlerCount == 0)) {
97         if (!caughtExceptions.pop()) {
98           activeExceptions.clear();
99           invalid = true;
100         }
101       }
102     });
103
104     registerRethrowExceptionCallback([](std::exception_ptr) noexcept {
105       addActiveException();
106     });
107
108     try {
109       ::folly::exception_tracer::installHandlers();
110     } catch (...) {
111     }
112   }
113 };
114
115 Initializer initializer;
116
117 } // namespace