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