This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
libbacktrace integration for _GLIBCXX_DEBUG mode
- From: François Dumont <frs dot dumont at gmail dot com>
- To: "libstdc++ at gcc dot gnu dot org" <libstdc++ at gcc dot gnu dot org>, gcc-patches <gcc-patches at gcc dot gnu dot org>
- Date: Tue, 11 Dec 2018 00:08:12 +0100
- Subject: libbacktrace integration for _GLIBCXX_DEBUG mode
Hi
Here is the integration of libbacktrace to provide the backtrace on
_GLIBCXX_DEBUG assertions.
I decided to integrate it without impacting the build scripts.
Users just need to install libbacktrace and once done _GLIBCXX_DEBUG
will look for it and start using it if supported. The drawback is that
as soon as libbacktrace is installed users will have to add -lbacktrace
in order to use _GLIBCXX_DEBUG mode. But I expect that if you install
libbacktrace it is for a reason.
Note that when libbacktrace is not supported I include stdint.h to
get uintptr_t, I hope it is the correct way to get it in a portable way.
I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure
libstdc++ has no libbacktrace dependency after usual build.
As it starts to make a lot of information displayed on Debug
assertion I have created print_function to filter output of functions.
It removes things like __cxx1998::, std::allocator and greatly
simplified _Safe_iterator rendering.
Here is an example of output when building
23_containers/vector/debug/construct3_neg.cc:
/home/fdt/dev/gcc/install/include/c++/9.0.0/debug/safe_iterator.h:321:
In function:
__gnu_debug::_Safe_iterator<_Iterator, _Sequence, _Category>&
__gnu_debug::_Safe_iterator<_Iterator, _Sequence,
_Category>::operator++() [with _Iterator = std::_List_iterator<int>;
_Sequence = std::__debug::list<int>; _Category =
std::forward_iterator_tag]
Backtrace:
0x40275f
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>::operator++()
/home/fdt/dev/gcc/install/include/c++/9.0.0/debug/safe_iterator.h:321
0x402181
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>::operator++()
/home/fdt/dev/gcc/install/include/c++/9.0.0/debug/safe_iterator.h:570
0x404082
std::iterator_traits<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>
>::difference_type
std::__distance<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>
>(__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
std::input_iterator_tag)
/home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_iterator_base_funcs.h:89
0x403795
std::iterator_traits<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>
>::difference_type
std::distance<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>
>(__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>)
/home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_iterator_base_funcs.h:141
0x4030b9 void
std::vector<int>::_M_range_initialize<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>
>(__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
std::forward_iterator_tag)
/home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_vector.h:1541
0x402a2d
std::vector<int>::vector<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
void>(__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>)
/home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_vector.h:618
0x4022ec
std::__debug::vector<int>::vector<__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
void>(__gnu_debug::_Safe_iterator<std::_List_iterator<int>>,
__gnu_debug::_Safe_iterator<std::_List_iterator<int>>)
/home/fdt/dev/gcc/install/include/c++/9.0.0/debug/vector:195
0x401e2c void
__gnu_test::check_construct3<std::__debug::vector<int> >()
./util/debug/checks.h:234
0x401460 test01()
/home/fdt/dev/poc/construct3_neg.cc:26
0x40146c main
/home/fdt/dev/poc/construct3_neg.cc:31
Error: attempt to increment a past-the-end iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7fff068adce0 {
type = std::_List_iterator<int> (mutable iterator);
state = past-the-end;
references sequence with type 'std::__debug::list<int>' @
0x0x7fff068ae080
}
* include/debug/formatter.h: Check for backtrace-supported.h access
and include it.
[BACKTRACE_SUPPORTED] Include <backtrace.h>
(_Error_formatter::_Bt_full_t): New function pointer type.
(_Error_formatter::_M_backtrace_state): New.
(_Error_formatter::_M_backtrace_full_func): New.
* src/c++11/debug.cc: Include <cstring>.
(PrintContext::_M_demangle_name): New.
(_Print_func_t): New.
(print_word(PrintContext&, const char*)): New.
(print_raw(PrintContext&, const char*)): New.
(print_function(PrintContext&, const char*, _Print_func_t)): New.
(print_type): Use latter.
(print_string(PrintContext&, const char*)): New.
(print_backtrace(void*, uintptr_t, const char*, int, const char*)):
New.
(_Error_formatter::_M_error()): Adapt.
Tested under Linux x86_64.
Ok to commit ? One day ?
François
diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index 1f03f251488..8fb5e57a4d4 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,25 @@
#include <bits/c++config.h>
+#if !defined(BACKTRACE_SUPPORTED) \
+ && defined(__has_include) && __has_include(<backtrace-supported.h>)
+# include <backtrace-supported.h>
+#endif
+
+#if BACKTRACE_SUPPORTED
+# include <backtrace.h>
+#else
+# include <stdint.h> // For uintptr_t.
+
+// Extracted from libbacktrace.
+typedef void (*backtrace_error_callback) (void*, const char*, int);
+
+typedef int (*backtrace_full_callback) (void*, uintptr_t, const char*, int,
+ const char*);
+
+struct backtrace_state;
+#endif
+
#if __cpp_rtti
# include <typeinfo>
# define _GLIBCXX_TYPEID(_Type) &typeid(_Type)
@@ -156,6 +175,9 @@ namespace __gnu_debug
class _Error_formatter
{
+ typedef int (*_Bt_full_t) (backtrace_state*, int, backtrace_full_callback,
+ backtrace_error_callback, void*);
+
// Tags denoting the type of parameter for construction
struct _Is_iterator { };
struct _Is_iterator_value_type { };
@@ -558,11 +580,21 @@ namespace __gnu_debug
_M_print_string(const char* __string) const _GLIBCXX_DEPRECATED;
#endif
+ void
+ _M_print_backtrace(backtrace_full_callback __cb, void* __data) const
+ { _M_backtrace_full_func(_M_backtrace_state, 1, __cb, 0, __data); }
+
private:
_Error_formatter(const char* __file, unsigned int __line,
const char* __function)
: _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0)
, _M_function(__function)
+#if BACKTRACE_SUPPORTED
+ , _M_backtrace_state(backtrace_create_state(NULL, 0, NULL, NULL))
+ , _M_backtrace_full_func(&backtrace_full)
+#else
+ , _M_backtrace_state()
+#endif
{ }
#if !_GLIBCXX_INLINE_VERSION
@@ -578,6 +610,8 @@ namespace __gnu_debug
unsigned int _M_num_parameters;
const char* _M_text;
const char* _M_function;
+ backtrace_state* _M_backtrace_state;
+ _Bt_full_t _M_backtrace_full_func;
public:
static _Error_formatter&
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index 88fe889dd6a..9922adf3c00 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -22,6 +22,9 @@
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
+// Make sure we won't have libbacktrace dependency when building the library.
+#define BACKTRACE_SUPPORTED 0
+
#include <bits/move.h>
#include <bits/stl_iterator_base_types.h>
@@ -34,16 +37,13 @@
#include <cassert>
#include <cstdio>
-#include <cctype> // for std::isspace
-
-#include <algorithm> // for std::min
+#include <cctype> // for std::isspace.
+#include <cstring> // for std::strstr.
-#include <cxxabi.h> // for __cxa_demangle
+#include <algorithm> // for std::min.
+#include <string>
-// libstdc++/85768
-#if 0 // defined _GLIBCXX_HAVE_EXECINFO_H
-# include <execinfo.h> // for backtrace
-#endif
+#include <cxxabi.h> // for __cxa_demangle.
#include "mutex_pool.h"
@@ -570,7 +570,8 @@ namespace
struct PrintContext
{
PrintContext()
- : _M_max_length(78), _M_column(1), _M_first_line(true), _M_wordwrap(false)
+ : _M_max_length(78), _M_column(1), _M_first_line(true), _M_wordwrap(false)
+ , _M_demangle_name(false)
{ get_max_length(_M_max_length); }
std::size_t _M_max_length;
@@ -578,16 +579,18 @@ namespace
std::size_t _M_column;
bool _M_first_line;
bool _M_wordwrap;
+ bool _M_demangle_name;
};
+ typedef void (*_Print_func_t) (PrintContext&, const char*);
+
template<size_t Length>
void
print_literal(PrintContext& ctx, const char(&word)[Length])
{ print_word(ctx, word, Length - 1); }
void
- print_word(PrintContext& ctx, const char* word,
- std::ptrdiff_t count = -1)
+ print_word(PrintContext& ctx, const char* word, std::ptrdiff_t count)
{
size_t length = count >= 0 ? count : __builtin_strlen(word);
if (length == 0)
@@ -623,7 +626,7 @@ namespace
ctx._M_column += ctx._M_indent;
}
- int written = fprintf(stderr, "%s", word);
+ int written = fprintf(stderr, word);
if (word[length - 1] == '\n')
{
@@ -640,6 +643,133 @@ namespace
}
}
+ void
+ print_word(PrintContext& ctx, const char* word)
+ { print_word(ctx, word, -1); }
+
+ void
+ print_raw(PrintContext&, const char* word)
+ { fprintf(stderr, word); }
+
+ void
+ print_function(PrintContext& ctx, const char* function,
+ _Print_func_t print_func)
+ {
+ const string cxx1998 = "__cxx1998::";
+ const string allocator = ", std::allocator<";
+ const string safe_iterator = "__gnu_debug::_Safe_iterator<";
+ string func(function);
+ string::size_type index = 0;
+ for (;;)
+ {
+ auto idx1 = func.find(cxx1998, index);
+ auto idx2 = func.find(allocator, index);
+ auto idx3 = ctx._M_demangle_name
+ ? func.find(safe_iterator, index) : string::npos;
+ if (idx1 != string::npos || idx2 != string::npos ||
+ idx3 != string::npos)
+ {
+ if (idx1 != string::npos
+ && (idx2 == string::npos || idx1 < idx2)
+ && (idx3 == string::npos || idx1 < idx3))
+ {
+ func[idx1] = '\0';
+ print_func(ctx, func.c_str() + index);
+ index = idx1 + cxx1998.size();
+ }
+ else if (idx2 != string::npos
+ && (idx3 == string::npos || idx2 < idx3))
+ {
+ func[idx2] = '\0';
+ print_func(ctx, func.c_str() + index);
+
+ // We need to look for the closing '>'
+ idx2 += allocator.size();
+ int open_bracket = 0;
+ for (; idx2 != func.size(); ++idx2)
+ {
+ if (func[idx2] == '>')
+ {
+ if (open_bracket)
+ --open_bracket;
+ else
+ break;
+ }
+ else if (func[idx2] == '<')
+ ++open_bracket;
+ }
+
+ index = idx2 + 1;
+ idx2 = func.find(" const&", index);
+ if (idx2 == index)
+ // Allocator parameter qualification, skipped too.
+ index += sizeof(" const&") - 1;
+ }
+ else if (idx3 != string::npos)
+ {
+ // Just keep the 1st template parameter.
+ // Look for 1st comma outside any additional brackets.
+ idx3 += safe_iterator.size();
+ char c = func[idx3];
+ func[idx3] = '\0';
+ print_func(ctx, func.c_str() + index);
+ func[idx3] = c;
+ index = idx3;
+ int nb_open_bracket = 0;
+ for (; nb_open_bracket != -1;)
+ {
+ auto n = func.find_first_of("<,>", idx3);
+ switch (func[n])
+ {
+ case '<':
+ ++nb_open_bracket;
+ break;
+ case '>':
+ --nb_open_bracket;
+ break;
+ case ',':
+ if (nb_open_bracket == 0)
+ {
+ func[n] = '\0';
+ print_function(ctx, func.c_str() + index, print_func);
+ nb_open_bracket = -1;
+ }
+
+ break;
+ }
+
+ idx3 = n + 1;
+ }
+
+ // Now look for the closing '>'.
+ nb_open_bracket = 0;
+ for (; idx3 != func.size(); ++idx3)
+ {
+ if (func[idx3] == '>')
+ {
+ if (nb_open_bracket)
+ --nb_open_bracket;
+ else
+ break;
+ }
+ else if (func[idx3] == '<')
+ ++nb_open_bracket;
+ }
+
+ index = idx3;
+ }
+
+ while (isspace(func[index]))
+ ++index;
+ }
+ else
+ {
+ print_func(ctx, func.c_str() + index);
+ break;
+ }
+ }
+ }
+
template<size_t Length>
void
print_type(PrintContext& ctx,
@@ -653,7 +783,13 @@ namespace
int status;
char* demangled_name =
__cxxabiv1::__cxa_demangle(info->name(), NULL, NULL, &status);
- print_word(ctx, status == 0 ? demangled_name : info->name());
+ if (status == 0)
+ {
+ ctx._M_demangle_name = true;
+ print_function(ctx, demangled_name, &print_word);
+ }
+ else
+ print_word(ctx, info->name());
free(demangled_name);
}
}
@@ -921,10 +1057,10 @@ namespace
}
void
- print_string(PrintContext& ctx, const char* string,
+ print_string(PrintContext& ctx, const char* str,
const _Parameter* parameters, std::size_t num_parameters)
{
- const char* start = string;
+ const char* start = str;
const int bufsize = 128;
char buf[bufsize];
int bufindex = 0;
@@ -1013,6 +1149,63 @@ namespace
print_word(ctx, buf, bufindex);
}
}
+
+ void
+ print_string(PrintContext& ctx, const char* str)
+ { print_string(ctx, str, nullptr, 0); }
+
+ int
+ print_backtrace(void* data, uintptr_t pc, const char* filename,
+ int lineno, const char* function)
+ {
+ const int bufsize = 64;
+ char buf[bufsize];
+
+ PrintContext& ctx = *static_cast<PrintContext*>(data);
+
+ int written = __builtin_sprintf(buf, "%p ", (void*)pc);
+ print_word(ctx, buf, written);
+
+ int ret = 0;
+ if (function)
+ {
+ int status;
+ char* demangled_name =
+ __cxxabiv1::__cxa_demangle(function, NULL, NULL, &status);
+ if (status == 0)
+ {
+ ctx._M_demangle_name = true;
+ print_function(ctx, demangled_name, &print_raw);
+ }
+ else
+ print_word(ctx, function);
+
+ free(demangled_name);
+ ret = strstr(function, "main") ? 1 : 0;
+ }
+
+ print_literal(ctx, "\n");
+
+ if (filename)
+ {
+ bool wordwrap = false;
+ swap(wordwrap, ctx._M_wordwrap);
+ print_word(ctx, filename);
+
+ if (lineno)
+ {
+ written = __builtin_sprintf(buf, ":%u\n", lineno);
+ print_word(ctx, buf, written);
+ }
+ else
+ print_literal(ctx, "\n");
+ swap(wordwrap, ctx._M_wordwrap);
+ }
+ else
+ print_literal(ctx, "???:0\n");
+
+ return ret;
+ }
}
namespace __gnu_debug
@@ -1054,35 +1247,19 @@ namespace __gnu_debug
if (_M_function)
{
print_literal(ctx, "In function:\n");
- print_string(ctx, _M_function, nullptr, 0);
+ print_function(ctx, _M_function, &print_string);
print_literal(ctx, "\n");
ctx._M_first_line = true;
print_literal(ctx, "\n");
}
-// libstdc++/85768
-#if 0 //defined _GLIBCXX_HAVE_EXECINFO_H
- {
- void* stack[32];
- int nb = backtrace(stack, 32);
-
- // Note that we skip current method symbol.
- if (nb > 1)
- {
- print_literal(ctx, "Backtrace:\n");
- auto symbols = backtrace_symbols(stack, nb);
- for (int i = 1; i < nb; ++i)
- {
- print_word(ctx, symbols[i]);
- print_literal(ctx, "\n");
- }
-
- free(symbols);
- ctx._M_first_line = true;
- print_literal(ctx, "\n");
- }
- }
-#endif
+ if (_M_backtrace_state)
+ {
+ print_literal(ctx, "Backtrace:\n");
+ _M_print_backtrace(print_backtrace, &ctx);
+ ctx._M_first_line = true;
+ print_literal(ctx, "\n");
+ }
print_literal(ctx, "Error: ");