Here is what I come up with.
Regarding allocation in print_function I would also prefer to avoid it.
But this patch also aim at creating a backtrace_state object in case of
UB so the alloc is perhaps not so important. I can't use string_view as
I need to modify it to display only a part of it through fsprintf. I
could try to use "%.*s" however. I haven't also consider your remark
about template parameters containing '<' yet.
+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+# if defined(__has_include) && !__has_include(<backtrace-supported.h>)
+# error No libbacktrace backtrace-supported.h file found.
+# endif
+# include <backtrace-supported.h>
+# endif
+# if !BACKTRACE_SUPPORTED
+# error libbacktrace not supported.
+# endif
+# include <backtrace.h>
+#else
+# include <stdint.h> // For uintptr_t.
Please use <cstdint> and std::uintptr_t.
I did so but then realized that to do so I had to be in C++11 mode. I
used tr1/cstdint in pre-C++11 mode.
+// 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*);
These typedefs should use __reserved_names.
+struct backtrace_state;
Although this one can't use a reserved name, unless we're going to
create opaque wrappers around the libbacktrace type. Introducing t his
non-reserved name means that defining _GLIBCXX_DEBUG makes the library
non-conforming.
It would be possible to avoid declaring this struct, by making
_M_backtrace_state a void* and creating a wrapper function for
backtrace_create_state, and a weak symbol in the library. I'll have to
think about this more
My main problem was to be able to respect the ODR even when
!BACKTRACE_SUPPORTED. To do so I eventually realized that I had to limit
the feature to system where uintptr_t is available which I detect thanks
to the _GLIBCXX_USE_C99_STDINT_TR1 macro which is used both in <cstdint>
and <tr1/cstdint>.
If you think it is fine I'll document it.
François
diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 23a5df975a2..680b9d5999d 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -162,6 +162,13 @@ which always works correctly.
<code>GLIBCXX_DEBUG_MESSAGE_LENGTH</code> can be used to request a
different length.</para>
+<para>Starting with GCC 10 libstdc++ is able to use
+ <link xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>
+ to produce backtraces on error. Use <code>-D_GLIBCXX_DEBUG_BACKTRACE</code> to
+ activate it. Note that if not properly installed or if libbacktrace is not
+ supported, compilation will fail. You'll also have to use
+ <code>-lbacktrace</code> to build your application.</para>
</section>
<section xml:id="debug_mode.using.specific" xreflabel="Using Specific"><info><title>Using a Specific Debug Container</title></info>
diff --git a/libstdc++-v3/doc/xml/manual/using.xml b/libstdc++-v3/doc/xml/manual/using.xml
index d7fbfe9584d..5769722192c 100644
--- a/libstdc++-v3/doc/xml/manual/using.xml
+++ b/libstdc++-v3/doc/xml/manual/using.xml
@@ -1128,6 +1128,16 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2 hello.cc -o test.exe
extensions and libstdc++-specific behavior into errors.
</para>
</listitem></varlistentry>
+ <varlistentry><term><code>_GLIBCXX_DEBUG_BACKTRACE</code></term>
+ <listitem>
+ <para>
+ Undefined by default. Considered only if <code>_GLIBCXX_DEBUG</code>
+ is defined. When defined, checks for <link xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>
+ support and use it to display backtraces on
+ <link linkend="manual.ext.debug_mode">debug mode</link> assertions.
+ </para>
+ </listitem></varlistentry>
<varlistentry><term><code>_GLIBCXX_PARALLEL</code></term>
<listitem>
<para>Undefined by default. When defined, compiles user code
@@ -1634,6 +1644,17 @@ A quick read of the relevant part of the GCC
header will remain compatible between different GCC releases.
</para>
</section>
+
+ <section xml:id="manual.intro.using.linkage.externals" xreflabel="External Libraries"><info><title>External Libraries</title></info>
+
+ <para>
+ GCC 10 <link linkend="manual.ext.debug_mode">debug mode</link> is able
+ produce backtraces thanks to <link xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>.
+ To use the library you should define <code>_GLIBCXX_DEBUG_BACKTRACE</code>
+ and link with <option>-lbacktrace</option>.
+ </para>
+ </section>
</section>
<section xml:id="manual.intro.using.concurrency" xreflabel="Concurrency"><info><title>Concurrency</title></info>
diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index 220379994c0..9e5962a4744 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,38 @@
#include <bits/c++config.h>
+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+# include <backtrace-supported.h>
+# endif
+#endif
+
+#if BACKTRACE_SUPPORTED
+# define _GLIBCXX_DEBUG_USE_BACKTRACE 1
+# include <backtrace.h>
+typedef backtrace_error_callback __backtrace_error_cb;
+typedef backtrace_full_callback __backtrace_full_cb;
+typedef backtrace_state __backtrace_state;
+#elif defined (_GLIBCXX_USE_C99_STDINT_TR1)
+# define _GLIBCXX_DEBUG_USE_BACKTRACE 1
+
+# if __cplusplus >= 201103L
+# include <cstdint> // For std::uintptr_t.
+typedef int (*__backtrace_full_cb) (void*, std::uintptr_t, const char*,
+ int, const char*);
+# else
+# include <tr1/cstdint>
+typedef int (*__backtrace_full_cb) (void*, std::tr1::uintptr_t, const char*,
+ int, const char*);
+# endif
+
+typedef void (*__backtrace_error_cb) (void*, const char*, int);
+
+struct __backtrace_state;
+#else
+# define _GLIBCXX_DEBUG_USE_BACKTRACE 0
+#endif
+
#if __cpp_rtti
# include <typeinfo>
# define _GLIBCXX_TYPEID(_Type) &typeid(_Type)
@@ -156,6 +188,12 @@ namespace __gnu_debug
class _Error_formatter
{
+#if _GLIBCXX_DEBUG_USE_BACKTRACE
+ typedef int (*_Bt_full_cb) (__backtrace_state*, int,
+ __backtrace_full_cb,
+ __backtrace_error_cb, void*);
+#endif
+
// Tags denoting the type of parameter for construction
struct _Is_iterator { };
struct _Is_iterator_value_type { };
@@ -558,11 +596,25 @@ namespace __gnu_debug
_M_print_string(const char* __string) const _GLIBCXX_DEPRECATED;
#endif
+#if _GLIBCXX_DEBUG_USE_BACKTRACE
+ void
+ _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const
+ { _M_backtrace_full_func(_M_backtrace_state, 1, __cb, 0, __data); }
+#endif
+
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 _GLIBCXX_DEBUG_USE_BACKTRACE
+# if defined(_GLIBCXX_DEBUG_BACKTRACE) && BACKTRACE_SUPPORTED
+ , _M_backtrace_state(backtrace_create_state(NULL, 0, NULL, NULL))
+ , _M_backtrace_full_func(&backtrace_full)
+# else
+ , _M_backtrace_state()
+# endif
+#endif
{ }
#if !_GLIBCXX_INLINE_VERSION
@@ -578,6 +630,10 @@ namespace __gnu_debug
unsigned int _M_num_parameters;
const char* _M_text;
const char* _M_function;
+#if _GLIBCXX_DEBUG_USE_BACKTRACE
+ __backtrace_state* _M_backtrace_state;
+ _Bt_full_cb _M_backtrace_full_func;
+#endif
public:
static _Error_formatter&
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index f5a49992efa..2e4de272ad8 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -34,16 +34,13 @@
#include <cassert>
#include <cstdio>
-#include <cctype> // for std::isspace
+#include <cctype> // for std::isspace.
+#include <cstring> // for std::strstr.
-#include <algorithm> // for std::min
+#include <algorithm> // for std::min.
+#include <string>
-#include <cxxabi.h> // for __cxa_demangle
-
-// 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"
@@ -571,6 +568,7 @@ namespace
{
PrintContext()
: _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 +576,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)
@@ -619,7 +619,7 @@ namespace
for (int i = 0; i < ctx._M_indent; ++i)
spacing[i] = ' ';
spacing[ctx._M_indent] = '\0';
- fprintf(stderr, "%s", spacing);
+ fprintf(stderr, spacing);
ctx._M_column += ctx._M_indent;
}
@@ -640,6 +640,143 @@ namespace
}
}
+ void
+ print_word(PrintContext& ctx, const char* word)
+ { print_word(ctx, word, -1); }
+
+ void
+ print_raw(PrintContext&, const char* word)
+ { fprintf(stderr, "%s", word); }
+
+ void
+ print_func_impl(PrintContext& ctx, const char* function,
+ _Print_func_t print_func)
+ {
+ const char cxx1998[] = "__cxx1998::";
+ const char allocator[] = ", std::allocator<";
+ const char 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 + sizeof(cxx1998) - 1;
+ }
+ 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 += sizeof(allocator) - 1;
+ 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 += sizeof(safe_iterator) - 1;
+ 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_func_impl(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;
+ }
+ }
+ }
+
+ void
+ print_function(PrintContext& ctx, const char* function,
+ _Print_func_t print_func)
+ {
+ __try
+ { print_func_impl(ctx, function, print_func); }
+ __catch (...)
+ { print_func(ctx, function); }
+ }
+
template<size_t Length>
void
print_type(PrintContext& ctx,
@@ -653,7 +790,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 +1064,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 +1156,65 @@ namespace
print_word(ctx, buf, bufindex);
}
}
+
+ void
+ print_string(PrintContext& ctx, const char* str)
+ { print_string(ctx, str, nullptr, 0); }
+
+#if _GLIBCXX_DEBUG_USE_BACKTRACE
+ int
+ print_backtrace(void* data, std::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;
+ }
+#endif
}
namespace __gnu_debug
@@ -1054,34 +1256,20 @@ 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)
+#if _GLIBCXX_DEBUG_USE_BACKTRACE
+ if (_M_backtrace_state)
{
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);
+ _M_print_backtrace(print_backtrace, &ctx);
ctx._M_first_line = true;
print_literal(ctx, "\n");
}
- }
#endif
print_literal(ctx, "Error: ");