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