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