diff mbox series

[_GLIBCXX_DEBUG] libbacktrace integration

Message ID e24b9d01-863e-5fb3-7adb-c96053875b5c@gmail.com
State New
Headers show
Series [_GLIBCXX_DEBUG] libbacktrace integration | expand

Commit Message

François Dumont April 24, 2021, 1:46 p.m. UTC
Hi

     Here is the patch to add backtrace generation on _GLIBCXX_DEBUG 
assertions thanks to libbacktrace.

     In addition to this integration I am also improving the generation 
of the assertion message thanks to the "%.*s" printf format, it avoids 
an intermediate buffer most of the time. I am also removing the "__" 
used for uglification to get a nicer output. I can propose it in a 
dedicated patch if you prefer.

     I am adding GLIBCXX_3.4.30 abi version to properly export the 2 new 
weak symbols. Let me know if it isn't necessary.

     libstdc++: [_GLIBCXX_DEBUG] Add backtrace generation thanks to 
libbacktrace

       Add _GLIBCXX_DEBUG_BACKTRACE macro to activate backtrace 
generation on
     _GLIBCXX_DEBUG assertions using libbacktrace.

             * config/abi/pre/gnu.ver: Add GLIBCXX_3.4.30 version and 
new exports.
             * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]:
             Include <backtrace-supported.h>.
             [_GLIBCXX_DEBUG_BACKTRACE && BACKTRACE_SUPPORTED]:
             Include <backtrace.h>.
             [(!_GLIBCXX_DEBUG_BACKTRACE || !BACKTRACE_SUPPORTED) &&
             _GLIBCXX_USE_C99_STDINT_TR1]: Include <stdint.h>.
             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
             (__gnu_debug::__create_backtrace_state): New.
             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
             (__gnu_debug::__render_backtrace): New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_print_backtrace):
             New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_backtrace_state):
             New.
             (_Error_formatter::_Error_formatter): Outline definition.
             * src/c++11/debug.cc: Include <cstring>.
             (_Print_func_t): New.
             (print_word): Use '%.*s' format in fprintf to render only 
expected
             number of chars.
             (print_raw(PrintContext&, const char*, ptrdiff_t)): New.
             (print_function(PrintContext&, const char*, 
_Print_func_t)): New.
             (print_type): Use latter.
             (print_string(PrintContext&, const char*, const 
_Parameter*, size_t)):
             Change signature to...
             (print_string(PrintContext&, const char*, ptrdiff_t, const 
_Parameter*,
             size_t)): ...this and adapt. Remove intermediate buffer to 
render input
             string.
             (print_string(PrintContext&, const char*, ptrdiff_t)): New.
             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
             (print_backtrace(void*, uintptr_t, const char*, int, const 
char*)): New.
             (_Error_formatter::_M_error()): Adapt.
             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
             (__gnu_debug::__create_backtrace_state): New, weak symbol.
             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
             (__gnu_debug::__render_backtrace): New, weak symbol.
             * testsuite/util/testsuite_abi.cc: Add new symbol version.
             * doc/xml/manual/debug_mode.xml: Document 
_GLIBCXX_DEBUG_BACKTRACE.
             * doc/xml/manual/using.xml: Likewise.

Tested under Linux x86_64.

Ok to commit ?

François

Comments

François Dumont May 3, 2021, 8:17 p.m. UTC | #1
Is it too early to consider this patch ? Or just lack of time ?

Considering the patch I would really appreciate that, if validated, it 
gets in as early as possible in next release.

Thanks,
François

On 24/04/21 3:46 pm, François Dumont wrote:
> Hi
>
>     Here is the patch to add backtrace generation on _GLIBCXX_DEBUG 
> assertions thanks to libbacktrace.
>
>     In addition to this integration I am also improving the generation 
> of the assertion message thanks to the "%.*s" printf format, it avoids 
> an intermediate buffer most of the time. I am also removing the "__" 
> used for uglification to get a nicer output. I can propose it in a 
> dedicated patch if you prefer.
>
>     I am adding GLIBCXX_3.4.30 abi version to properly export the 2 
> new weak symbols. Let me know if it isn't necessary.
>
>     libstdc++: [_GLIBCXX_DEBUG] Add backtrace generation thanks to 
> libbacktrace
>
>       Add _GLIBCXX_DEBUG_BACKTRACE macro to activate backtrace 
> generation on
>     _GLIBCXX_DEBUG assertions using libbacktrace.
>
>             * config/abi/pre/gnu.ver: Add GLIBCXX_3.4.30 version and 
> new exports.
>             * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]:
>             Include <backtrace-supported.h>.
>             [_GLIBCXX_DEBUG_BACKTRACE && BACKTRACE_SUPPORTED]:
>             Include <backtrace.h>.
>             [(!_GLIBCXX_DEBUG_BACKTRACE || !BACKTRACE_SUPPORTED) &&
>             _GLIBCXX_USE_C99_STDINT_TR1]: Include <stdint.h>.
>             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>             (__gnu_debug::__create_backtrace_state): New.
>             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>             (__gnu_debug::__render_backtrace): New.
> [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_print_backtrace):
>             New.
> [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_backtrace_state):
>             New.
>             (_Error_formatter::_Error_formatter): Outline definition.
>             * src/c++11/debug.cc: Include <cstring>.
>             (_Print_func_t): New.
>             (print_word): Use '%.*s' format in fprintf to render only 
> expected
>             number of chars.
>             (print_raw(PrintContext&, const char*, ptrdiff_t)): New.
>             (print_function(PrintContext&, const char*, 
> _Print_func_t)): New.
>             (print_type): Use latter.
>             (print_string(PrintContext&, const char*, const 
> _Parameter*, size_t)):
>             Change signature to...
>             (print_string(PrintContext&, const char*, ptrdiff_t, const 
> _Parameter*,
>             size_t)): ...this and adapt. Remove intermediate buffer to 
> render input
>             string.
>             (print_string(PrintContext&, const char*, ptrdiff_t)): New.
>             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>             (print_backtrace(void*, uintptr_t, const char*, int, const 
> char*)): New.
>             (_Error_formatter::_M_error()): Adapt.
>             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>             (__gnu_debug::__create_backtrace_state): New, weak symbol.
>             [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>             (__gnu_debug::__render_backtrace): New, weak symbol.
>             * testsuite/util/testsuite_abi.cc: Add new symbol version.
>             * doc/xml/manual/debug_mode.xml: Document 
> _GLIBCXX_DEBUG_BACKTRACE.
>             * doc/xml/manual/using.xml: Likewise.
>
> Tested under Linux x86_64.
>
> Ok to commit ?
>
> François
>
Jonathan Wakely May 3, 2021, 9:06 p.m. UTC | #2
On 03/05/21 22:17 +0200, François Dumont via Libstdc++ wrote:
>Is it too early to consider this patch ? Or just lack of time ?

I haven't had time to review it yet, but my general feeling hasn't
changed. I still don't like the idea of executing additional code
after undefined behaviour is detected. I've been convinced by glibc
folk that every bit of code run when the program state is corrupt
increases the risk that it can be exploited by an attacker.
François Dumont May 4, 2021, 6:03 a.m. UTC | #3
On 03/05/21 11:06 pm, Jonathan Wakely wrote:
> On 03/05/21 22:17 +0200, François Dumont via Libstdc++ wrote:
>> Is it too early to consider this patch ? Or just lack of time ?
>
> I haven't had time to review it yet, but my general feeling hasn't
> changed. I still don't like the idea of executing additional code
> after undefined behaviour is detected. I've been convinced by glibc
> folk that every bit of code run when the program state is corrupt
> increases the risk that it can be exploited by an attacker.
>
>
>
Ok, I must have miss (or forgot) this feedback.

Well, isn't it the current situation of the whole _GLIBCXX_DEBUG mode ?

For me _GLIBCXX_DEBUG mode purpose is to detect UB situation and to 
assert _before_ any UB code is run.

Moreover it is optional. This is a feature to use when _GLIBCXX_DEBUG is 
telling you that you have a problem in your code but you just cannot 
find where it is called.
Jonathan Wakely May 5, 2021, 10:01 a.m. UTC | #4
On 04/05/21 08:03 +0200, François Dumont wrote:
>On 03/05/21 11:06 pm, Jonathan Wakely wrote:
>>On 03/05/21 22:17 +0200, François Dumont via Libstdc++ wrote:
>>>Is it too early to consider this patch ? Or just lack of time ?
>>
>>I haven't had time to review it yet, but my general feeling hasn't
>>changed. I still don't like the idea of executing additional code
>>after undefined behaviour is detected. I've been convinced by glibc
>>folk that every bit of code run when the program state is corrupt
>>increases the risk that it can be exploited by an attacker.
>>
>>
>>
>Ok, I must have miss (or forgot) this feedback.

See https://gcc.gnu.org/pipermail/libstdc++/2018-December/048061.html

>Well, isn't it the current situation of the whole _GLIBCXX_DEBUG mode ?

Yes, but adding more code makes it worse.

>For me _GLIBCXX_DEBUG mode purpose is to detect UB situation and to 
>assert _before_ any UB code is run.

Yes, it stops running the user code, but then runs its own code to
format the message to show to the user. The more code that runs when
the program is in an inconsistent/undefined state, the more likely it
is that some of that code can be exploited to do something bad.

>Moreover it is optional. This is a feature to use when _GLIBCXX_DEBUG 
>is telling you that you have a problem in your code but you just 
>cannot find where it is called.

Which you can do with a debugger. When debug mode calls abort() it
will stop the program in a debugger, or produce a core file that can
be examined in a debugger.

The stacktrace is a convenience, not providing anything that couldn't
be done already.

Anyway, I'll review the patch...
Jonathan Wakely May 5, 2021, 11:33 a.m. UTC | #5
On 24/04/21 15:46 +0200, François Dumont via Libstdc++ wrote:
>Hi
>
>    Here is the patch to add backtrace generation on _GLIBCXX_DEBUG 
>assertions thanks to libbacktrace.
>
>    In addition to this integration I am also improving the generation 
>of the assertion message thanks to the "%.*s" printf format, it avoids 
>an intermediate buffer most of the time. I am also removing the "__" 
>used for uglification to get a nicer output. I can propose it in a 
>dedicated patch if you prefer.
>
>    I am adding GLIBCXX_3.4.30 abi version to properly export the 2 
>new weak symbols. Let me know if it isn't necessary.
>
>    libstdc++: [_GLIBCXX_DEBUG] Add backtrace generation thanks to 
>libbacktrace
>
>      Add _GLIBCXX_DEBUG_BACKTRACE macro to activate backtrace 
>generation on
>    _GLIBCXX_DEBUG assertions using libbacktrace.
>
>            * config/abi/pre/gnu.ver: Add GLIBCXX_3.4.30 version and 
>new exports.
>            * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]:
>            Include <backtrace-supported.h>.
>            [_GLIBCXX_DEBUG_BACKTRACE && BACKTRACE_SUPPORTED]:
>            Include <backtrace.h>.
>            [(!_GLIBCXX_DEBUG_BACKTRACE || !BACKTRACE_SUPPORTED) &&
>            _GLIBCXX_USE_C99_STDINT_TR1]: Include <stdint.h>.
>            [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>            (__gnu_debug::__create_backtrace_state): New.
>            [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>            (__gnu_debug::__render_backtrace): New.
>[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_print_backtrace):
>            New.
>[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_backtrace_state):
>            New.
>            (_Error_formatter::_Error_formatter): Outline definition.
>            * src/c++11/debug.cc: Include <cstring>.
>            (_Print_func_t): New.
>            (print_word): Use '%.*s' format in fprintf to render only 
>expected
>            number of chars.
>            (print_raw(PrintContext&, const char*, ptrdiff_t)): New.
>            (print_function(PrintContext&, const char*, 
>_Print_func_t)): New.
>            (print_type): Use latter.
>            (print_string(PrintContext&, const char*, const 
>_Parameter*, size_t)):
>            Change signature to...
>            (print_string(PrintContext&, const char*, ptrdiff_t, const 
>_Parameter*,
>            size_t)): ...this and adapt. Remove intermediate buffer to 
>render input
>            string.
>            (print_string(PrintContext&, const char*, ptrdiff_t)): New.
>            [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>            (print_backtrace(void*, uintptr_t, const char*, int, const 
>char*)): New.
>            (_Error_formatter::_M_error()): Adapt.
>            [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>            (__gnu_debug::__create_backtrace_state): New, weak symbol.
>            [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
>            (__gnu_debug::__render_backtrace): New, weak symbol.
>            * testsuite/util/testsuite_abi.cc: Add new symbol version.
>            * doc/xml/manual/debug_mode.xml: Document 
>_GLIBCXX_DEBUG_BACKTRACE.
>            * doc/xml/manual/using.xml: Likewise.
>
>Tested under Linux x86_64.
>
>Ok to commit ?
>
>François
>

>diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
>index 5323c7f0604..2606d67d8a9 100644
>--- a/libstdc++-v3/config/abi/pre/gnu.ver
>+++ b/libstdc++-v3/config/abi/pre/gnu.ver
>@@ -2397,6 +2397,15 @@ GLIBCXX_3.4.29 {
> 
> } GLIBCXX_3.4.28;
> 
>+GLIBCXX_3.4.30 {
>+
>+    # __gnu_debug::__create_backtrace
>+    _ZN11__gnu_debug24__create_backtrace_stateEv;
>+    _ZN11__gnu_debug18__render_backtraceEPvPFiS0_mPKciS2_ES0_;
>+
>+} GLIBCXX_3.4.29;
>+
>+
> # Symbols in the support library (libsupc++) have their own tag.
> CXXABI_1.3 {
> 
>diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
>index 883e8cb4f03..931b09710f3 100644
>--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
>+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
>@@ -160,6 +160,12 @@ which always works correctly.
>   <code>GLIBCXX_DEBUG_MESSAGE_LENGTH</code> can be used to request a
>   different length.</para>
> 
>+<para>Note that 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. You'll also have to link with libbacktrace
>+  (<option>-lbacktrace</option>) 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 24543e9526e..9bd0da8c1c5 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

s/and use it/and uses it/

>+	<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>
>+      libstdc++ <link linkend="manual.ext.debug_mode">debug mode</link> is able to
>+      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> along
>+      with <code>_GLIBCXX_DEBUG</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 7140fed5e83..ec0984e438f 100644
>--- a/libstdc++-v3/include/debug/formatter.h
>+++ b/libstdc++-v3/include/debug/formatter.h
>@@ -31,6 +31,31 @@
> 
> #include <bits/c++config.h>
> 
>+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
>+# if !defined(BACKTRACE_SUPPORTED)

I think this should also depend on __has_include(<backtrace-supported.h>)


#if defined(_GLIBCXX_DEBUG_BACKTRACE)
# if !defined(BACKTRACE_SUPPORTED) && __has_include(<backtrace-supported.h>)
#  include <backtrace-supported.h>
# endif
#endif



>+#  include <backtrace-supported.h>
>+# endif
>+#endif
>+
>+#if defined(_GLIBCXX_DEBUG_BACKTRACE) && BACKTRACE_SUPPORTED
>+# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 1
>+# include <backtrace.h>
>+
>+namespace __gnu_debug
>+{
>+  typedef backtrace_full_callback __backtrace_full_cb;
>+}
>+#elif defined (_GLIBCXX_USE_C99_STDINT_TR1)
>+# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 0

The interaction between _GLIBCXX_DEBUG_USE_LIBBACKTRACE==1 and
_GLIBCXX_DEBUG_USE_LIBBACKTRACE==0 is a bit subtle and should be
documented in a comment.

If I'm reading this correctly, we will always define it (at least when
USE_C99_STDINT_TR1 is defined ... which is always), but we don't
always use libbacktrace.

>+# include <stdint.h> // For uintptr_t.
>+
>+namespace __gnu_debug
>+{
>+  typedef int (*__backtrace_full_cb) (void*, uintptr_t, const char*,
>+				      int, const char*);

I don't think we need this type. You can just use void(*)() as the
callback argument.

>+}
>+#endif
>+
> #if __cpp_rtti
> # include <typeinfo>
> # define _GLIBCXX_TYPEID(_Type) &typeid(_Type)
>@@ -155,6 +180,16 @@ namespace __gnu_debug
>     __msg_irreflexive_ordering
>   };
> 
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+
>+  void*
>+  __create_backtrace_state();
>+
>+  void
>+  __render_backtrace(void*, __backtrace_full_cb, void*);
>+
>+#endif
>+
>   class _Error_formatter
>   {
>     // Tags denoting the type of parameter for construction
>@@ -558,12 +593,14 @@ namespace __gnu_debug
>     _M_print_string(const char* __string) const _GLIBCXX_DEPRECATED;
> #endif
> 
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+    void
>+    _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const;
>+#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)
>-    { }
>+		     const char* __function);
> 
> #if !_GLIBCXX_INLINE_VERSION
>     void
>@@ -578,6 +615,9 @@ namespace __gnu_debug
>     unsigned int	_M_num_parameters;
>     const char*		_M_text;
>     const char*		_M_function;
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+    void*		_M_backtrace_state;
>+#endif

This member could be defined unconditionally, and just initialize it
to 0 in the constructor if not producing backtraces.

> 
>   public:
>     static _Error_formatter&
>@@ -587,6 +627,36 @@ namespace __gnu_debug
>       return __formatter;
>     }
>   };
>+
>+#if _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+
>+  void*
>+  __create_backtrace_state()
>+  { return backtrace_create_state(NULL, 0, NULL, NULL); }
>+
>+  void
>+  __render_backtrace(void* __bt, __backtrace_full_cb __cb, void* __data)
>+  { backtrace_full((backtrace_state*)__bt, 1, __cb, 0, __data); }

These two functions need to be 'inline' don't they?

And if you pass void(*)() as the callback you can just cast it back to
the right type here:

   void
   __render_backtrace(void* __bt, void (*__cb)(), void* __data)
   {
     backtrace_full((backtrace_state*)__bt, 1,
                    (backtrace_full_callback)__cb, 0, __data);
   }

That way you don't need the __backtrace_full_cb typedef defined
anywhere. Pass around void(*)() and cast to the right type only when
you actually use it, where we know that libbacktrace is supported.


>+#endif
>+
>+  _Error_formatter::
>+  _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)
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+  , _M_backtrace_state(__create_backtrace_state())
>+#endif
>+  { }
>+
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+  void
>+  _Error_formatter::
>+  _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const
>+  { __render_backtrace(_M_backtrace_state, __cb, __data); }
>+#endif

And these need to be inline too. Maybe just define them in the class body again?

> } // namespace __gnu_debug
> 
> #undef _GLIBCXX_TYPEID
>diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
>index 5a642097d17..790810fdab4 100644
>--- a/libstdc++-v3/src/c++11/debug.cc
>+++ b/libstdc++-v3/src/c++11/debug.cc
>@@ -34,16 +34,12 @@
> 
> #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 <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"
> 
>@@ -574,7 +570,7 @@ 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)
>     { get_max_length(_M_max_length); }
> 
>     std::size_t	_M_max_length;
>@@ -584,16 +580,17 @@ namespace
>     bool	_M_wordwrap;
>   };
> 
>+  typedef void (*_Print_func_t) (PrintContext&, const char*, ptrdiff_t);

This can use an alias-declaration to be more readable:

   using _Print_func_t = void (PrintContext&, const char*, ptrdiff_t);



>+  void
>+  print_function(PrintContext& ctx, const char* func,
>+		 _Print_func_t print_func)
>+  {
>+    const char cxx1998[] = "__cxx1998::";
>+    const char uglification[] = "__";
>+    for (;;)
>+      {
>+	auto idx1 = strstr(func, cxx1998);
>+	auto idx2 = strstr(func, uglification);
>+	if (idx1 || idx2)

Checking idx1 is redundant, because if the func name contains
"__cxx1998" then it also contains "__"

>+	  {
>+	    if (idx1 && (!idx2 || idx1 <= idx2))

It's impossible for idx1 && !idx2 to be true.

It's also impossible for idx1 < idx2 to be true.

I think this can be:

   if (auto idx = strstr(func, cxx1998))
     {
       ...
     }
   else if (idx = strstr(func, uglification))
     {
       ...
     }
   else


>+	      {
>+		if (idx1 != func)
>+		  print_func(ctx, func, idx1 - func);
>+
>+		func = idx1 + sizeof(cxx1998) - 1;
>+	      }
>+	    else if (idx2)
>+	      {
>+		if (idx2 != func)
>+		  print_func(ctx, func, idx2 - func);
>+
>+		func = idx2 + sizeof(uglification) - 1;
>+	      }
>+
>+	    while (*func && isspace(*func))

This needs to be isspace((unsigned char)*func), see
https://en.cppreference.com/w/cpp/string/byte/isspace#Notes

It's probably unlikely we'll get negative char values in these
backtraces, but let's not risk it.

>+	      ++func;
>+
>+	    if (!*func)
>+	      break;
>+	  }
>+	else
>+	  {
>+	    print_func(ctx, func, -1);
>+	    break;
>+	  }
>       }
>   }
> 
>@@ -657,7 +701,10 @@ 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)
>+	    print_function(ctx, demangled_name, &print_word);
>+	  else
>+	    print_word(ctx, info->name(), -1);
> 	  free(demangled_name);
> 	}
>     }
>@@ -925,61 +972,62 @@ namespace
>   }
> 
>   void
>-  print_string(PrintContext& ctx, const char* string,
>+  print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc,
> 	       const _Parameter* parameters, std::size_t num_parameters)
>   {
>-    const char* start = string;
>-    const int bufsize = 128;
>-    char buf[bufsize];
>-    int bufindex = 0;
>+    const char* start = str;
>+    const char* end = nbc >= 0 ? start + nbc : nullptr;
>+    char buf[64];
> 
>-    while (*start)
>+    while ((end && str != end) || (!end && *str))
>       {
>-	if (isspace(*start))
>+	if (isspace(*str))

Again, this needs to be isspace((unsigned char)*str).

> 	  {
>-	    buf[bufindex++] = *start++;
>-	    buf[bufindex] = '\0';
>-	    print_word(ctx, buf, bufindex);
>-	    bufindex = 0;
>+	    ++str;
>+	    print_word(ctx, start, str - start);
>+	    start = str;
> 	    continue;
> 	  }
> 
>-	if (!num_parameters || *start != '%')
>+	if (!parameters || *str != '%')
> 	  {
> 	    // Normal char or no parameter to look for.
>-	    buf[bufindex++] = *start++;
>+	    ++str;
> 	    continue;
> 	  }
> 
>-	if (*++start == '%')
>+	if (*++str == '%')
> 	  {
> 	    // Escaped '%'
>-	    buf[bufindex++] = *start++;
>+	    print_word(ctx, start, str - start);
>+	    ++str;
>+	    start = str;
> 	    continue;
> 	  }

All this string manipulation code scares me, this is exactly the kind
of thing I'm talking about when I say that running extra code between
detecting UB and terminating the process is risky.

I know we already have this code, but it's exactly the kind of code
where subtle bugs creep in and nobody notices :-(

> 	// We are on a parameter property reference, we need to flush buffer
> 	// first.
>-	if (bufindex != 0)
>+	if (str != start)
> 	  {
>-	    buf[bufindex] = '\0';
>-	    print_word(ctx, buf, bufindex);
>-	    bufindex = 0;
>+	    // Avoid to print the '%'.

s/Avoid to print/Avoid printing/

>+	    if (str - start > 1)
>+	      print_word(ctx, start, str - start - 1);
>+	    start = str;
> 	  }
> 
> 	// Get the parameter number
>-	assert(*start >= '1' && *start <= '9');
>-	size_t param_index = *start - '0' - 1;
>+	assert(*str >= '1' && *str <= '9');
>+	size_t param_index = *str - '0' - 1;
> 	assert(param_index < num_parameters);
> 	const auto& param = parameters[param_index];
> 
> 	// '.' separates the parameter number from the field
> 	// name, if there is one.
>-	++start;
>-	if (*start != '.')
>+	++str;
>+	if (*str != '.')
> 	  {
>-	    assert(*start == ';');
>-	    ++start;
>+	    assert(*str == ';');
>+	    ++str;
> 	    if (param._M_kind == _Parameter::__integer)
> 	      {
> 		int written
>@@ -988,8 +1036,9 @@ namespace
> 		print_word(ctx, buf, written);
> 	      }
> 	    else if (param._M_kind == _Parameter::__string)
>-	      print_string(ctx, param._M_variant._M_string._M_value,
>+	      print_string(ctx, param._M_variant._M_string._M_value, -1,
> 			   parameters, num_parameters);
>+	    start = str;
> 	    continue;
> 	  }
> 
>@@ -997,26 +1046,80 @@ namespace
> 	const int max_field_len = 16;
> 	char field[max_field_len];
> 	int field_idx = 0;
>-	++start;
>-	while (*start != ';')
>+	++str;
>+	while (*str != ';')
> 	  {
>-	    assert(*start);
>+	    assert(*str);
> 	    assert(field_idx < max_field_len - 1);
>-	    field[field_idx++] = *start++;
>+	    field[field_idx++] = *str++;
> 	  }
>-	++start;
>+	++str;
> 	field[field_idx] = '\0';
> 
> 	print_field(ctx, param, field);
>+	start = str;
>       }
> 
>     // Might need to flush.
>-    if (bufindex)
>+    if (str != start)
>+      print_word(ctx, start, str - start);
>+  }
>+
>+  void
>+  print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc)
>+  { print_string(ctx, str, nbc, nullptr, 0); }
>+
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+  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)
>       {
>-	buf[bufindex] = '\0';
>-	print_word(ctx, buf, bufindex);
>+	int status;
>+	char* demangled_name =
>+	  __cxxabiv1::__cxa_demangle(function, NULL, NULL, &status);
>+	if (status == 0)
>+	  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
>@@ -1058,41 +1161,27 @@ 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");
>-	}
>-    }
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+    if (_M_backtrace_state)
>+      {
>+	print_literal(ctx, "Backtrace:\n");
>+	_M_print_backtrace(print_backtrace, &ctx);

If libstdc++.so gets compiled on a machine without libacktrace
installed, then _GLIBCXX_DEBUG_USE_LIBBACKTRACE will be defined to 0
here. How do you prevent _M_print_backtrace from being inlined, using
the no-op definition of __render_backtrace defined in this file?

I think you might want to defined _M_print_backtrace as:

__attribute__((__noinline__)) inline void
_Error_formatter::
_M_print_backtrace(void (*__cb)(), void* __data) const
{ __render_backtrace(_M_backtrace_state, (backtrace_full_callback)__cb, __data); }

The __noinline__ attribute will ensure it isn't inlined into
src/c++11/debug.cc and that a definition in the application can be
interposed in place of the one in libstdc++.so

I think.

>+	ctx._M_first_line = true;
>+	print_literal(ctx, "\n");
>+      }
> #endif
> 
>     print_literal(ctx, "Error: ");
> 
>     // Print the error message
>     assert(_M_text);
>-    print_string(ctx, _M_text, _M_parameters, _M_num_parameters);
>+    print_string(ctx, _M_text, -1, _M_parameters, _M_num_parameters);
>     print_literal(ctx, ".\n");
> 
>     // Emit descriptions of the objects involved in the operation
>@@ -1123,6 +1212,18 @@ namespace __gnu_debug
>     abort();
>   }
> 
>+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
>+
>+  _GLIBCXX_WEAK_DEFINITION void*
>+  __create_backtrace_state()
>+  { return nullptr; }
>+
>+  _GLIBCXX_WEAK_DEFINITION void
>+  __render_backtrace(void*, __backtrace_full_cb, void*)
>+  { }
>+

What happens if the target doesn't support weak symbols?

Should the use of libbacktrace be conditional on whether weak symbols
are supported?

Or do we even need these functions at all? Would this work?

__attribute__((__noinline__)) inline void
_Error_formatter::
_M_print_backtrace(void (*__cb)(), void* __data) const
{
#if _GLIBCXX_DEBUG_USE_LIBBACKTRACE
     backtrace_full((backtrace_state*)_M_backtrace_state, 1,
                    (backtrace_full_callback)__cb, 0, __data);
#endif
}

And for the constructor:

__attribute__((__noinline__)) inline void
_Error_formatter::
_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)
   , _M_backtrace_state(0)
{
#if _GLIBCXX_DEBUG_USE_LIBBACKTRACE
   _M_backtrace_state = backtrace_create_state(NULL, 0, NULL, NULL);
#endif
}

I think this will mean that the application code will use
backtrace_create_state when constructing an _Error_formatter and then
the library code will call _M_print_backtrace, which will use the
first definition that is found by the linker.


>+#endif
>+
> #if !_GLIBCXX_INLINE_VERSION
>   // Deprecated methods kept for backward compatibility.
>   void
>diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
>index 3af5dc593c2..2c3204e57a5 100644
>--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
>+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
>@@ -210,6 +210,7 @@ check_version(symbol& test, bool added)
>       known_versions.push_back("GLIBCXX_3.4.27");
>       known_versions.push_back("GLIBCXX_3.4.28");
>       known_versions.push_back("GLIBCXX_3.4.29");
>+      known_versions.push_back("GLIBCXX_3.4.30");
>       known_versions.push_back("GLIBCXX_LDBL_3.4.29");
>       known_versions.push_back("GLIBCXX_IEEE128_3.4.29");
>       known_versions.push_back("CXXABI_1.3");

You also need to update the latestp variable:

       // Check that added symbols are added in the latest pre-release version.
       bool latestp = (test.version_name == "GLIBCXX_3.4.29"

And if you add a new symbol version then you need to update
libtool_VERSION in acinclude.m4 and regenerate the autoconf templates,
see https://gcc.gnu.org/onlinedocs/libstdc++/manual/appendix_porting.html#build_hacking.configure.version
Jonathan Wakely May 7, 2021, 2:26 p.m. UTC | #6
On 05/05/21 12:33 +0100, Jonathan Wakely wrote:
>On 24/04/21 15:46 +0200, François Dumont via Libstdc++ wrote:
>>Hi
>>
>>    Here is the patch to add backtrace generation on _GLIBCXX_DEBUG 
>>assertions thanks to libbacktrace.

Ville pointed out that we'll need to use libbacktrace for
std::stacktrace <https://wg21.link/P0881R7> anyway, and it would be
useful if/when we add support for C++ Contracts to the lirbary.

So let's integrate libbacktrace into libstdc++ properly. Jakub
suggested doing it how libsanitizer does it, which is to rebuild the
libbacktrace sources as part of the libsanitizer build, using the
preprocessor to rename the symbols so that they use reserved names.
e.g. rename backtrace_full to __glibcxx_backtrace_full or something
like that.

I'll work on getting it building as part of libstdc++ (or maybe as a
separate static library for now, as we do for libstdc++fs.a) and then
you can rework your Debug Mode patch to depend on the private version
of libbacktrace included with libstdc++ (instead of expecting users to
provide it themselves).
François Dumont May 9, 2021, 3:03 p.m. UTC | #7
On 07/05/21 4:26 pm, Jonathan Wakely wrote:
> On 05/05/21 12:33 +0100, Jonathan Wakely wrote:
>> On 24/04/21 15:46 +0200, François Dumont via Libstdc++ wrote:
>>> Hi
>>>
>>>     Here is the patch to add backtrace generation on _GLIBCXX_DEBUG 
>>> assertions thanks to libbacktrace.
>
> Ville pointed out that we'll need to use libbacktrace for
> std::stacktrace <https://wg21.link/P0881R7> anyway, and it would be
> useful if/when we add support for C++ Contracts to the lirbary.
>
> So let's integrate libbacktrace into libstdc++ properly. Jakub
> suggested doing it how libsanitizer does it, which is to rebuild the
> libbacktrace sources as part of the libsanitizer build, using the
> preprocessor to rename the symbols so that they use reserved names.
> e.g. rename backtrace_full to __glibcxx_backtrace_full or something
> like that.
>
> I'll work on getting it building as part of libstdc++ (or maybe as a
> separate static library for now, as we do for libstdc++fs.a) and then
> you can rework your Debug Mode patch to depend on the private version
> of libbacktrace included with libstdc++ (instead of expecting users to
> provide it themselves).
>
Ok, thanks for the info.

I will perhaps extract the non-backtrace related stuff from this patch 
then and I'll rework the backtrace part later.
diff mbox series

Patch

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 5323c7f0604..2606d67d8a9 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2397,6 +2397,15 @@  GLIBCXX_3.4.29 {
 
 } GLIBCXX_3.4.28;
 
+GLIBCXX_3.4.30 {
+
+    # __gnu_debug::__create_backtrace
+    _ZN11__gnu_debug24__create_backtrace_stateEv;
+    _ZN11__gnu_debug18__render_backtraceEPvPFiS0_mPKciS2_ES0_;
+
+} GLIBCXX_3.4.29;
+
+
 # Symbols in the support library (libsupc++) have their own tag.
 CXXABI_1.3 {
 
diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 883e8cb4f03..931b09710f3 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -160,6 +160,12 @@  which always works correctly.
   <code>GLIBCXX_DEBUG_MESSAGE_LENGTH</code> can be used to request a
   different length.</para>
 
+<para>Note that 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. You'll also have to link with libbacktrace
+  (<option>-lbacktrace</option>) 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 24543e9526e..9bd0da8c1c5 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>
+      libstdc++ <link linkend="manual.ext.debug_mode">debug mode</link> is able to
+      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> along
+      with <code>_GLIBCXX_DEBUG</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 7140fed5e83..ec0984e438f 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,31 @@ 
 
 #include <bits/c++config.h>
 
+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+#  include <backtrace-supported.h>
+# endif
+#endif
+
+#if defined(_GLIBCXX_DEBUG_BACKTRACE) && BACKTRACE_SUPPORTED
+# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 1
+# include <backtrace.h>
+
+namespace __gnu_debug
+{
+  typedef backtrace_full_callback __backtrace_full_cb;
+}
+#elif defined (_GLIBCXX_USE_C99_STDINT_TR1)
+# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 0
+# include <stdint.h> // For uintptr_t.
+
+namespace __gnu_debug
+{
+  typedef int (*__backtrace_full_cb) (void*, uintptr_t, const char*,
+				      int, const char*);
+}
+#endif
+
 #if __cpp_rtti
 # include <typeinfo>
 # define _GLIBCXX_TYPEID(_Type) &typeid(_Type)
@@ -155,6 +180,16 @@  namespace __gnu_debug
     __msg_irreflexive_ordering
   };
 
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+
+  void*
+  __create_backtrace_state();
+
+  void
+  __render_backtrace(void*, __backtrace_full_cb, void*);
+
+#endif
+
   class _Error_formatter
   {
     // Tags denoting the type of parameter for construction
@@ -558,12 +593,14 @@  namespace __gnu_debug
     _M_print_string(const char* __string) const _GLIBCXX_DEPRECATED;
 #endif
 
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    void
+    _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const;
+#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)
-    { }
+		     const char* __function);
 
 #if !_GLIBCXX_INLINE_VERSION
     void
@@ -578,6 +615,9 @@  namespace __gnu_debug
     unsigned int	_M_num_parameters;
     const char*		_M_text;
     const char*		_M_function;
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    void*		_M_backtrace_state;
+#endif
 
   public:
     static _Error_formatter&
@@ -587,6 +627,36 @@  namespace __gnu_debug
       return __formatter;
     }
   };
+
+#if _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+
+  void*
+  __create_backtrace_state()
+  { return backtrace_create_state(NULL, 0, NULL, NULL); }
+
+  void
+  __render_backtrace(void* __bt, __backtrace_full_cb __cb, void* __data)
+  { backtrace_full((backtrace_state*)__bt, 1, __cb, 0, __data); }
+
+#endif
+
+  _Error_formatter::
+  _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)
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+  , _M_backtrace_state(__create_backtrace_state())
+#endif
+  { }
+
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+  void
+  _Error_formatter::
+  _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const
+  { __render_backtrace(_M_backtrace_state, __cb, __data); }
+#endif
+
 } // namespace __gnu_debug
 
 #undef _GLIBCXX_TYPEID
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index 5a642097d17..790810fdab4 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -34,16 +34,12 @@ 
 
 #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 <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"
 
@@ -574,7 +570,7 @@  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)
     { get_max_length(_M_max_length); }
 
     std::size_t	_M_max_length;
@@ -584,16 +580,17 @@  namespace
     bool	_M_wordwrap;
   };
 
+  typedef void (*_Print_func_t) (PrintContext&, const char*, ptrdiff_t);
+
   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, ptrdiff_t nbc = -1)
   {
-    size_t length = count >= 0 ? count : __builtin_strlen(word);
+    size_t length = nbc >= 0 ? nbc : __builtin_strlen(word);
     if (length == 0)
       return;
 
@@ -619,15 +616,12 @@  namespace
 	// If this isn't the first line, indent
 	if (ctx._M_column == 1 && !ctx._M_first_line)
 	  {
-	    char spacing[ctx._M_indent + 1];
-	    for (int i = 0; i < ctx._M_indent; ++i)
-	      spacing[i] = ' ';
-	    spacing[ctx._M_indent] = '\0';
+	    const char spacing[ctx._M_indent + 1] = "    ";
 	    fprintf(stderr, "%s", spacing);
 	    ctx._M_column += ctx._M_indent;
 	  }
 
-	int written = fprintf(stderr, "%s", word);
+	int written = fprintf(stderr, "%.*s", (int)length, word);
 
 	if (word[length - 1] == '\n')
 	  {
@@ -640,7 +634,57 @@  namespace
     else
       {
 	print_literal(ctx, "\n");
-	print_word(ctx, word, count);
+	print_word(ctx, word, nbc);
+      }
+  }
+
+  void
+  print_raw(PrintContext&, const char* str, ptrdiff_t nbc)
+  {
+    if (nbc >= 0)
+      fprintf(stderr, "%.*s", (int)nbc, str);
+    else
+      fprintf(stderr, "%s", str);
+  }
+
+  void
+  print_function(PrintContext& ctx, const char* func,
+		 _Print_func_t print_func)
+  {
+    const char cxx1998[] = "__cxx1998::";
+    const char uglification[] = "__";
+    for (;;)
+      {
+	auto idx1 = strstr(func, cxx1998);
+	auto idx2 = strstr(func, uglification);
+	if (idx1 || idx2)
+	  {
+	    if (idx1 && (!idx2 || idx1 <= idx2))
+	      {
+		if (idx1 != func)
+		  print_func(ctx, func, idx1 - func);
+
+		func = idx1 + sizeof(cxx1998) - 1;
+	      }
+	    else if (idx2)
+	      {
+		if (idx2 != func)
+		  print_func(ctx, func, idx2 - func);
+
+		func = idx2 + sizeof(uglification) - 1;
+	      }
+
+	    while (*func && isspace(*func))
+	      ++func;
+
+	    if (!*func)
+	      break;
+	  }
+	else
+	  {
+	    print_func(ctx, func, -1);
+	    break;
+	  }
       }
   }
 
@@ -657,7 +701,10 @@  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)
+	    print_function(ctx, demangled_name, &print_word);
+	  else
+	    print_word(ctx, info->name(), -1);
 	  free(demangled_name);
 	}
     }
@@ -925,61 +972,62 @@  namespace
   }
 
   void
-  print_string(PrintContext& ctx, const char* string,
+  print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc,
 	       const _Parameter* parameters, std::size_t num_parameters)
   {
-    const char* start = string;
-    const int bufsize = 128;
-    char buf[bufsize];
-    int bufindex = 0;
+    const char* start = str;
+    const char* end = nbc >= 0 ? start + nbc : nullptr;
+    char buf[64];
 
-    while (*start)
+    while ((end && str != end) || (!end && *str))
       {
-	if (isspace(*start))
+	if (isspace(*str))
 	  {
-	    buf[bufindex++] = *start++;
-	    buf[bufindex] = '\0';
-	    print_word(ctx, buf, bufindex);
-	    bufindex = 0;
+	    ++str;
+	    print_word(ctx, start, str - start);
+	    start = str;
 	    continue;
 	  }
 
-	if (!num_parameters || *start != '%')
+	if (!parameters || *str != '%')
 	  {
 	    // Normal char or no parameter to look for.
-	    buf[bufindex++] = *start++;
+	    ++str;
 	    continue;
 	  }
 
-	if (*++start == '%')
+	if (*++str == '%')
 	  {
 	    // Escaped '%'
-	    buf[bufindex++] = *start++;
+	    print_word(ctx, start, str - start);
+	    ++str;
+	    start = str;
 	    continue;
 	  }
 
 	// We are on a parameter property reference, we need to flush buffer
 	// first.
-	if (bufindex != 0)
+	if (str != start)
 	  {
-	    buf[bufindex] = '\0';
-	    print_word(ctx, buf, bufindex);
-	    bufindex = 0;
+	    // Avoid to print the '%'.
+	    if (str - start > 1)
+	      print_word(ctx, start, str - start - 1);
+	    start = str;
 	  }
 
 	// Get the parameter number
-	assert(*start >= '1' && *start <= '9');
-	size_t param_index = *start - '0' - 1;
+	assert(*str >= '1' && *str <= '9');
+	size_t param_index = *str - '0' - 1;
 	assert(param_index < num_parameters);
 	const auto& param = parameters[param_index];
 
 	// '.' separates the parameter number from the field
 	// name, if there is one.
-	++start;
-	if (*start != '.')
+	++str;
+	if (*str != '.')
 	  {
-	    assert(*start == ';');
-	    ++start;
+	    assert(*str == ';');
+	    ++str;
 	    if (param._M_kind == _Parameter::__integer)
 	      {
 		int written
@@ -988,8 +1036,9 @@  namespace
 		print_word(ctx, buf, written);
 	      }
 	    else if (param._M_kind == _Parameter::__string)
-	      print_string(ctx, param._M_variant._M_string._M_value,
+	      print_string(ctx, param._M_variant._M_string._M_value, -1,
 			   parameters, num_parameters);
+	    start = str;
 	    continue;
 	  }
 
@@ -997,26 +1046,80 @@  namespace
 	const int max_field_len = 16;
 	char field[max_field_len];
 	int field_idx = 0;
-	++start;
-	while (*start != ';')
+	++str;
+	while (*str != ';')
 	  {
-	    assert(*start);
+	    assert(*str);
 	    assert(field_idx < max_field_len - 1);
-	    field[field_idx++] = *start++;
+	    field[field_idx++] = *str++;
 	  }
-	++start;
+	++str;
 	field[field_idx] = '\0';
 
 	print_field(ctx, param, field);
+	start = str;
       }
 
     // Might need to flush.
-    if (bufindex)
+    if (str != start)
+      print_word(ctx, start, str - start);
+  }
+
+  void
+  print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc)
+  { print_string(ctx, str, nbc, nullptr, 0); }
+
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+  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)
       {
-	buf[bufindex] = '\0';
-	print_word(ctx, buf, bufindex);
+	int status;
+	char* demangled_name =
+	  __cxxabiv1::__cxa_demangle(function, NULL, NULL, &status);
+	if (status == 0)
+	  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
@@ -1058,41 +1161,27 @@  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");
-	}
-    }
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    if (_M_backtrace_state)
+      {
+	print_literal(ctx, "Backtrace:\n");
+	_M_print_backtrace(print_backtrace, &ctx);
+	ctx._M_first_line = true;
+	print_literal(ctx, "\n");
+      }
 #endif
 
     print_literal(ctx, "Error: ");
 
     // Print the error message
     assert(_M_text);
-    print_string(ctx, _M_text, _M_parameters, _M_num_parameters);
+    print_string(ctx, _M_text, -1, _M_parameters, _M_num_parameters);
     print_literal(ctx, ".\n");
 
     // Emit descriptions of the objects involved in the operation
@@ -1123,6 +1212,18 @@  namespace __gnu_debug
     abort();
   }
 
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+
+  _GLIBCXX_WEAK_DEFINITION void*
+  __create_backtrace_state()
+  { return nullptr; }
+
+  _GLIBCXX_WEAK_DEFINITION void
+  __render_backtrace(void*, __backtrace_full_cb, void*)
+  { }
+
+#endif
+
 #if !_GLIBCXX_INLINE_VERSION
   // Deprecated methods kept for backward compatibility.
   void
diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
index 3af5dc593c2..2c3204e57a5 100644
--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
@@ -210,6 +210,7 @@  check_version(symbol& test, bool added)
       known_versions.push_back("GLIBCXX_3.4.27");
       known_versions.push_back("GLIBCXX_3.4.28");
       known_versions.push_back("GLIBCXX_3.4.29");
+      known_versions.push_back("GLIBCXX_3.4.30");
       known_versions.push_back("GLIBCXX_LDBL_3.4.29");
       known_versions.push_back("GLIBCXX_IEEE128_3.4.29");
       known_versions.push_back("CXXABI_1.3");