ExceptionTracerLib.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright 2012-present 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. #include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
  17. #include <dlfcn.h>
  18. #include <vector>
  19. #include <folly/Indestructible.h>
  20. #include <folly/Portability.h>
  21. #include <folly/SharedMutex.h>
  22. #include <folly/Synchronized.h>
  23. namespace __cxxabiv1 {
  24. extern "C" {
  25. void __cxa_throw(
  26. void* thrownException,
  27. std::type_info* type,
  28. void (*destructor)(void*)) __attribute__((__noreturn__));
  29. void* __cxa_begin_catch(void* excObj) throw();
  30. void __cxa_rethrow(void) __attribute__((__noreturn__));
  31. void __cxa_end_catch(void);
  32. }
  33. } // namespace __cxxabiv1
  34. using namespace folly::exception_tracer;
  35. namespace {
  36. template <typename Function>
  37. class CallbackHolder {
  38. public:
  39. void registerCallback(Function f) {
  40. callbacks_.wlock()->push_back(std::move(f));
  41. }
  42. // always inline to enforce kInternalFramesNumber
  43. template <typename... Args>
  44. FOLLY_ALWAYS_INLINE void invoke(Args... args) {
  45. auto callbacksLock = callbacks_.rlock();
  46. for (auto& cb : *callbacksLock) {
  47. cb(args...);
  48. }
  49. }
  50. private:
  51. folly::Synchronized<std::vector<Function>> callbacks_;
  52. };
  53. } // namespace
  54. namespace folly {
  55. namespace exception_tracer {
  56. #define DECLARE_CALLBACK(NAME) \
  57. CallbackHolder<NAME##Type>& get##NAME##Callbacks() { \
  58. static Indestructible<CallbackHolder<NAME##Type>> Callbacks; \
  59. return *Callbacks; \
  60. } \
  61. void register##NAME##Callback(NAME##Type callback) { \
  62. get##NAME##Callbacks().registerCallback(callback); \
  63. }
  64. DECLARE_CALLBACK(CxaThrow)
  65. DECLARE_CALLBACK(CxaBeginCatch)
  66. DECLARE_CALLBACK(CxaRethrow)
  67. DECLARE_CALLBACK(CxaEndCatch)
  68. DECLARE_CALLBACK(RethrowException)
  69. } // namespace exception_tracer
  70. } // namespace folly
  71. // Clang is smart enough to understand that the symbols we're loading
  72. // are [[noreturn]], but GCC is not. In order to be able to build with
  73. // -Wunreachable-code enable for Clang, these __builtin_unreachable()
  74. // calls need to go away. Everything else is messy though, so just
  75. // #define it to an empty macro under Clang and be done with it.
  76. #ifdef __clang__
  77. #define __builtin_unreachable()
  78. #endif
  79. namespace __cxxabiv1 {
  80. void __cxa_throw(
  81. void* thrownException,
  82. std::type_info* type,
  83. void (*destructor)(void*)) {
  84. static auto orig_cxa_throw =
  85. reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, "__cxa_throw"));
  86. getCxaThrowCallbacks().invoke(thrownException, type, destructor);
  87. orig_cxa_throw(thrownException, type, destructor);
  88. __builtin_unreachable(); // orig_cxa_throw never returns
  89. }
  90. void __cxa_rethrow() {
  91. // __cxa_rethrow leaves the current exception on the caught stack,
  92. // and __cxa_begin_catch recognizes that case. We could do the same, but
  93. // we'll implement something simpler (and slower): we pop the exception from
  94. // the caught stack, and push it back onto the active stack; this way, our
  95. // implementation of __cxa_begin_catch doesn't have to do anything special.
  96. static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>(
  97. dlsym(RTLD_NEXT, "__cxa_rethrow"));
  98. getCxaRethrowCallbacks().invoke();
  99. orig_cxa_rethrow();
  100. __builtin_unreachable(); // orig_cxa_rethrow never returns
  101. }
  102. void* __cxa_begin_catch(void* excObj) throw() {
  103. // excObj is a pointer to the unwindHeader in __cxa_exception
  104. static auto orig_cxa_begin_catch =
  105. reinterpret_cast<decltype(&__cxa_begin_catch)>(
  106. dlsym(RTLD_NEXT, "__cxa_begin_catch"));
  107. getCxaBeginCatchCallbacks().invoke(excObj);
  108. return orig_cxa_begin_catch(excObj);
  109. }
  110. void __cxa_end_catch() {
  111. static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>(
  112. dlsym(RTLD_NEXT, "__cxa_end_catch"));
  113. getCxaEndCatchCallbacks().invoke();
  114. orig_cxa_end_catch();
  115. }
  116. } // namespace __cxxabiv1
  117. namespace std {
  118. void rethrow_exception(std::exception_ptr ep) {
  119. // Mangled name for std::rethrow_exception
  120. // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
  121. // is typedef'ed to a type in namespace __exception_ptr
  122. static auto orig_rethrow_exception =
  123. reinterpret_cast<decltype(&rethrow_exception)>(dlsym(
  124. RTLD_NEXT,
  125. "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE"));
  126. getRethrowExceptionCallbacks().invoke(ep);
  127. orig_rethrow_exception(ep);
  128. __builtin_unreachable(); // orig_rethrow_exception never returns
  129. }
  130. } // namespace std