ExceptionTracer.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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/ExceptionTracer.h>
  17. #include <exception>
  18. #include <iostream>
  19. #include <dlfcn.h>
  20. #include <glog/logging.h>
  21. #include <folly/String.h>
  22. #include <folly/experimental/exception_tracer/ExceptionAbi.h>
  23. #include <folly/experimental/exception_tracer/StackTrace.h>
  24. #include <folly/experimental/symbolizer/Symbolizer.h>
  25. namespace {
  26. using namespace ::folly::exception_tracer;
  27. using namespace ::folly::symbolizer;
  28. using namespace __cxxabiv1;
  29. extern "C" {
  30. StackTraceStack* getExceptionStackTraceStack(void) __attribute__((__weak__));
  31. typedef StackTraceStack* (*GetExceptionStackTraceStackType)();
  32. GetExceptionStackTraceStackType getExceptionStackTraceStackFn;
  33. }
  34. } // namespace
  35. namespace folly {
  36. namespace exception_tracer {
  37. std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {
  38. printExceptionInfo(out, info, SymbolizePrinter::COLOR_IF_TTY);
  39. return out;
  40. }
  41. void printExceptionInfo(
  42. std::ostream& out,
  43. const ExceptionInfo& info,
  44. int options) {
  45. out << "Exception type: ";
  46. if (info.type) {
  47. out << folly::demangle(*info.type);
  48. } else {
  49. out << "(unknown type)";
  50. }
  51. out << " (" << info.frames.size()
  52. << (info.frames.size() == 1 ? " frame" : " frames") << ")\n";
  53. try {
  54. size_t frameCount = info.frames.size();
  55. // Skip our own internal frames
  56. static constexpr size_t kInternalFramesNumber = 3;
  57. if (frameCount > kInternalFramesNumber) {
  58. auto addresses = info.frames.data() + kInternalFramesNumber;
  59. frameCount -= kInternalFramesNumber;
  60. std::vector<SymbolizedFrame> frames;
  61. frames.resize(frameCount);
  62. Symbolizer symbolizer(
  63. (options & SymbolizePrinter::NO_FILE_AND_LINE)
  64. ? Dwarf::LocationInfoMode::DISABLED
  65. : Symbolizer::kDefaultLocationInfoMode);
  66. symbolizer.symbolize(addresses, frames.data(), frameCount);
  67. OStreamSymbolizePrinter osp(out, options);
  68. osp.println(addresses, frames.data(), frameCount);
  69. }
  70. } catch (const std::exception& e) {
  71. out << "\n !! caught " << folly::exceptionStr(e) << "\n";
  72. } catch (...) {
  73. out << "\n !!! caught unexpected exception\n";
  74. }
  75. }
  76. namespace {
  77. /**
  78. * Is this a standard C++ ABI exception?
  79. *
  80. * Dependent exceptions (thrown via std::rethrow_exception) aren't --
  81. * exc doesn't actually point to a __cxa_exception structure, but
  82. * the offset of unwindHeader is correct, so exc->unwindHeader actually
  83. * returns a _Unwind_Exception object. Yeah, it's ugly like that.
  84. */
  85. bool isAbiCppException(const __cxa_exception* exc) {
  86. // The least significant four bytes must be "C++\0"
  87. static const uint64_t cppClass =
  88. ((uint64_t)'C' << 24) | ((uint64_t)'+' << 16) | ((uint64_t)'+' << 8);
  89. return (exc->unwindHeader.exception_class & 0xffffffff) == cppClass;
  90. }
  91. } // namespace
  92. std::vector<ExceptionInfo> getCurrentExceptions() {
  93. struct Once {
  94. Once() {
  95. // See if linked in with us (getExceptionStackTraceStack is weak)
  96. getExceptionStackTraceStackFn = getExceptionStackTraceStack;
  97. if (!getExceptionStackTraceStackFn) {
  98. // Nope, see if it's in a shared library
  99. getExceptionStackTraceStackFn = (GetExceptionStackTraceStackType)dlsym(
  100. RTLD_NEXT, "getExceptionStackTraceStack");
  101. }
  102. }
  103. };
  104. static Once once;
  105. std::vector<ExceptionInfo> exceptions;
  106. auto currentException = __cxa_get_globals()->caughtExceptions;
  107. if (!currentException) {
  108. return exceptions;
  109. }
  110. StackTraceStack* traceStack = nullptr;
  111. if (!getExceptionStackTraceStackFn) {
  112. static bool logged = false;
  113. if (!logged) {
  114. LOG(WARNING)
  115. << "Exception tracer library not linked, stack traces not available";
  116. logged = true;
  117. }
  118. } else if ((traceStack = getExceptionStackTraceStackFn()) == nullptr) {
  119. static bool logged = false;
  120. if (!logged) {
  121. LOG(WARNING)
  122. << "Exception stack trace invalid, stack traces not available";
  123. logged = true;
  124. }
  125. }
  126. StackTrace* trace = traceStack ? traceStack->top() : nullptr;
  127. while (currentException) {
  128. ExceptionInfo info;
  129. // Dependent exceptions (thrown via std::rethrow_exception) aren't
  130. // standard ABI __cxa_exception objects, and are correctly labeled as
  131. // such in the exception_class field. We could try to extract the
  132. // primary exception type in horribly hacky ways, but, for now, nullptr.
  133. info.type = isAbiCppException(currentException)
  134. ? currentException->exceptionType
  135. : nullptr;
  136. if (traceStack) {
  137. LOG_IF(DFATAL, !trace)
  138. << "Invalid trace stack for exception of type: "
  139. << (info.type ? folly::demangle(*info.type) : "null");
  140. if (!trace) {
  141. return {};
  142. }
  143. info.frames.assign(
  144. trace->addresses, trace->addresses + trace->frameCount);
  145. trace = traceStack->next(trace);
  146. }
  147. currentException = currentException->nextException;
  148. exceptions.push_back(std::move(info));
  149. }
  150. LOG_IF(DFATAL, trace) << "Invalid trace stack!";
  151. return exceptions;
  152. }
  153. namespace {
  154. std::terminate_handler origTerminate = abort;
  155. std::unexpected_handler origUnexpected = abort;
  156. void dumpExceptionStack(const char* prefix) {
  157. auto exceptions = getCurrentExceptions();
  158. if (exceptions.empty()) {
  159. return;
  160. }
  161. LOG(ERROR) << prefix << ", exception stack follows";
  162. for (auto& exc : exceptions) {
  163. LOG(ERROR) << exc << "\n";
  164. }
  165. LOG(ERROR) << "exception stack complete";
  166. }
  167. void terminateHandler() {
  168. dumpExceptionStack("terminate() called");
  169. origTerminate();
  170. }
  171. void unexpectedHandler() {
  172. dumpExceptionStack("Unexpected exception");
  173. origUnexpected();
  174. }
  175. } // namespace
  176. void installHandlers() {
  177. struct Once {
  178. Once() {
  179. origTerminate = std::set_terminate(terminateHandler);
  180. origUnexpected = std::set_unexpected(unexpectedHandler);
  181. }
  182. };
  183. static Once once;
  184. }
  185. } // namespace exception_tracer
  186. } // namespace folly