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:
Antony Polukhin
2016-12-26 22:18:27 +03:00
parent 2893578446
commit 16ef077fa4
3 changed files with 56 additions and 62 deletions

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();