stacktrace.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. // Copyright Antony Polukhin, 2016-2017.
  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/explicit_operator_bool.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. #ifdef BOOST_INTEL
  24. # pragma warning(push)
  25. # pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
  26. #endif
  27. namespace boost { namespace stacktrace {
  28. /// Class that on construction copies minimal information about call stack into its internals and provides access to that information.
  29. /// @tparam Allocator Allocator to use during stack capture.
  30. template <class Allocator>
  31. class basic_stacktrace {
  32. std::vector<boost::stacktrace::frame, Allocator> impl_;
  33. typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t;
  34. /// @cond
  35. void fill(native_frame_ptr_t* begin, std::size_t size) {
  36. if (!size) {
  37. return;
  38. }
  39. impl_.reserve(static_cast<std::size_t>(size));
  40. for (std::size_t i = 0; i < size; ++i) {
  41. if (!begin[i]) {
  42. return;
  43. }
  44. impl_.push_back(
  45. frame(begin[i])
  46. );
  47. }
  48. }
  49. static std::size_t frames_count_from_buffer_size(std::size_t buffer_size) BOOST_NOEXCEPT {
  50. const std::size_t ret = (buffer_size > sizeof(native_frame_ptr_t) ? buffer_size / sizeof(native_frame_ptr_t) : 0);
  51. return (ret > 1024 ? 1024 : ret); // Dealing with suspiciously big sizes
  52. }
  53. BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) {
  54. BOOST_CONSTEXPR_OR_CONST std::size_t buffer_size = 128;
  55. if (!max_depth) {
  56. return;
  57. }
  58. try {
  59. { // Fast path without additional allocations
  60. native_frame_ptr_t buffer[buffer_size];
  61. const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, buffer_size, frames_to_skip + 1);
  62. if (buffer_size > frames_count || frames_count >= max_depth) {
  63. const std::size_t size = (max_depth < frames_count ? max_depth : frames_count);
  64. fill(buffer, size);
  65. return;
  66. }
  67. }
  68. // Failed to fit in `buffer_size`. Allocating memory:
  69. #ifdef BOOST_NO_CXX11_ALLOCATOR
  70. typedef typename Allocator::template rebind<native_frame_ptr_t>::other allocator_void_t;
  71. #else
  72. typedef typename std::allocator_traits<Allocator>::template rebind_alloc<native_frame_ptr_t> allocator_void_t;
  73. #endif
  74. std::vector<native_frame_ptr_t, allocator_void_t> buf(buffer_size * 2, 0, impl_.get_allocator());
  75. do {
  76. const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(&buf[0], buf.size(), frames_to_skip + 1);
  77. if (buf.size() > frames_count || frames_count >= max_depth) {
  78. const std::size_t size = (max_depth < frames_count ? max_depth : frames_count);
  79. fill(&buf[0], size);
  80. return;
  81. }
  82. buf.resize(buf.size() * 2);
  83. } while (buf.size() < buf.max_size()); // close to `true`, but suppresses `C4127: conditional expression is constant`.
  84. } catch (...) {
  85. // ignore exception
  86. }
  87. }
  88. /// @endcond
  89. public:
  90. typedef typename std::vector<boost::stacktrace::frame, Allocator>::value_type value_type;
  91. typedef typename std::vector<boost::stacktrace::frame, Allocator>::allocator_type allocator_type;
  92. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer pointer;
  93. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer const_pointer;
  94. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference reference;
  95. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference const_reference;
  96. typedef typename std::vector<boost::stacktrace::frame, Allocator>::size_type size_type;
  97. typedef typename std::vector<boost::stacktrace::frame, Allocator>::difference_type difference_type;
  98. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator iterator;
  99. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator const_iterator;
  100. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator reverse_iterator;
  101. typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator const_reverse_iterator;
  102. /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
  103. ///
  104. /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
  105. ///
  106. /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  107. BOOST_FORCEINLINE basic_stacktrace() BOOST_NOEXCEPT
  108. : impl_()
  109. {
  110. init(0 , static_cast<std::size_t>(-1));
  111. }
  112. /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
  113. ///
  114. /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
  115. ///
  116. /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  117. ///
  118. /// @param a Allocator that would be passed to underlying storeage.
  119. BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) BOOST_NOEXCEPT
  120. : impl_(a)
  121. {
  122. init(0 , static_cast<std::size_t>(-1));
  123. }
  124. /// @brief Stores [skip, skip + max_depth) of 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: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  129. ///
  130. /// @param skip How many top calls to skip and do not store in *this.
  131. ///
  132. /// @param max_depth Max call sequence depth to collect.
  133. ///
  134. /// @param a Allocator that would be passed to underlying storeage.
  135. ///
  136. /// @throws Nothing. Note that default construction of allocator may throw, however it is
  137. /// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
  138. BOOST_FORCEINLINE basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type& a = allocator_type()) BOOST_NOEXCEPT
  139. : impl_(a)
  140. {
  141. init(skip , max_depth);
  142. }
  143. /// @b Complexity: O(st.size())
  144. ///
  145. /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  146. basic_stacktrace(const basic_stacktrace& st)
  147. : impl_(st.impl_)
  148. {}
  149. /// @b Complexity: O(st.size())
  150. ///
  151. /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
  152. basic_stacktrace& operator=(const basic_stacktrace& st) {
  153. impl_ = st.impl_;
  154. return *this;
  155. }
  156. #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
  157. /// @b Complexity: O(1)
  158. ///
  159. /// @b Async-Handler-Safety: Safe if Allocator::deallocate is async signal safe.
  160. ~basic_stacktrace() BOOST_NOEXCEPT = default;
  161. #endif
  162. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  163. /// @b Complexity: O(1)
  164. ///
  165. /// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
  166. basic_stacktrace(basic_stacktrace&& st) BOOST_NOEXCEPT
  167. : impl_(std::move(st.impl_))
  168. {}
  169. /// @b Complexity: O(st.size())
  170. ///
  171. /// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
  172. basic_stacktrace& operator=(basic_stacktrace&& st)
  173. #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
  174. BOOST_NOEXCEPT_IF(( std::is_nothrow_move_assignable< std::vector<boost::stacktrace::frame, Allocator> >::value ))
  175. #else
  176. BOOST_NOEXCEPT
  177. #endif
  178. {
  179. impl_ = std::move(st.impl_);
  180. return *this;
  181. }
  182. #endif
  183. /// @returns Number of function names stored inside the class.
  184. ///
  185. /// @b Complexity: O(1)
  186. ///
  187. /// @b Async-Handler-Safety: Safe.
  188. size_type size() const BOOST_NOEXCEPT {
  189. return impl_.size();
  190. }
  191. /// @param frame_no Zero based index of frame to return. 0
  192. /// is the function index where stacktrace was constructed and
  193. /// index close to this->size() contains function `main()`.
  194. /// @returns frame that references the actual frame info, stored inside *this.
  195. ///
  196. /// @b Complexity: O(1).
  197. ///
  198. /// @b Async-Handler-Safety: Safe.
  199. const_reference operator[](std::size_t frame_no) const BOOST_NOEXCEPT {
  200. return impl_[frame_no];
  201. }
  202. /// @b Complexity: O(1)
  203. ///
  204. /// @b Async-Handler-Safety: Safe.
  205. const_iterator begin() const BOOST_NOEXCEPT { return impl_.begin(); }
  206. /// @b Complexity: O(1)
  207. ///
  208. /// @b Async-Handler-Safety: Safe.
  209. const_iterator cbegin() const BOOST_NOEXCEPT { return impl_.begin(); }
  210. /// @b Complexity: O(1)
  211. ///
  212. /// @b Async-Handler-Safety: Safe.
  213. const_iterator end() const BOOST_NOEXCEPT { return impl_.end(); }
  214. /// @b Complexity: O(1)
  215. ///
  216. /// @b Async-Handler-Safety: Safe.
  217. const_iterator cend() const BOOST_NOEXCEPT { return impl_.end(); }
  218. /// @b Complexity: O(1)
  219. ///
  220. /// @b Async-Handler-Safety: Safe.
  221. const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); }
  222. /// @b Complexity: O(1)
  223. ///
  224. /// @b Async-Handler-Safety: Safe.
  225. const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); }
  226. /// @b Complexity: O(1)
  227. ///
  228. /// @b Async-Handler-Safety: Safe.
  229. const_reverse_iterator rend() const BOOST_NOEXCEPT { return impl_.rend(); }
  230. /// @b Complexity: O(1)
  231. ///
  232. /// @b Async-Handler-Safety: Safe.
  233. const_reverse_iterator crend() const BOOST_NOEXCEPT { return impl_.rend(); }
  234. /// @brief Allows to check that stack trace capturing was successful.
  235. /// @returns `true` if `this->size() != 0`
  236. ///
  237. /// @b Complexity: O(1)
  238. ///
  239. /// @b Async-Handler-Safety: Safe.
  240. BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
  241. /// @brief Allows to check that stack trace failed.
  242. /// @returns `true` if `this->size() == 0`
  243. ///
  244. /// @b Complexity: O(1)
  245. ///
  246. /// @b Async-Handler-Safety: Safe.
  247. bool empty() const BOOST_NOEXCEPT { return !size(); }
  248. /// @cond
  249. bool operator!() const BOOST_NOEXCEPT { return !size(); }
  250. /// @endcond
  251. const std::vector<boost::stacktrace::frame, Allocator>& as_vector() const BOOST_NOEXCEPT {
  252. return impl_;
  253. }
  254. /// Constructs stacktrace from basic_istreamable that references the dumped stacktrace. Terminating zero frame is discarded.
  255. ///
  256. /// @b Complexity: O(N)
  257. template <class Char, class Trait>
  258. static basic_stacktrace from_dump(std::basic_istream<Char, Trait>& in, const allocator_type& a = allocator_type()) {
  259. typedef typename std::basic_istream<Char, Trait>::pos_type pos_type;
  260. basic_stacktrace ret(0, 0, a);
  261. // reserving space
  262. const pos_type pos = in.tellg();
  263. in.seekg(0, in.end);
  264. const std::size_t frames_count = frames_count_from_buffer_size(static_cast<std::size_t>(in.tellg()));
  265. in.seekg(pos);
  266. if (!frames_count) {
  267. return ret;
  268. }
  269. native_frame_ptr_t ptr = 0;
  270. ret.impl_.reserve(frames_count);
  271. while (in.read(reinterpret_cast<Char*>(&ptr), sizeof(ptr))) {
  272. if (!ptr) {
  273. break;
  274. }
  275. ret.impl_.push_back(frame(ptr));
  276. }
  277. return ret;
  278. }
  279. /// Constructs stacktrace from raw memory dump. Terminating zero frame is discarded.
  280. ///
  281. /// @b Complexity: O(size) in worst case
  282. static basic_stacktrace from_dump(const void* begin, std::size_t buffer_size_in_bytes, const allocator_type& a = allocator_type()) {
  283. basic_stacktrace ret(0, 0, a);
  284. const native_frame_ptr_t* first = static_cast<const native_frame_ptr_t*>(begin);
  285. const std::size_t frames_count = frames_count_from_buffer_size(buffer_size_in_bytes);
  286. if (!frames_count) {
  287. return ret;
  288. }
  289. const native_frame_ptr_t* const last = first + frames_count;
  290. ret.impl_.reserve(frames_count);
  291. for (; first != last; ++first) {
  292. if (!*first) {
  293. break;
  294. }
  295. ret.impl_.push_back(frame(*first));
  296. }
  297. return ret;
  298. }
  299. };
  300. /// @brief Compares stacktraces for less, order is platform dependent.
  301. ///
  302. /// @b Complexity: Amortized O(1); worst case O(size())
  303. ///
  304. /// @b Async-Handler-Safety: Safe.
  305. template <class Allocator1, class Allocator2>
  306. bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  307. return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector());
  308. }
  309. /// @brief Compares stacktraces for equality.
  310. ///
  311. /// @b Complexity: Amortized O(1); worst case O(size())
  312. ///
  313. /// @b Async-Handler-Safety: Safe.
  314. template <class Allocator1, class Allocator2>
  315. bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  316. return lhs.as_vector() == rhs.as_vector();
  317. }
  318. /// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
  319. template <class Allocator1, class Allocator2>
  320. bool operator> (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  321. return rhs < lhs;
  322. }
  323. template <class Allocator1, class Allocator2>
  324. bool operator<=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  325. return !(lhs > rhs);
  326. }
  327. template <class Allocator1, class Allocator2>
  328. bool operator>=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  329. return !(lhs < rhs);
  330. }
  331. template <class Allocator1, class Allocator2>
  332. bool operator!=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
  333. return !(lhs == rhs);
  334. }
  335. /// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe.
  336. template <class Allocator>
  337. std::size_t hash_value(const basic_stacktrace<Allocator>& st) BOOST_NOEXCEPT {
  338. return boost::hash_range(st.as_vector().begin(), st.as_vector().end());
  339. }
  340. /// Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers.
  341. template <class CharT, class TraitsT, class Allocator>
  342. std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) {
  343. if (bt) {
  344. os << boost::stacktrace::detail::to_string(&bt.as_vector()[0], bt.size());
  345. }
  346. return os;
  347. }
  348. /// This is the typedef to use unless you'd like to provide a specific allocator to boost::stacktrace::basic_stacktrace.
  349. typedef basic_stacktrace<> stacktrace;
  350. }} // namespace boost::stacktrace
  351. #ifdef BOOST_INTEL
  352. # pragma warning(pop)
  353. #endif
  354. #endif // BOOST_STACKTRACE_STACKTRACE_HPP