mirror of
https://github.com/boostorg/stacktrace.git
synced 2026-01-31 08:42:09 +00:00
Fix unsigned overflow in basic_stacktrace constructor, improve some tests, make sure that skipping 2 frames does not show internals to the user
This commit is contained in:
@@ -63,7 +63,7 @@ namespace boost { namespace stacktrace { namespace detail {
|
||||
// Class that implements the actual backtracing
|
||||
class backend {
|
||||
public:
|
||||
BOOST_STACKTRACE_FUNCTION static std::size_t collect(void** memory, std::size_t size) BOOST_NOEXCEPT;
|
||||
BOOST_NOINLINE BOOST_STACKTRACE_FUNCTION static std::size_t collect(void** memory, std::size_t size) BOOST_NOEXCEPT;
|
||||
BOOST_STACKTRACE_FUNCTION static std::string get_name(const void* addr);
|
||||
BOOST_STACKTRACE_FUNCTION static std::string get_source_file(const void* addr);
|
||||
BOOST_STACKTRACE_FUNCTION static std::size_t get_source_line(const void* addr);
|
||||
|
||||
@@ -36,7 +36,7 @@ class basic_stacktrace {
|
||||
boost::container::vector<frame, Allocator> impl_;
|
||||
|
||||
/// @cond
|
||||
static const std::size_t frames_to_skip = 0;
|
||||
static const std::size_t frames_to_skip = 2;
|
||||
|
||||
void fill(void** begin, std::size_t size) {
|
||||
if (size < frames_to_skip) {
|
||||
@@ -50,14 +50,43 @@ class basic_stacktrace {
|
||||
);
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
BOOST_NOINLINE void init(std::size_t max_depth) BOOST_NOEXCEPT {
|
||||
public:
|
||||
typedef typename boost::container::vector<frame, Allocator>::value_type value_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::allocator_type allocator_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_pointer pointer;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_pointer const_pointer;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reference reference;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reference const_reference;
|
||||
typedef typename boost::container::vector<frame, Allocator>::size_type size_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::difference_type difference_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_iterator iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_iterator const_iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator reverse_iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
/// @brief Stores the current function call sequence inside the class.
|
||||
///
|
||||
/// @b Complexity: O(N) where N is call sequence length, O(1) for noop backend.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
|
||||
///
|
||||
/// @param max_depth max stack depth
|
||||
///
|
||||
/// @throws Nothing. Note that default construction of allocator may throw, hovewer it is
|
||||
/// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
|
||||
BOOST_NOINLINE explicit basic_stacktrace(std::size_t max_depth = static_cast<std::size_t>(-1), const allocator_type& a = allocator_type()) BOOST_NOEXCEPT
|
||||
: impl_(a)
|
||||
{
|
||||
const size_t buffer_size = 128;
|
||||
if (!max_depth) {
|
||||
return;
|
||||
}
|
||||
|
||||
max_depth += frames_to_skip;
|
||||
if (static_cast<std::size_t>(-1) - frames_to_skip >= max_depth) {
|
||||
max_depth += frames_to_skip;
|
||||
}
|
||||
|
||||
try {
|
||||
{ // Fast path without additional allocations
|
||||
@@ -83,47 +112,10 @@ class basic_stacktrace {
|
||||
|
||||
buf.resize(buf.size() * 2);
|
||||
} while (1);
|
||||
|
||||
|
||||
} catch (...) {
|
||||
// ignore exception
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
public:
|
||||
typedef typename boost::container::vector<frame, Allocator>::value_type value_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::allocator_type allocator_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_pointer pointer;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_pointer const_pointer;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reference reference;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reference const_reference;
|
||||
typedef typename boost::container::vector<frame, Allocator>::size_type size_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::difference_type difference_type;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_iterator iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_iterator const_iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator reverse_iterator;
|
||||
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
/// @brief Stores the current function call sequence inside the class.
|
||||
///
|
||||
/// @b Complexity: O(N) where N is call sequence length, O(1) for noop backend.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
|
||||
///
|
||||
/// @throws Nothing. Note that default construction of allocator may throw, hovewer it is
|
||||
/// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
|
||||
BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a = allocator_type()) BOOST_NOEXCEPT
|
||||
: impl_(a)
|
||||
{
|
||||
init(static_cast<std::size_t>(-1));
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE explicit basic_stacktrace(std::size_t max_depth, const allocator_type& a = allocator_type()) BOOST_NOEXCEPT
|
||||
: impl_(a)
|
||||
{
|
||||
init(max_depth);
|
||||
}
|
||||
|
||||
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
|
||||
/// @b Complexity: O(st.size())
|
||||
@@ -136,22 +128,26 @@ public:
|
||||
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
|
||||
basic_stacktrace(basic_stacktrace&& st) = default;
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator::deallocate is async signal safe.
|
||||
~basic_stacktrace() BOOST_NOEXCEPT = default;
|
||||
#endif
|
||||
|
||||
/// @b Complexity: O(st.size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
|
||||
basic_stacktrace& operator=(const basic_stacktrace& st) = default;
|
||||
basic_stacktrace& operator=(const basic_stacktrace& st) {
|
||||
impl_ = st.impl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
/// @b Complexity: O(st.size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
|
||||
basic_stacktrace& operator=(basic_stacktrace&& st) = default;
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe if Allocator::deallocate are async signal safe..
|
||||
~basic_stacktrace() BOOST_NOEXCEPT = default;
|
||||
#endif
|
||||
|
||||
*/
|
||||
/// @returns Number of function names stored inside the class.
|
||||
///
|
||||
/// @b Complexity: O(1)
|
||||
|
||||
@@ -36,10 +36,8 @@ void test_deeply_nested_namespaces() {
|
||||
std::cout << ss.str() << '\n';
|
||||
BOOST_TEST(ss.str().find("main") != std::string::npos);
|
||||
|
||||
#if defined(BOOST_STACKTRACE_DYN_LINK)
|
||||
BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos
|
||||
|| ss.str().find("return_from_nested_namespaces") != std::string::npos);
|
||||
#endif
|
||||
BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos);
|
||||
BOOST_TEST(ss.str().find("return_from_nested_namespaces") != std::string::npos);
|
||||
|
||||
stacktrace ns1 = return_from_nested_namespaces();
|
||||
BOOST_TEST(ns1 != return_from_nested_namespaces()); // Different addresses in test_deeply_nested_namespaces() function
|
||||
@@ -193,9 +191,9 @@ void test_frame() {
|
||||
BOOST_TEST(st[i] <= st[i]);
|
||||
BOOST_TEST(st[i] >= st[i]);
|
||||
|
||||
frame fv = nst[2];
|
||||
frame fv = nst[0];
|
||||
BOOST_TEST(fv);
|
||||
if (i >= 2 && i < min_size - 3) { // Begin and end of the trace may match, skipping them
|
||||
if (i < min_size - 3) { // End of the trace may match, skipping
|
||||
BOOST_TEST(st[i] != fv);
|
||||
BOOST_TEST(st[i].name() != fv.name());
|
||||
BOOST_TEST(st[i] != fv);
|
||||
@@ -215,7 +213,7 @@ void test_frame() {
|
||||
BOOST_TEST(empty_frame.name() == "");
|
||||
BOOST_TEST(empty_frame.source_line() == 0);
|
||||
}
|
||||
/*
|
||||
|
||||
void test_empty_basic_stacktrace() {
|
||||
typedef boost::stacktrace::stacktrace st_t;
|
||||
st_t st(0);
|
||||
@@ -233,11 +231,11 @@ void test_empty_basic_stacktrace() {
|
||||
BOOST_TEST(st.crbegin() == st.crend());
|
||||
BOOST_TEST(st.rbegin() == st.crend());
|
||||
|
||||
BOOST_TEST(hash_value(st) == hash_value(st_t()));
|
||||
BOOST_TEST(st == st_t());
|
||||
BOOST_TEST(!(st < st_t()));
|
||||
BOOST_TEST(!(st > st_t()));
|
||||
}*/
|
||||
BOOST_TEST(hash_value(st) == hash_value(st_t(0)));
|
||||
BOOST_TEST(st == st_t(0));
|
||||
BOOST_TEST(!(st < st_t(0)));
|
||||
BOOST_TEST(!(st > st_t(0)));
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_deeply_nested_namespaces();
|
||||
@@ -245,7 +243,7 @@ int main() {
|
||||
test_comparisons();
|
||||
test_iterators();
|
||||
test_frame();
|
||||
//test_empty_basic_stacktrace();
|
||||
test_empty_basic_stacktrace();
|
||||
|
||||
BOOST_TEST(&bar1 != &bar2);
|
||||
boost::stacktrace::stacktrace b1 = bar1();
|
||||
|
||||
Reference in New Issue
Block a user