https://gcc.gnu.org/g:08b2c542e4d0c223f50e46692bccf0c2ebbe3454
commit r16-4457-g08b2c542e4d0c223f50e46692bccf0c2ebbe3454 Author: Jonathan Wakely <[email protected]> Date: Wed Oct 15 20:10:34 2025 +0100 libstdc++: Improve ostream output for std::stacktrace With this change stacktrace entries always output the frame address, and source file information no longer results in " at :0", e.g. 16# myfunc(int) at /tmp/bt.cc:48 [0x4008b7] 17# main at /tmp/bt.cc:61 [0x40091a] 18# __libc_start_call_main [0x7efc3d6d3574] 19# __libc_start_main@GLIBC_2.2.5 [0x7efc3d6d3627] 20# _start [0x400684] This replaces the previous output: 16# myfunc(int) at /tmp/bt.cc:48 17# main at /tmp/bt.cc:61 18# __libc_start_call_main at :0 19# __libc_start_main@GLIBC_2.2.5 at :0 20# _start at :0 A change that is not visible in the examples above is that for a non-empty stacktrace_entry, we now print "<unknown>" for the function name if description() returns an empty string. For an empty (e.g. default constructed) stacktrace_entry the entire string representation is now "<unknown>" instead of an empty string. Instead of printing "<unknown>" for the function name, we could set that string in the stacktrace_entry::_Info object, so that description() returns "<unknown>" and then operator<< wouldn't need to handle an empty description() string. However, returning an empty string from that function seems simpler for users to detect, rather than having to parse "<unknown>". We could also choose a different string for an empty stacktrace_entry, maybe "<none>" or "<invalid>", but "<unknown>" seems good. libstdc++-v3/ChangeLog: * include/std/stacktrace (operator<<(ostream&, const stacktrace_entry&)): Improve output when description() or source_file() returns an empty string, or the stacktrace_entry is invalid. Append frame address to output. (operator<<(ostream&, const basic_stacktrace<A>&)): Use the size_type of the correct specialization. Reviewed-by: Tomasz KamiĆski <[email protected]> Reviewed-by: Nathan Myers <[email protected]> Diff: --- libstdc++-v3/include/std/stacktrace | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/std/stacktrace b/libstdc++-v3/include/std/stacktrace index b9e260e19f89..587a163e9766 100644 --- a/libstdc++-v3/include/std/stacktrace +++ b/libstdc++-v3/include/std/stacktrace @@ -683,13 +683,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline ostream& operator<<(ostream& __os, const stacktrace_entry& __f) { + if (!__f) [[unlikely]] + return __os << "<unknown>"; + string __desc, __file; int __line; - if (__f._M_get_info(&__desc, &__file, &__line)) + if (__f._M_get_info(&__desc, &__file, &__line)) [[likely]] { - __os.width(4); - __os << __desc << " at " << __file << ':' << __line; + __os << ' '; + if (__desc.empty()) [[unlikely]] + __os << "<unknown>"; + else + __os << __desc; + if (!__file.empty()) [[likely]] + __os << " at " << __file << ':' << __line; } + + struct _Flag_guard // Set and restore hex format + { + _Flag_guard(ios& __s) : _M_ios(__s) { } + ~_Flag_guard() { _M_ios.setf(_M_f); } + + ios& _M_ios; + ios::fmtflags _M_f = _M_ios.setf(ios::hex, ios::basefield); + }; + _Flag_guard __g(__os); + __os << " [0x" << __f.native_handle() << ']'; return __os; } @@ -697,7 +716,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __st) { - for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i) + using size_type = typename basic_stacktrace<_Allocator>::size_type; + for (size_type __i = 0, __size = __st.size(); __i < __size; ++__i) { __os.width(4); __os << __i << "# " << __st[__i] << '\n';
