Tested x86_64-linux, pushed to trunk. -- >8 --
This prevents inlining the current() function to guarantee that it is present in the stacktrace, then tells libbacktrace to skip that frame. To avoid overflow in the int argument to __glibcxx_backtrace_simple, we need to check if the skip parameter exceeds INT_MAX (which is possible for 16-bit targets where short and int have the same width). We also need to limit the size of the returned value to the max_depth parameter, which was missing previously. This also fixes basic_stacktrace::max_size() to not exceed the maximum size supported by the allocator, which might be smaller than the maximum value of size_type. libstdc++-v3/ChangeLog: * include/std/stacktrace (basic_stacktrace::current): Duplicate implementation into each overload. Add noinline attribute and skip current frame. (basic_stacktrace::max_size()): Call _Impl::_S_max_size. (basic_stacktrace::_S_curr_cb()): New function defining lambda. (basic_stacktrace::_Impl::_S_max_size): New function defining maximum size in terms of allocator and size_type. (basic_stacktrace::_Impl::_M_allocate): Check against max_size(). * testsuite/19_diagnostics/stacktrace/entry.cc: Call function for non-constexpr checks. Check line number is correct. --- libstdc++-v3/include/std/stacktrace | 91 ++++++++++++++----- .../19_diagnostics/stacktrace/entry.cc | 7 +- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/libstdc++-v3/include/std/stacktrace b/libstdc++-v3/include/std/stacktrace index 623f44bdca4..4e271cef3f3 100644 --- a/libstdc++-v3/include/std/stacktrace +++ b/libstdc++-v3/include/std/stacktrace @@ -36,6 +36,7 @@ #include <bits/stl_algo.h> #include <bits/stl_iterator.h> #include <bits/stl_uninitialized.h> +#include <ext/numeric_traits.h> #include <cxxabi.h> struct __glibcxx_backtrace_state; @@ -232,19 +233,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // [stacktrace.basic.ctor], creation and assignment + [[__gnu__::__noinline__]] static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept { - return current(0, size_type(-1), __alloc); + auto __state = stacktrace_entry::_S_init(); + basic_stacktrace __ret(__alloc); + if (!__ret._M_reserve(64)) [[unlikely]] + return __ret; + + if (__glibcxx_backtrace_simple(__state, 1, _S_curr_cb(), + nullptr, std::__addressof(__ret))) + __ret._M_clear(); + + return __ret; } + [[__gnu__::__noinline__]] static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept { - return current(__skip, size_type(-1), __alloc); + auto __state = stacktrace_entry::_S_init(); + basic_stacktrace __ret(__alloc); + if (__skip >= __INT_MAX__) [[unlikely]] + return __ret; + if (!__ret._M_reserve(64)) [[unlikely]] + return __ret; + + if (__glibcxx_backtrace_simple(__state, __skip + 1, _S_curr_cb(), + nullptr, std::__addressof(__ret))) + __ret._M_clear(); + + return __ret; } + [[__gnu__::__noinline__]] static basic_stacktrace current(size_type __skip, size_type __max_depth, const allocator_type& __alloc = allocator_type()) noexcept @@ -253,23 +277,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __state = stacktrace_entry::_S_init(); basic_stacktrace __ret(__alloc); - if (!__ret._M_reserve(std::min<int>(__max_depth, 64))) + if (__max_depth == 0 || __skip >= __INT_MAX__) [[unlikely]] + return __ret; + if (!__ret._M_reserve(std::min<int>(__max_depth, 64))) [[unlikely]] return __ret; - auto __cb = [](void* __data, uintptr_t __pc) { - auto& __s = *static_cast<basic_stacktrace*>(__data); - stacktrace_entry __f; - __f._M_pc = __pc; - if (__s._M_push_back(__f)) - return 0; - return 1; - }; + if (__glibcxx_backtrace_simple(__state, __skip + 1, _S_curr_cb(), + nullptr, std::__addressof(__ret))) + __ret._M_clear(); + else if (__ret.size() > __max_depth) + __ret.resize(__max_depth); - if (__glibcxx_backtrace_simple(__state, __skip, +__cb, nullptr, - std::__addressof(__ret))) - { - __ret._M_clear(); - } return __ret; } @@ -443,7 +461,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[nodiscard]] bool empty() const noexcept { return size() == 0; } size_type size() const noexcept { return _M_impl._M_size; } - size_type max_size() const noexcept { return size_type(-1); } + + size_type + max_size() const noexcept + { return _Impl::_S_max_size(_M_impl._M_alloc); } const_reference operator[](size_type __n) const noexcept @@ -507,6 +528,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_impl._M_deallocate(_M_alloc); } + static auto + _S_curr_cb() noexcept + -> int (*) (void*, uintptr_t) + { + return [](void* __data, uintptr_t __pc) { + auto& __s = *static_cast<basic_stacktrace*>(__data); + stacktrace_entry __f; + __f._M_pc = __pc; + if (__s._M_push_back(__f)) + return 0; + return 1; + }; + } + struct _Impl { using pointer = typename _AllocTraits::pointer; @@ -515,21 +550,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_type _M_size = 0; size_type _M_capacity = 0; + static size_type + _S_max_size(const allocator_type& __alloc) noexcept + { + const size_t __size_max = __gnu_cxx::__int_traits<size_type>::__max; + const size_t __alloc_max = _AllocTraits::max_size(__alloc); + return std::min(__size_max, __alloc_max); + } + // Precondition: _M_frames == nullptr pointer _M_allocate(allocator_type& __alloc, size_type __n) noexcept { __try { - _M_frames = __n ? __alloc.allocate(__n) : nullptr; - _M_capacity = __n; + if (0 < __n && __n <= _S_max_size(__alloc)) [[unlikely]] + { + _M_frames = __alloc.allocate(__n); + _M_capacity = __n; + return _M_frames; + } } __catch (...) { - _M_frames = nullptr; - _M_capacity = 0; } - return _M_frames; + _M_frames = nullptr; + _M_capacity = 0; + return nullptr;; } void diff --git a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/entry.cc b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/entry.cc index 0bbcabd8fae..a222c425b20 100644 --- a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/entry.cc +++ b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/entry.cc @@ -36,7 +36,8 @@ test_members() VERIFY( e1 != e2 ); VERIFY( e1.description() == e2.description() ); VERIFY( e1.source_file() == e2.source_file() ); - VERIFY( e1.source_line() != e2.source_line() ); + VERIFY( e1.source_line() == (__LINE__ - 5) ); + VERIFY( e2.source_line() == (__LINE__ - 5) ); std::stacktrace_entry e3 = []{ return std::stacktrace::current().at(0); @@ -44,10 +45,10 @@ test_members() VERIFY( e1 != e3 ); VERIFY( e1.description() != e3.description() ); VERIFY( e1.source_file() == e3.source_file() ); - VERIFY( e1.source_line() != e3.source_line() ); + VERIFY( e3.source_line() == (__LINE__ - 5) ); } int main() { - test_constexpr(); + test_members(); } -- 2.34.1