backtrace.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*************************************************************************
  2. *
  3. * Copyright 2018 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #ifndef REALM_UTIL_BACKTRACE_HPP
  19. #define REALM_UTIL_BACKTRACE_HPP
  20. #include <string>
  21. #include <iosfwd>
  22. #include <stdexcept>
  23. namespace realm {
  24. namespace util {
  25. /// Backtrace encapsulates a stack trace, usually as captured by `backtrace()`
  26. /// and `backtrace_symbols()` (or platform-specific equivalents).
  27. struct Backtrace {
  28. /// Capture a symbolicated stack trace, excluding the call to `capture()`
  29. /// itself. If any error occurs while capturing the stack trace or
  30. /// translating symbol names, a `Backtrace` object is returned containing a
  31. /// single line describing the error.
  32. ///
  33. /// This function only allocates memory as part of calling
  34. /// `backtrace_symbols()` (or the current platform's equivalent).
  35. static Backtrace capture() noexcept;
  36. /// Print the backtrace to the stream. Each line is separated by a newline.
  37. /// The format of the output is unspecified.
  38. void print(std::ostream&) const;
  39. /// Construct an empty stack trace.
  40. Backtrace() noexcept
  41. {
  42. }
  43. /// Move constructor. This operation cannot fail.
  44. Backtrace(Backtrace&&) noexcept;
  45. /// Copy constructor. See the copy assignment operator.
  46. Backtrace(const Backtrace&) noexcept;
  47. ~Backtrace();
  48. /// Move assignment operator. This operation cannot fail.
  49. Backtrace& operator=(Backtrace&&) noexcept;
  50. /// Copy assignment operator. Copying a `Backtrace` object may result in a
  51. /// memory allocation. If such an allocation fails, the backtrace is
  52. /// replaced with a single line describing the error.
  53. Backtrace& operator=(const Backtrace&) noexcept;
  54. private:
  55. Backtrace(void* memory, const char* const* strs, size_t len)
  56. : m_memory(memory)
  57. , m_strs(strs)
  58. , m_len(len)
  59. {
  60. }
  61. Backtrace(void* memory, size_t len)
  62. : m_memory(memory)
  63. , m_strs(static_cast<char* const*>(memory))
  64. , m_len(len)
  65. {
  66. }
  67. // m_memory is a pointer to the memory block returned by
  68. // `backtrace_symbols()`. It is usually equal to `m_strs`, except in the
  69. // case where an error has occurred and `m_strs` points to statically
  70. // allocated memory describing the error.
  71. //
  72. // When `m_memory` is non-null, the memory is owned by this object.
  73. void* m_memory = nullptr;
  74. // A pointer to a list of string pointers describing the stack trace (same
  75. // format as returned by `backtrace_symbols()`).
  76. const char* const* m_strs = nullptr;
  77. // Number of entries in this stack trace.
  78. size_t m_len = 0;
  79. };
  80. namespace detail {
  81. class ExceptionWithBacktraceBase {
  82. public:
  83. ExceptionWithBacktraceBase()
  84. : m_backtrace(util::Backtrace::capture())
  85. {
  86. }
  87. const util::Backtrace& backtrace() const noexcept
  88. {
  89. return m_backtrace;
  90. }
  91. virtual const char* message() const noexcept = 0;
  92. protected:
  93. util::Backtrace m_backtrace;
  94. // Cannot use Optional here, because Optional wants to use
  95. // ExceptionWithBacktrace.
  96. mutable bool m_has_materialized_message = false;
  97. mutable std::string m_materialized_message;
  98. // Render the message and the backtrace into m_message_with_backtrace. If an
  99. // exception is thrown while rendering the message, the message without the
  100. // backtrace will be returned.
  101. const char* materialize_message() const noexcept;
  102. };
  103. } // namespace detail
  104. /// Base class for exceptions that record a stack trace of where they were
  105. /// thrown.
  106. ///
  107. /// The template argument is expected to be an exception type conforming to the
  108. /// standard library exception API (`std::exception` and friends).
  109. ///
  110. /// It is possible to opt in to exception backtraces in two ways, (a) as part of
  111. /// the exception type, in which case the backtrace will always be included for
  112. /// all exceptions of that type, or (b) at the call-site of an opaque exception
  113. /// type, in which case it is up to the throw-site to decide whether a backtrace
  114. /// should be included.
  115. ///
  116. /// Example (a):
  117. /// ```
  118. /// class MyException : ExceptionWithBacktrace<std::exception> {
  119. /// public:
  120. /// const char* message() const noexcept override
  121. /// {
  122. /// return "MyException error message";
  123. /// }
  124. /// };
  125. ///
  126. /// ...
  127. ///
  128. /// try {
  129. /// throw MyException{};
  130. /// }
  131. /// catch (const MyException& ex) {
  132. /// // Print the backtrace without the message:
  133. /// std::cerr << ex.backtrace() << "\n";
  134. /// // Print the exception message and the backtrace:
  135. /// std::cerr << ex.what() << "\n";
  136. /// // Print the exception message without the backtrace:
  137. /// std::cerr << ex.message() << "\n";
  138. /// }
  139. /// ```
  140. ///
  141. /// Example (b):
  142. /// ```
  143. /// class MyException : std::exception {
  144. /// public:
  145. /// const char* what() const noexcept override
  146. /// {
  147. /// return "MyException error message";
  148. /// }
  149. /// };
  150. ///
  151. /// ...
  152. ///
  153. /// try {
  154. /// throw ExceptionWithBacktrace<MyException>{};
  155. /// }
  156. /// catch (const MyException& ex) {
  157. /// // Print the exception message and the backtrace:
  158. /// std::cerr << ex.what() << "\n";
  159. /// }
  160. /// ```
  161. template <class Base = std::runtime_error>
  162. class ExceptionWithBacktrace : public Base, public detail::ExceptionWithBacktraceBase {
  163. public:
  164. template <class... Args>
  165. inline ExceptionWithBacktrace(Args&&... args)
  166. : Base(std::forward<Args>(args)...)
  167. , detail::ExceptionWithBacktraceBase() // backtrace captured here
  168. {
  169. }
  170. /// Return the message of the exception, including the backtrace of where
  171. /// the exception was thrown.
  172. const char* what() const noexcept final
  173. {
  174. return materialize_message();
  175. }
  176. /// Return the message of the exception without the backtrace. The default
  177. /// implementation calls `Base::what()`.
  178. const char* message() const noexcept override
  179. {
  180. return Base::what();
  181. }
  182. };
  183. // Wrappers for standard exception types with backtrace support
  184. using runtime_error = ExceptionWithBacktrace<std::runtime_error>;
  185. using range_error = ExceptionWithBacktrace<std::range_error>;
  186. using overflow_error = ExceptionWithBacktrace<std::overflow_error>;
  187. using underflow_error = ExceptionWithBacktrace<std::underflow_error>;
  188. using bad_alloc = ExceptionWithBacktrace<std::bad_alloc>;
  189. using invalid_argument = ExceptionWithBacktrace<std::invalid_argument>;
  190. using out_of_range = ExceptionWithBacktrace<std::out_of_range>;
  191. using logic_error = ExceptionWithBacktrace<std::logic_error>;
  192. } // namespace util
  193. } // namespace realm
  194. inline std::ostream& operator<<(std::ostream& os, const realm::util::Backtrace& bt)
  195. {
  196. bt.print(os);
  197. return os;
  198. }
  199. #endif // REALM_UTIL_BACKTRACE_HPP