stacktrace.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. // Copyright Antony Polukhin, 2016-2024.
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See
  4. // accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_STACKTRACE_STACKTRACE_HPP
  7. #define BOOST_STACKTRACE_STACKTRACE_HPP
  8. #include <boost/config.hpp>
  9. #ifdef BOOST_HAS_PRAGMA_ONCE
  10. # pragma once
  11. #endif
  12. #include <boost/core/no_exceptions_support.hpp>
  13. #include <boost/container_hash/hash_fwd.hpp>
  14. #include <iosfwd>
  15. #include <string>
  16. #include <vector>
  17. #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
  18. # include <type_traits>
  19. #endif
  20. #include <boost/stacktrace/stacktrace_fwd.hpp>
  21. #include <boost/stacktrace/safe_dump_to.hpp>
  22. #include <boost/stacktrace/detail/frame_decl.hpp>
  23. #include <boost/stacktrace/frame.hpp>
  24. #ifdef BOOST_INTEL
  25. # pragma warning(push)
  26. # pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
  27. #endif
  28. #if defined(BOOST_MSVC)
  29. extern "C" {
  30. BOOST_SYMBOL_EXPORT inline void* boost_stacktrace_impl_return_nullptr() { return nullptr; }
  31. const char* boost_stacktrace_impl_current_exception_stacktrace();
  32. bool* boost_stacktrace_impl_ref_capture_stacktraces_at_throw();
  33. }
  34. #ifdef _M_IX86
  35. # pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_current_exception_stacktrace=_boost_stacktrace_impl_return_nullptr")
  36. # pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_ref_capture_stacktraces_at_throw=_boost_stacktrace_impl_return_nullptr")
  37. #else
  38. # pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_current_exception_stacktrace=boost_stacktrace_impl_return_nullptr")
  39. # pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_ref_capture_stacktraces_at_throw=boost_stacktrace_impl_return_nullptr")
  40. #endif
  41. #endif
  42. namespace boost { namespace stacktrace {
  43. namespace impl {
  44. #if defined(__GNUC__) && defined(__ELF__)
  45. BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
  46. const char* current_exception_stacktrace() noexcept;
  47. BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
  48. bool& ref_capture_stacktraces_at_throw() noexcept;
  49. #endif
  50. } // namespace impl
  51. /// Class that on construction copies minimal information about call stack into its internals and provides access to that information.
  52. /// @tparam Allocator Allocator to use during stack capture.
  53. template <class Allocator>
  54. class basic_stacktrace {
  55. std::vector<boost::stacktrace::frame, Allocator> impl_;
  56. typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t;
  57. /// @cond
  58. void fill(native_frame_ptr_t* begin, std::size_t size) {
  59. if (!size) {
  60. return;
  61. }
  62. impl_.reserve(static_cast<std::size_t>(size));
  63. for (std::size_t i = 0; i < size; ++i) {
  64. if (!begin[i]) {
  65. return;
  66. }
  67. impl_.push_back(
  68. frame(begin[i])
  69. );
  70. }
  71. }
  72. static std::size_t frames_count_from_buffer_size(std::size_t buffer_size) noexcept {
  73. const std::size_t ret = (buffer_size > sizeof(native_frame_ptr_t) ? buffer_size / sizeof(native_frame_ptr_t) : 0);
  74. return (ret > 1024 ? 1024 : ret); // Dealing with suspiciously big sizes
  75. }
  76. BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) {
  77. constexpr std::size_t buffer_size = 128;
  78. if (!max_depth) {
  79. return;
  80. }
  81. BOOST_TRY {
  82. { // Fast path without additional allocations
  83. native_frame_ptr_t buffer[buffer_size];
  84. const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, buffer_size < max_depth ? buffer_size : max_depth, frames_to_skip + 1);
  85. if (buffer_size > frames_count || frames_count == max_depth) {
  86. fill(buffer, frames_count);
  87. return;
  88. }
  89. }
  90. // Failed to fit in `buffer_size`. Allocating memory:
  91. #ifdef BOOST_NO_CXX11_ALLOCATOR
  92. typedef typename Allocator::template rebind<native_frame_ptr_t>::other allocator_void_t;
  93. #else
  94. typedef typename std::allocator_traits<Allocator>::template rebind_alloc<native_frame_ptr_t> allocator_void_t;
  95. #endif
  96. std::vector<native_frame_ptr_t, allocator_void_t> buf(buffer_size * 2, 0, impl_.get_allocator());
  97. do {
  98. const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(&buf[0], buf.size() < max_depth ? buf.size() : max_depth, frames_to_skip + 1);
  99. if (buf.size() > frames_count || frames_count == max_depth) {
  100. fill(&buf[0], frames_count);
  101. return;
  102. }
  103. buf.resize(buf.size() * 2);
  104. } while (buf.size() < buf.max_size()); // close to `true`, but suppresses `C4127: conditional expression is constant`.
  105. } BOOST_CATCH (...) {
  106. // ignore exception
  107. }
  108. BOOST_CATCH_END
  109. }
  110. /// @endcond
  111. public:
  112. typedef typename std::vector<boost::stacktrace::frame, Allocator>::value_type value_type;
  113. typedef typename std::vector<boost::stacktrace::frame, Allocator>::allocator_type allocator_type;
  114. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer pointer;
  115. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer const_pointer;
  116. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference reference;
  117. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference const_reference;
  118. typedef typename std::vector<boost::stacktrace::frame, Allocator>::size_type size_type;
  119. typedef typename std::vector<boost::stacktrace::frame, Allocator>::difference_type difference_type;
  120. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator iterator;
  121. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator const_iterator;
  122. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator reverse_iterator;
  123. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator const_reverse_iterator;
  124. /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
  125. ///
  126. /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
  127. ///
  128. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  129. BOOST_FORCEINLINE basic_stacktrace() noexcept
  130. : impl_()
  131. {
  132. init(0 , static_cast<std::size_t>(-1));
  133. }
  134. /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
  135. ///
  136. /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
  137. ///
  138. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  139. ///
  140. /// @param a Allocator that would be passed to underlying storage.
  141. BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) noexcept
  142. : impl_(a)
  143. {
  144. init(0 , static_cast<std::size_t>(-1));
  145. }
  146. /// @brief Stores [skip, skip + max_depth) of the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
  147. ///
  148. /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
  149. ///
  150. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  151. ///
  152. /// @param skip How many top calls to skip and do not store in *this.
  153. ///
  154. /// @param max_depth Max call sequence depth to collect.
  155. ///
  156. /// @param a Allocator that would be passed to underlying storage.
  157. ///
  158. /// @throws Nothing. Note that default construction of allocator may throw, however it is
  159. /// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
  160. BOOST_FORCEINLINE basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type& a = allocator_type()) noexcept
  161. : impl_(a)
  162. {
  163. init(skip , max_depth);
  164. }
  165. /// @b Complexity: O(st.size())
  166. ///
  167. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  168. basic_stacktrace(const basic_stacktrace& st)
  169. : impl_(st.impl_)
  170. {}
  171. /// @b Complexity: O(st.size())
  172. ///
  173. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  174. basic_stacktrace& operator=(const basic_stacktrace& st) {
  175. impl_ = st.impl_;
  176. return *this;
  177. }
  178. #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
  179. /// @b Complexity: O(1)
  180. ///
  181. /// @b Async-Handler-Safety: \asyncsafe if Allocator::deallocate is async signal safe.
  182. ~basic_stacktrace() noexcept = default;
  183. #endif
  184. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  185. /// @b Complexity: O(1)
  186. ///
  187. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
  188. basic_stacktrace(basic_stacktrace&& st) noexcept
  189. : impl_(std::move(st.impl_))
  190. {}
  191. /// @b Complexity: O(st.size())
  192. ///
  193. /// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
  194. basic_stacktrace& operator=(basic_stacktrace&& st)
  195. #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
  196. noexcept(( std::is_nothrow_move_assignable< std::vector<boost::stacktrace::frame, Allocator> >::value ))
  197. #else
  198. noexcept
  199. #endif
  200. {
  201. impl_ = std::move(st.impl_);
  202. return *this;
  203. }
  204. #endif
  205. /// @returns Number of function names stored inside the class.
  206. ///
  207. /// @b Complexity: O(1)
  208. ///
  209. /// @b Async-Handler-Safety: \asyncsafe.
  210. size_type size() const noexcept {
  211. return impl_.size();
  212. }
  213. /// @param frame_no Zero based index of frame to return. 0
  214. /// is the function index where stacktrace was constructed and
  215. /// index close to this->size() contains function `main()`.
  216. /// @returns frame that references the actual frame info, stored inside *this.
  217. ///
  218. /// @b Complexity: O(1).
  219. ///
  220. /// @b Async-Handler-Safety: \asyncsafe.
  221. const_reference operator[](std::size_t frame_no) const noexcept {
  222. return impl_[frame_no];
  223. }
  224. /// @b Complexity: O(1)
  225. ///
  226. /// @b Async-Handler-Safety: \asyncsafe.
  227. const_iterator begin() const noexcept { return impl_.begin(); }
  228. /// @b Complexity: O(1)
  229. ///
  230. /// @b Async-Handler-Safety: \asyncsafe.
  231. const_iterator cbegin() const noexcept { return impl_.begin(); }
  232. /// @b Complexity: O(1)
  233. ///
  234. /// @b Async-Handler-Safety: \asyncsafe.
  235. const_iterator end() const noexcept { return impl_.end(); }
  236. /// @b Complexity: O(1)
  237. ///
  238. /// @b Async-Handler-Safety: \asyncsafe.
  239. const_iterator cend() const noexcept { return impl_.end(); }
  240. /// @b Complexity: O(1)
  241. ///
  242. /// @b Async-Handler-Safety: \asyncsafe.
  243. const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
  244. /// @b Complexity: O(1)
  245. ///
  246. /// @b Async-Handler-Safety: \asyncsafe.
  247. const_reverse_iterator crbegin() const noexcept { return impl_.rbegin(); }
  248. /// @b Complexity: O(1)
  249. ///
  250. /// @b Async-Handler-Safety: \asyncsafe.
  251. const_reverse_iterator rend() const noexcept { return impl_.rend(); }
  252. /// @b Complexity: O(1)
  253. ///
  254. /// @b Async-Handler-Safety: \asyncsafe.
  255. const_reverse_iterator crend() const noexcept { return impl_.rend(); }
  256. /// @brief Allows to check that stack trace capturing was successful.
  257. /// @returns `true` if `this->size() != 0`
  258. ///
  259. /// @b Complexity: O(1)
  260. ///
  261. /// @b Async-Handler-Safety: \asyncsafe.
  262. constexpr explicit operator bool () const noexcept { return !empty(); }
  263. /// @brief Allows to check that stack trace failed.
  264. /// @returns `true` if `this->size() == 0`
  265. ///
  266. /// @b Complexity: O(1)
  267. ///
  268. /// @b Async-Handler-Safety: \asyncsafe.
  269. bool empty() const noexcept { return !size(); }
  270. const std::vector<boost::stacktrace::frame, Allocator>& as_vector() const noexcept {
  271. return impl_;
  272. }
  273. /// Constructs stacktrace from basic_istreamable that references the dumped stacktrace. Terminating zero frame is discarded.
  274. ///
  275. /// @b Complexity: O(N)
  276. template <class Char, class Trait>
  277. static basic_stacktrace from_dump(std::basic_istream<Char, Trait>& in, const allocator_type& a = allocator_type()) {
  278. typedef typename std::basic_istream<Char, Trait>::pos_type pos_type;
  279. basic_stacktrace ret(0, 0, a);
  280. // reserving space
  281. const pos_type pos = in.tellg();
  282. in.seekg(0, in.end);
  283. const std::size_t frames_count = frames_count_from_buffer_size(static_cast<std::size_t>(in.tellg()));
  284. in.seekg(pos);
  285. if (!frames_count) {
  286. return ret;
  287. }
  288. native_frame_ptr_t ptr = 0;
  289. ret.impl_.reserve(frames_count);
  290. while (in.read(reinterpret_cast<Char*>(&ptr), sizeof(ptr))) {
  291. if (!ptr) {
  292. break;
  293. }
  294. ret.impl_.push_back(frame(ptr));
  295. }
  296. return ret;
  297. }
  298. /// Constructs stacktrace from raw memory dump. Terminating zero frame is discarded.
  299. ///
  300. /// @param begin Beginning of the memory where the stacktrace was saved using the boost::stacktrace::safe_dump_to
  301. ///
  302. /// @param buffer_size_in_bytes Size of the memory. Usually the same value that was passed to the boost::stacktrace::safe_dump_to
  303. ///
  304. /// @b Complexity: O(size) in worst case
  305. static basic_stacktrace from_dump(const void* begin, std::size_t buffer_size_in_bytes, const allocator_type& a = allocator_type()) {
  306. basic_stacktrace ret(0, 0, a);
  307. const native_frame_ptr_t* first = static_cast<const native_frame_ptr_t*>(begin);
  308. const std::size_t frames_count = frames_count_from_buffer_size(buffer_size_in_bytes);
  309. if (!frames_count) {
  310. return ret;
  311. }
  312. const native_frame_ptr_t* const last = first + frames_count;
  313. ret.impl_.reserve(frames_count);
  314. for (; first != last; ++first) {
  315. if (!*first) {
  316. break;
  317. }
  318. ret.impl_.push_back(frame(*first));
  319. }
  320. return ret;
  321. }
  322. /// Returns a basic_stacktrace object containing a stacktrace captured at
  323. /// the point where the currently handled exception was thrown by its
  324. /// initial throw-expression (i.e. not a rethrow), or an empty
  325. /// basic_stacktrace object if:
  326. ///
  327. /// - the `boost_stacktrace_from_exception` library is not linked to the
  328. /// current binary, or
  329. /// - the initialization of stacktrace failed, or
  330. /// - stacktrace captures are not enabled for the throwing thread, or
  331. /// - no exception is being handled, or
  332. /// - due to implementation-defined reasons.
  333. ///
  334. /// `alloc` is passed to the constructor of the stacktrace object.
  335. /// Rethrowing an exception using a throw-expression with no operand does
  336. /// not alter the captured stacktrace.
  337. ///
  338. /// Implements https://wg21.link/p2370r1
  339. static basic_stacktrace<Allocator> from_current_exception(const allocator_type& alloc = allocator_type()) noexcept {
  340. // Matches the constant from implementation
  341. constexpr std::size_t kStacktraceDumpSize = 4096;
  342. const char* trace = nullptr;
  343. #if defined(__GNUC__) && defined(__ELF__)
  344. if (impl::current_exception_stacktrace) {
  345. trace = impl::current_exception_stacktrace();
  346. }
  347. #elif defined(BOOST_MSVC)
  348. trace = boost_stacktrace_impl_current_exception_stacktrace();
  349. #endif
  350. if (trace) {
  351. try {
  352. return basic_stacktrace<Allocator>::from_dump(trace, kStacktraceDumpSize, alloc);
  353. } catch (const std::exception&) {
  354. // ignore
  355. }
  356. }
  357. return basic_stacktrace<Allocator>{0, 0, alloc};
  358. }
  359. };
  360. /// @brief Compares stacktraces for less, order is platform dependent.
  361. ///
  362. /// @b Complexity: Amortized O(1); worst case O(size())
  363. ///
  364. /// @b Async-Handler-Safety: \asyncsafe.
  365. template <class Allocator1, class Allocator2>
  366. bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  367. return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector());
  368. }
  369. /// @brief Compares stacktraces for equality.
  370. ///
  371. /// @b Complexity: Amortized O(1); worst case O(size())
  372. ///
  373. /// @b Async-Handler-Safety: \asyncsafe.
  374. template <class Allocator1, class Allocator2>
  375. bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  376. return lhs.as_vector() == rhs.as_vector();
  377. }
  378. /// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
  379. template <class Allocator1, class Allocator2>
  380. bool operator> (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  381. return rhs < lhs;
  382. }
  383. template <class Allocator1, class Allocator2>
  384. bool operator<=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  385. return !(lhs > rhs);
  386. }
  387. template <class Allocator1, class Allocator2>
  388. bool operator>=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  389. return !(lhs < rhs);
  390. }
  391. template <class Allocator1, class Allocator2>
  392. bool operator!=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
  393. return !(lhs == rhs);
  394. }
  395. /// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe.
  396. template <class Allocator>
  397. std::size_t hash_value(const basic_stacktrace<Allocator>& st) noexcept {
  398. return boost::hash_range(st.as_vector().begin(), st.as_vector().end());
  399. }
  400. /// Returns std::string with the stacktrace in a human readable format; unsafe to use in async handlers.
  401. template <class Allocator>
  402. std::string to_string(const basic_stacktrace<Allocator>& bt) {
  403. if (!bt) {
  404. return std::string();
  405. }
  406. return boost::stacktrace::detail::to_string(&bt.as_vector()[0], bt.size());
  407. }
  408. /// Outputs stacktrace in a human readable format to the output stream `os`; unsafe to use in async handlers.
  409. template <class CharT, class TraitsT, class Allocator>
  410. std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) {
  411. return os << boost::stacktrace::to_string(bt);
  412. }
  413. /// This is the typedef to use unless you'd like to provide a specific allocator to boost::stacktrace::basic_stacktrace.
  414. typedef basic_stacktrace<> stacktrace;
  415. }} // namespace boost::stacktrace
  416. #ifdef BOOST_INTEL
  417. # pragma warning(pop)
  418. #endif
  419. #endif // BOOST_STACKTRACE_STACKTRACE_HPP