libbacktrace integration for _GLIBCXX_DEBUG mode

Message ID cf6ee005-4f92-cdc8-0f29-4c64e990b712@gmail.com
State New
Headers show
Series
  • libbacktrace integration for _GLIBCXX_DEBUG mode
Related show

Commit Message

François Dumont Dec. 10, 2018, 11:08 p.m.
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

Comments

Jonathan Wakely Dec. 21, 2018, 8:35 p.m. | #1
On 11/12/18 00:08 +0100, François Dumont wrote:
>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 ?


Maybe one day, but not now during stage 3.

I'm concerned about the requirement to link to libbacktrace
explicitly (which will break existing makefiles and build systems that
currently use debug mode in testing).

Also, some of the glibc team pointed out to me that running *any*
extra code after undefined behaviour has been detected is a potential
risk. The less that you do between detecting UB and calling abort(),
the better. Giving the users more information is helpful, but comes
with some additional risk.
Ville Voutilainen Dec. 21, 2018, 8:47 p.m. | #2
On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> wrote:
> >    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

> >libstdc++ has no libbacktrace dependency after usual build.


> I'm concerned about the requirement to link to libbacktrace

> explicitly (which will break existing makefiles and build systems that

> currently use debug mode in testing).


But see what Francois wrote, "I also explcitely define
BACKTRACE_SUPPORTED to 0 to make sure
libstdc++ has no libbacktrace dependency after usual build."

> Also, some of the glibc team pointed out to me that running *any*

> extra code after undefined behaviour has been detected is a potential

> risk. The less that you do between detecting UB and calling abort(),

> the better. Giving the users more information is helpful, but comes

> with some additional risk.


Ditto. Having said those things, I think we need to figure out a good
way to provide this sensibly
as an opt-in. The backtrace support is bloody useful, and dovetails
into a possible Contracts-aware
implementation of our library, but I think we need to do some more
thought-work on this, thus I agree
that it's not stage3 material. I do think it's something that we need
to keep in mind, thanks
for working on it, Francois!
Jonathan Wakely Dec. 21, 2018, 9:03 p.m. | #3
On 21/12/18 22:47 +0200, Ville Voutilainen wrote:
>On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> wrote:

>> >    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

>> >libstdc++ has no libbacktrace dependency after usual build.

>

>> I'm concerned about the requirement to link to libbacktrace

>> explicitly (which will break existing makefiles and build systems that

>> currently use debug mode in testing).

>

>But see what Francois wrote, "I also explcitely define

>BACKTRACE_SUPPORTED to 0 to make sure

>libstdc++ has no libbacktrace dependency after usual build."


Yes, but if you happen to install libbacktrace headers, the behaviour
for users building their own code changes. I agree that if you install
those headers, it's probably for a reason, but it might be a different
reason to "so that libstdc++ prints better backtraces".

>> Also, some of the glibc team pointed out to me that running *any*

>> extra code after undefined behaviour has been detected is a potential

>> risk. The less that you do between detecting UB and calling abort(),

>> the better. Giving the users more information is helpful, but comes

>> with some additional risk.

>

>Ditto. Having said those things, I think we need to figure out a good

>way to provide this sensibly

>as an opt-in. The backtrace support is bloody useful, and dovetails

>into a possible Contracts-aware

>implementation of our library, but I think we need to do some more

>thought-work on this, thus I agree

>that it's not stage3 material. I do think it's something that we need

>to keep in mind, thanks

>for working on it, Francois!


Yes, I agree that making it available via a more explicit opt-in would
be good. Maybe require users to define _GLIBCXX_DEBUG_BACKTRACE as well
as _GLIBCXX_DEBUG, or something like that.
François Dumont Dec. 23, 2018, 7:19 p.m. | #4
On 12/21/18 10:03 PM, Jonathan Wakely wrote:
> On 21/12/18 22:47 +0200, Ville Voutilainen wrote:

>> On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> 

>> wrote:

>>> >    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

>>> >libstdc++ has no libbacktrace dependency after usual build.

>>

>>> I'm concerned about the requirement to link to libbacktrace

>>> explicitly (which will break existing makefiles and build systems that

>>> currently use debug mode in testing).

>>

>> But see what Francois wrote, "I also explcitely define

>> BACKTRACE_SUPPORTED to 0 to make sure

>> libstdc++ has no libbacktrace dependency after usual build."

>

> Yes, but if you happen to install libbacktrace headers, the behaviour

> for users building their own code changes. I agree that if you install

> those headers, it's probably for a reason, but it might be a different

> reason to "so that libstdc++ prints better backtraces".

>

>>> Also, some of the glibc team pointed out to me that running *any*

>>> extra code after undefined behaviour has been detected is a potential

>>> risk. The less that you do between detecting UB and calling abort(),

>>> the better. Giving the users more information is helpful, but comes

>>> with some additional risk.

>>

>> Ditto. Having said those things, I think we need to figure out a good

>> way to provide this sensibly

>> as an opt-in. The backtrace support is bloody useful, and dovetails

>> into a possible Contracts-aware

>> implementation of our library, but I think we need to do some more

>> thought-work on this, thus I agree

>> that it's not stage3 material. I do think it's something that we need

>> to keep in mind, thanks

>> for working on it, Francois!

>

> Yes, I agree that making it available via a more explicit opt-in would

> be good. Maybe require users to define _GLIBCXX_DEBUG_BACKTRACE as well

> as _GLIBCXX_DEBUG, or something like that.

>

>

Yes, I considered that if you had install libbacktrace you wanted to use 
it. But I agree that an explicit activation might be better.

I'll prepare that.

François
François Dumont May 23, 2019, 5:39 a.m. | #5
Hi

     So here what I come up with.

     _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 
it and there is a detectable issue with libbacktrace then I generate a 
compilation error. I want to avoid users defining it but having no 
backtrace in the end in the debug assertion.

     With this new setup I manage to run testsuite with it like that:

export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/
make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 
-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

     An example of result:

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:
In function:
     std::__debug::vector<_Tp, _Allocator>::iterator
     std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,
     _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
     _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;
     _Allocator = std::allocator<int>; std::__debug::vector<_Tp,
     _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::
     vector<int> >, std::__debug::vector<int>,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::vector<_Tp, _Alloc>::iterator>::iterator_category =
     std::random_access_iterator_tag; typename std::vector<_Tp,
     _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 
std::vector<int>
     >; std::__debug::vector<_Tp, _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,
     std::vector<int> >, std::__debug::vector<int>,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =
     std::random_access_iterator_tag; typename std::vector<_Tp,
     _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const int*, 
std::
     vector<int> >]

Backtrace:
     0x402718 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 
std::vector<int> >> std::__debug::vector<int>::insert<int*, 
void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 
const*, std::vector<int> >>, int*, int*)
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606
     0x402718 test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29
     0x401428 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

Error: attempt to insert with an iterator range [__first, __last) from this
container.

Objects involved in the operation:
     iterator "__first" @ 0x0x7fff730b96b0 {
       type = int* (mutable iterator);
     }
     iterator "__last" @ 0x0x7fff730b96b8 {
       type = int* (mutable iterator);
     }
     sequence "this" @ 0x0x7fff730b9720 {
       type = std::__debug::vector<int>;
     }
XFAIL: 23_containers/vector/debug/57779_neg.cc execution test


     * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]: Include
     <backtrace-supported.h> and <backtrace.h>.
     [!_GLIBCXX_DEBUG_BACKTRACE]: Include <stdint.h>.
     [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_error_callback): New.
     [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_full_callback): New.
     [!_GLIBCXX_DEBUG_BACKTRACE](struct backtrace_state): New declaration.
     (_Error_formatter::_Bt_full_t): New function pointer type.
     (_Error_formatter::_M_print_backtrace): New.
     (_Error_formatter::_M_backtrace_state): New.
     (_Error_formatter::_M_backtrace_full_func): New.
     * src/c++11/debug.cc: Include <cstring> and <string>.
     (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.
     * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.

Tested under Linux x86_64.

Ok to commit ?

François


On 12/21/18 10:03 PM, Jonathan Wakely wrote:
> On 21/12/18 22:47 +0200, Ville Voutilainen wrote:

>> On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> 

>> wrote:

>>> >    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

>>> >libstdc++ has no libbacktrace dependency after usual build.

>>

>>> I'm concerned about the requirement to link to libbacktrace

>>> explicitly (which will break existing makefiles and build systems that

>>> currently use debug mode in testing).

>>

>> But see what Francois wrote, "I also explcitely define

>> BACKTRACE_SUPPORTED to 0 to make sure

>> libstdc++ has no libbacktrace dependency after usual build."

>

> Yes, but if you happen to install libbacktrace headers, the behaviour

> for users building their own code changes. I agree that if you install

> those headers, it's probably for a reason, but it might be a different

> reason to "so that libstdc++ prints better backtraces".

>

>>> Also, some of the glibc team pointed out to me that running *any*

>>> extra code after undefined behaviour has been detected is a potential

>>> risk. The less that you do between detecting UB and calling abort(),

>>> the better. Giving the users more information is helpful, but comes

>>> with some additional risk.

>>

>> Ditto. Having said those things, I think we need to figure out a good

>> way to provide this sensibly

>> as an opt-in. The backtrace support is bloody useful, and dovetails

>> into a possible Contracts-aware

>> implementation of our library, but I think we need to do some more

>> thought-work on this, thus I agree

>> that it's not stage3 material. I do think it's something that we need

>> to keep in mind, thanks

>> for working on it, Francois!

>

> Yes, I agree that making it available via a more explicit opt-in would

> be good. Maybe require users to define _GLIBCXX_DEBUG_BACKTRACE as well

> as _GLIBCXX_DEBUG, or something like that.

>

>

>
diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 570c17ba28a..27873151dae 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -104,9 +104,11 @@
 <para>The following library components provide extra debugging
   capabilities in debug mode:</para>
 <itemizedlist>
+  <listitem><para><code>std::array</code> (no safe iterators)</para></listitem>
   <listitem><para><code>std::basic_string</code> (no safe iterators and see note below)</para></listitem>
   <listitem><para><code>std::bitset</code></para></listitem>
   <listitem><para><code>std::deque</code></para></listitem>
+  <listitem><para><code>std::forward_list</code></para></listitem>
   <listitem><para><code>std::list</code></para></listitem>
   <listitem><para><code>std::map</code></para></listitem>
   <listitem><para><code>std::multimap</code></para></listitem>
@@ -160,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++ has integrated
+  <link xmlns:xlink="http://www.w3.org/1999/xlink"
+  xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>
+  to produce backtrace 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 the
+  <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/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index 220379994c0..690750f42be 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,29 @@
 
 #include <bits/c++config.h>
 
+#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.
+
+// 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 +179,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 +584,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 +614,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 f5a49992efa..aa7c17248fe 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"
 
@@ -570,7 +567,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 +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)
@@ -623,7 +623,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 +640,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 +780,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 +1054,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 +1146,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 +1244,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: ");
Jonathan Wakely May 28, 2019, 10:06 p.m. | #6
On 23/05/19 07:39 +0200, François Dumont wrote:
>Hi

>

>    So here what I come up with.

>

>    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 


Thanks for making this opt-in.

>it and there is a detectable issue with libbacktrace then I generate a 

>compilation error. I want to avoid users defining it but having no 

>backtrace in the end in the debug assertion.


Why do you want to avoid that?

This means users can't just define the macro in their makefiles
unconditionally, they have to check if libbacktrace is installed and
supported. That might mean having platform-specific conditionals in
the makefile to only enable it sometimes.

What harm does it do to just ignore the _GLIBCXX_DEBUG_BACKTRACE macro
if backtraces can't be enabled? Or just use #warning instead of
#error?


>    With this new setup I manage to run testsuite with it like that:

>

>export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/

>make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 

>-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

>

>    An example of result:

>

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:

>In function:

>    std::__debug::vector<_Tp, _Allocator>::iterator

>    std::__debug::vector<_Tp, 

>_Allocator>::insert(std::__debug::vector<_Tp,

>    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with

>    _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;

>    _Allocator = std::allocator<int>; std::__debug::vector<_Tp,

>    _Allocator>::iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::

>    vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 

>std::vector<int>

>    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,

>    std::vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const int*, 

>std::

>    vector<int> >]

>

>Backtrace:

>    0x402718 

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 

>std::vector<int> >> std::__debug::vector<int>::insert<int*, 

>void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 

>const*, std::vector<int> >>, int*, int*)

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606

>    0x402718 test01()

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29

>    0x401428 main

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

>

>Error: attempt to insert with an iterator range [__first, __last) from this

>container.

>

>Objects involved in the operation:

>    iterator "__first" @ 0x0x7fff730b96b0 {

>      type = int* (mutable iterator);

>    }

>    iterator "__last" @ 0x0x7fff730b96b8 {

>      type = int* (mutable iterator);

>    }

>    sequence "this" @ 0x0x7fff730b9720 {

>      type = std::__debug::vector<int>;

>    }


This is nice.

>diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>index 570c17ba28a..27873151dae 100644

>--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml

>+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>@@ -104,9 +104,11 @@

> <para>The following library components provide extra debugging

>   capabilities in debug mode:</para>

> <itemizedlist>

>+  <listitem><para><code>std::array</code> (no safe iterators)</para></listitem>

>   <listitem><para><code>std::basic_string</code> (no safe iterators and see note below)</para></listitem>

>   <listitem><para><code>std::bitset</code></para></listitem>

>   <listitem><para><code>std::deque</code></para></listitem>

>+  <listitem><para><code>std::forward_list</code></para></listitem>

>   <listitem><para><code>std::list</code></para></listitem>

>   <listitem><para><code>std::map</code></para></listitem>

>   <listitem><para><code>std::multimap</code></para></listitem>

>@@ -160,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++ has integrated


I think "integrated" is the wrong word, because you haven't made
libbacktrace and libstdc++ into a single thing. You've just added the
ability for libstdc++ to use libbacktrace.

I suggest "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 backtrace on error. Use <code>-D_GLIBCXX_DEBUG_BACKTRACE</code> to


Should be "produce backtraces".

>+  activate it. Note that if not properly installed or if libbacktrace is not

>+  supported compilation will fail. You'll also have to use the


Comma after "supported". Remove "the" at the end of the line.


>+  <code>-lbacktrace</code> to build your application.</para>

> </section>


The new _GLIBCXX_DEBUG_BACKTRACE macro should be documented in
doc/xml/manual/using.xml too. I think we also have a table in that
chapter which lists relevant GCC options, so -lbacktrace could go in
there too.

Enabling backtrace support doesn't alter the ABI of the debug mode
containers, right? It only affects the size of the _Error_formatter
type, which is not a member of any container or iterator.
François Dumont May 29, 2019, 5:45 p.m. | #7
On 5/29/19 12:06 AM, Jonathan Wakely wrote:
> On 23/05/19 07:39 +0200, François Dumont wrote:

>> Hi

>>

>>     So here what I come up with.

>>

>>     _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 

>

> Thanks for making this opt-in.

>

>> it and there is a detectable issue with libbacktrace then I generate 

>> a compilation error. I want to avoid users defining it but having no 

>> backtrace in the end in the debug assertion.

>

> Why do you want to avoid that?

>

> This means users can't just define the macro in their makefiles

> unconditionally, they have to check if libbacktrace is installed and

> supported. That might mean having platform-specific conditionals in

> the makefile to only enable it sometimes.

>

> What harm does it do to just ignore the _GLIBCXX_DEBUG_BACKTRACE macro

> if backtraces can't be enabled? Or just use #warning instead of

> #error?

>

What I want to avoid is PR  saying that despite _GLIBCXX_DEBUG_BACKTRACE 
being defined there isn't any backtrace displayed in the assertion message.

Maybe I can still fail to compile if I can't include 
backtrace-supported.h to make clear that the -I... option is missing but 
ignore if !BACKTRACE_SUPPORTED. Would it be fine ?

>

>>     With this new setup I manage to run testsuite with it like that:

>>

>> export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/

>> make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 

>> -I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

>>

>>     An example of result:

>>

>> /home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606: 

>>

>> In function:

>>     std::__debug::vector<_Tp, _Allocator>::iterator

>>     std::__debug::vector<_Tp, 

>> _Allocator>::insert(std::__debug::vector<_Tp,

>>     _Allocator>::const_iterator, _InputIterator, _InputIterator) [with

>>     _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;

>>     _Allocator = std::allocator<int>; std::__debug::vector<_Tp,

>>     _Allocator>::iterator =

>> __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::

>>     vector<int> >, std::__debug::vector<int>,

>>     std::random_access_iterator_tag>; typename 

>> std::iterator_traits<typename

>>     std::vector<_Tp, _Alloc>::iterator>::iterator_category =

>>     std::random_access_iterator_tag; typename std::vector<_Tp,

>>     _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 

>> std::vector<int>

>>     >; std::__debug::vector<_Tp, _Allocator>::const_iterator =

>> __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,

>>     std::vector<int> >, std::__debug::vector<int>,

>>     std::random_access_iterator_tag>; typename 

>> std::iterator_traits<typename

>>     std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =

>>     std::random_access_iterator_tag; typename std::vector<_Tp,

>>     _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const 

>> int*, std::

>>     vector<int> >]

>>

>> Backtrace:

>>     0x402718 

>> __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 

>> std::vector<int> >> std::__debug::vector<int>::insert<int*, 

>> void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 

>> const*, std::vector<int> >>, int*, int*)

>> /home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606 

>>

>>     0x402718 test01()

>> /home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29 

>>

>>     0x401428 main

>> /home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34 

>>

>>

>> Error: attempt to insert with an iterator range [__first, __last) 

>> from this

>> container.

>>

>> Objects involved in the operation:

>>     iterator "__first" @ 0x0x7fff730b96b0 {

>>       type = int* (mutable iterator);

>>     }

>>     iterator "__last" @ 0x0x7fff730b96b8 {

>>       type = int* (mutable iterator);

>>     }

>>     sequence "this" @ 0x0x7fff730b9720 {

>>       type = std::__debug::vector<int>;

>>     }

>

> This is nice.


Yes, I forgot to say that I made an effort to clean a little the 
demangle names but I think you saw it already.

I was surprised to see that the content of the __PRETTY_FUNCTION__ macro 
is quite different from the demangling of the same symbol, too bad.

>

>> diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml 

>> b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>> index 570c17ba28a..27873151dae 100644

>> --- a/libstdc++-v3/doc/xml/manual/debug_mode.xml

>> +++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>> @@ -104,9 +104,11 @@

>> <para>The following library components provide extra debugging

>>   capabilities in debug mode:</para>

>> <itemizedlist>

>> + <listitem><para><code>std::array</code> (no safe 

>> iterators)</para></listitem>

>> <listitem><para><code>std::basic_string</code> (no safe iterators and 

>> see note below)</para></listitem>

>> <listitem><para><code>std::bitset</code></para></listitem>

>> <listitem><para><code>std::deque</code></para></listitem>

>> + <listitem><para><code>std::forward_list</code></para></listitem>

>> <listitem><para><code>std::list</code></para></listitem>

>> <listitem><para><code>std::map</code></para></listitem>

>> <listitem><para><code>std::multimap</code></para></listitem>

>> @@ -160,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++ has integrated

>

> I think "integrated" is the wrong word, because you haven't made

> libbacktrace and libstdc++ into a single thing. You've just added the

> ability for libstdc++ to use libbacktrace.

>

> I suggest "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 backtrace on error. Use 

>> <code>-D_GLIBCXX_DEBUG_BACKTRACE</code> to

>

> Should be "produce backtraces".

>

>> +  activate it. Note that if not properly installed or if 

>> libbacktrace is not

>> +  supported compilation will fail. You'll also have to use the

>

> Comma after "supported". Remove "the" at the end of the line.

>

>

>> +  <code>-lbacktrace</code> to build your application.</para>

>> </section>

>

> The new _GLIBCXX_DEBUG_BACKTRACE macro should be documented in

> doc/xml/manual/using.xml too. I think we also have a table in that

> chapter which lists relevant GCC options, so -lbacktrace could go in

> there too.



Ok, I'll consider your suggestions accordingly and complete this chapter.

>

> Enabling backtrace support doesn't alter the ABI of the debug mode

> containers, right? It only affects the size of the _Error_formatter

> type, which is not a member of any container or iterator


Indeed, no ABI breaking change.
Jonathan Wakely May 29, 2019, 7:38 p.m. | #8
On 29/05/19 19:45 +0200, François Dumont wrote:
>On 5/29/19 12:06 AM, Jonathan Wakely wrote:

>>On 23/05/19 07:39 +0200, François Dumont wrote:

>>>Hi

>>>

>>>    So here what I come up with.

>>>

>>>    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user 

>>>define

>>

>>Thanks for making this opt-in.

>>

>>>it and there is a detectable issue with libbacktrace then I 

>>>generate a compilation error. I want to avoid users defining it 

>>>but having no backtrace in the end in the debug assertion.

>>

>>Why do you want to avoid that?

>>

>>This means users can't just define the macro in their makefiles

>>unconditionally, they have to check if libbacktrace is installed and

>>supported. That might mean having platform-specific conditionals in

>>the makefile to only enable it sometimes.

>>

>>What harm does it do to just ignore the _GLIBCXX_DEBUG_BACKTRACE macro

>>if backtraces can't be enabled? Or just use #warning instead of

>>#error?

>>

>What I want to avoid is PR  saying that despite 

>_GLIBCXX_DEBUG_BACKTRACE being defined there isn't any backtrace 

>displayed in the assertion message.


I think optimizing for "we don't want to get PRs" instead of what's
useful to users is the wrong goal.

>Maybe I can still fail to compile if I can't include 

>backtrace-supported.h to make clear that the -I... option is missing 

>but ignore if !BACKTRACE_SUPPORTED. Would it be fine ?


How about just warnings?

#if defined(_GLIBCXX_DEBUG_BACKTRACE)
# if !defined(BACKTRACE_SUPPORTED)
#  if defined(__has_include) && !__has_include(<backtrace-supported.h>)
#   warning "_GLIBCXX_DEBUG_BACKTRACE is defined but <backtrace-supported.h> header from libbacktrace was not found"
#  endif
#  include <backtrace-supported.h>
# endif
# if !BACKTRACE_SUPPORTED
#   warning "_GLIBCXX_DEBUG_BACKTRACE is defined but libbacktrace is not supported"
# endif
# include <backtrace.h>
#else

It might be even better to give a way to suppress those warnings:

#if defined(_GLIBCXX_DEBUG_BACKTRACE)
# if !defined(BACKTRACE_SUPPORTED)
#  if defined(__has_include) && !__has_include(<backtrace-supported.h>) \
      && !defined _GLIBCXX_DEBUG_BACKTRACE_FAIL_QUIETLY
#   warning "_GLIBCXX_DEBUG_BACKTRACE is defined but <backtrace-supported.h> header from libbacktrace was not found"
#  endif
#  include <backtrace-supported.h>
# endif
# if !BACKTRACE_SUPPORTED && !defined _GLIBCXX_DEBUG_BACKTRACE_FAIL_QUIETLY
#   warning "_GLIBCXX_DEBUG_BACKTRACE is defined but libbacktrace is not supported"
# endif
# include <backtrace.h>
#else



>>

>>>    With this new setup I manage to run testsuite with it like that:

>>>

>>>export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/

>>>make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 

>>>-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

>>>

>>>    An example of result:

>>>

>>>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:

>>>

>>>In function:

>>>    std::__debug::vector<_Tp, _Allocator>::iterator

>>>    std::__debug::vector<_Tp, 

>>>_Allocator>::insert(std::__debug::vector<_Tp,

>>>    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with

>>>    _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;

>>>    _Allocator = std::allocator<int>; std::__debug::vector<_Tp,

>>>    _Allocator>::iterator =

>>>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::

>>>    vector<int> >, std::__debug::vector<int>,

>>>    std::random_access_iterator_tag>; typename 

>>>std::iterator_traits<typename

>>>    std::vector<_Tp, _Alloc>::iterator>::iterator_category =

>>>    std::random_access_iterator_tag; typename std::vector<_Tp,

>>>    _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 

>>>std::vector<int>

>>>    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =

>>>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,

>>>    std::vector<int> >, std::__debug::vector<int>,

>>>    std::random_access_iterator_tag>; typename 

>>>std::iterator_traits<typename

>>>    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =

>>>    std::random_access_iterator_tag; typename std::vector<_Tp,

>>>    _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const 

>>>int*, std::

>>>    vector<int> >]

>>>

>>>Backtrace:

>>>    0x402718 

>>>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 

>>>std::vector<int> >> std::__debug::vector<int>::insert<int*, 

>>>void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 

>>>const*, std::vector<int> >>, int*, int*)

>>>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606

>>>

>>>    0x402718 test01()

>>>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29

>>>

>>>    0x401428 main

>>>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

>>>

>>>

>>>Error: attempt to insert with an iterator range [__first, __last) 

>>>from this

>>>container.

>>>

>>>Objects involved in the operation:

>>>    iterator "__first" @ 0x0x7fff730b96b0 {

>>>      type = int* (mutable iterator);

>>>    }

>>>    iterator "__last" @ 0x0x7fff730b96b8 {

>>>      type = int* (mutable iterator);

>>>    }

>>>    sequence "this" @ 0x0x7fff730b9720 {

>>>      type = std::__debug::vector<int>;

>>>    }

>>

>>This is nice.

>

>Yes, I forgot to say that I made an effort to clean a little the 

>demangle names but I think you saw it already.


Yes, all that code concerns me. I'll reply to the patch email again
...
Jonathan Wakely May 29, 2019, 7:47 p.m. | #9
On 23/05/19 07:39 +0200, François Dumont wrote:
>Hi

>

>    So here what I come up with.

>

>    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 

>it and there is a detectable issue with libbacktrace then I generate a 

>compilation error. I want to avoid users defining it but having no 

>backtrace in the end in the debug assertion.

>

>    With this new setup I manage to run testsuite with it like that:

>

>export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/

>make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 

>-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

>

>    An example of result:

>

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:

>In function:

>    std::__debug::vector<_Tp, _Allocator>::iterator

>    std::__debug::vector<_Tp, 

>_Allocator>::insert(std::__debug::vector<_Tp,

>    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with

>    _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;

>    _Allocator = std::allocator<int>; std::__debug::vector<_Tp,

>    _Allocator>::iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::

>    vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 

>std::vector<int>

>    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,

>    std::vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const int*, 

>std::

>    vector<int> >]

>

>Backtrace:

>    0x402718 

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 

>std::vector<int> >> std::__debug::vector<int>::insert<int*, 

>void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 

>const*, std::vector<int> >>, int*, int*)

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606

>    0x402718 test01()

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29

>    0x401428 main

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

>

>Error: attempt to insert with an iterator range [__first, __last) from this

>container.

>

>Objects involved in the operation:

>    iterator "__first" @ 0x0x7fff730b96b0 {

>      type = int* (mutable iterator);

>    }

>    iterator "__last" @ 0x0x7fff730b96b8 {

>      type = int* (mutable iterator);

>    }

>    sequence "this" @ 0x0x7fff730b9720 {

>      type = std::__debug::vector<int>;

>    }

>XFAIL: 23_containers/vector/debug/57779_neg.cc execution test

>

>

>    * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]: Include

>    <backtrace-supported.h> and <backtrace.h>.

>    [!_GLIBCXX_DEBUG_BACKTRACE]: Include <stdint.h>.

>    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_error_callback): New.

>    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_full_callback): New.

>    [!_GLIBCXX_DEBUG_BACKTRACE](struct backtrace_state): New declaration.

>    (_Error_formatter::_Bt_full_t): New function pointer type.

>    (_Error_formatter::_M_print_backtrace): New.

>    (_Error_formatter::_M_backtrace_state): New.

>    (_Error_formatter::_M_backtrace_full_func): New.

>    * src/c++11/debug.cc: Include <cstring> and <string>.

>    (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.

>    * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.

>

>Tested under Linux x86_64.

>

>Ok to commit ?

>

>François

>

>

>On 12/21/18 10:03 PM, Jonathan Wakely wrote:

>>On 21/12/18 22:47 +0200, Ville Voutilainen wrote:

>>>On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> 

>>>wrote:

>>>>>    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

>>>>>libstdc++ has no libbacktrace dependency after usual build.

>>>

>>>>I'm concerned about the requirement to link to libbacktrace

>>>>explicitly (which will break existing makefiles and build systems that

>>>>currently use debug mode in testing).

>>>

>>>But see what Francois wrote, "I also explcitely define

>>>BACKTRACE_SUPPORTED to 0 to make sure

>>>libstdc++ has no libbacktrace dependency after usual build."

>>

>>Yes, but if you happen to install libbacktrace headers, the behaviour

>>for users building their own code changes. I agree that if you install

>>those headers, it's probably for a reason, but it might be a different

>>reason to "so that libstdc++ prints better backtraces".

>>

>>>>Also, some of the glibc team pointed out to me that running *any*

>>>>extra code after undefined behaviour has been detected is a potential

>>>>risk. The less that you do between detecting UB and calling abort(),

>>>>the better. Giving the users more information is helpful, but comes

>>>>with some additional risk.

>>>

>>>Ditto. Having said those things, I think we need to figure out a good

>>>way to provide this sensibly

>>>as an opt-in. The backtrace support is bloody useful, and dovetails

>>>into a possible Contracts-aware

>>>implementation of our library, but I think we need to do some more

>>>thought-work on this, thus I agree

>>>that it's not stage3 material. I do think it's something that we need

>>>to keep in mind, thanks

>>>for working on it, Francois!

>>

>>Yes, I agree that making it available via a more explicit opt-in would

>>be good. Maybe require users to define _GLIBCXX_DEBUG_BACKTRACE as well

>>as _GLIBCXX_DEBUG, or something like that.

>>

>>

>>

>


>diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>index 570c17ba28a..27873151dae 100644

>--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml

>+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>@@ -104,9 +104,11 @@

> <para>The following library components provide extra debugging

>   capabilities in debug mode:</para>

> <itemizedlist>

>+  <listitem><para><code>std::array</code> (no safe iterators)</para></listitem>

>   <listitem><para><code>std::basic_string</code> (no safe iterators and see note below)</para></listitem>

>   <listitem><para><code>std::bitset</code></para></listitem>

>   <listitem><para><code>std::deque</code></para></listitem>

>+  <listitem><para><code>std::forward_list</code></para></listitem>

>   <listitem><para><code>std::list</code></para></listitem>

>   <listitem><para><code>std::map</code></para></listitem>

>   <listitem><para><code>std::multimap</code></para></listitem>

>@@ -160,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++ has integrated

>+  <link xmlns:xlink="http://www.w3.org/1999/xlink"

>+  xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>

>+  to produce backtrace 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 the

>+  <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/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h

>index 220379994c0..690750f42be 100644

>--- a/libstdc++-v3/include/debug/formatter.h

>+++ b/libstdc++-v3/include/debug/formatter.h

>@@ -31,6 +31,29 @@

> 

> #include <bits/c++config.h>

> 

>+#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.

>+

>+// 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 +179,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 +584,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 +614,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 f5a49992efa..aa7c17248fe 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"

> 

>@@ -570,7 +567,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 +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)

>@@ -623,7 +623,7 @@ namespace

> 	    ctx._M_column += ctx._M_indent;

> 	  }

> 

>-	int written = fprintf(stderr, "%s", word);

>+	int written = fprintf(stderr, word);


I don't think this is safe. The words being printed come from the
user's code, so could be controlled by an attacker. If a word contains
a printf format specifier like %s then your change will make it
dereference garbage. Please do not make this change.

> 	if (word[length - 1] == '\n')

> 	  {

>@@ -640,6 +640,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); }


This is unsafe too, this should be fprintf(stderr, "%s", 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<";


These should be string_view objects, not strings that allocate memory.

This code runs when the program has encountered undefined behaviour,
we should be running as little code as possible, and avoiding memory
allocations if possible (and string_view is just a better option here
anyway). If you need to compile this file as C++14 to use
std::experimental::string_view or C++17 to use std::string_view, then
do that. Do't repeatedly allocate strings every time print_function
gets called. Especially as that allocation could fail, which would
then leak the demangled_name string in the caller.


>+    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;

>+		  }


Will this work if a template argument is an expression like (sizeof(T) < 4) ?

>+

>+		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;


This is a lot of code with no error checking that is going to run when
the program has encountered undefined behaviour. That's worrying.

>+	  }

>+	else

>+	  {

>+	    print_func(ctx, func.c_str() + index);

>+	    break;

>+	  }

>+      }

>+  }

>+

>   template<size_t Length>

>     void

>     print_type(PrintContext& ctx,

>@@ -653,7 +780,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 +1054,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 +1146,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 +1244,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: ");

>
Jonathan Wakely May 29, 2019, 11:04 p.m. | #10
On 23/05/19 07:39 +0200, François Dumont wrote:
>Hi

>

>    So here what I come up with.

>

>    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 

>it and there is a detectable issue with libbacktrace then I generate a 

>compilation error. I want to avoid users defining it but having no 

>backtrace in the end in the debug assertion.

>

>    With this new setup I manage to run testsuite with it like that:

>

>export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/

>make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 

>-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug

>

>    An example of result:

>

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:

>In function:

>    std::__debug::vector<_Tp, _Allocator>::iterator

>    std::__debug::vector<_Tp, 

>_Allocator>::insert(std::__debug::vector<_Tp,

>    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with

>    _InputIterator = int*; <template-parameter-2-2> = void; _Tp = int;

>    _Allocator = std::allocator<int>; std::__debug::vector<_Tp,

>    _Allocator>::iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::

>    vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::iterator = __gnu_cxx::__normal_iterator<int*, 

>std::vector<int>

>    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int*,

>    std::vector<int> >, std::__debug::vector<int>,

>    std::random_access_iterator_tag>; typename 

>std::iterator_traits<typename

>    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =

>    std::random_access_iterator_tag; typename std::vector<_Tp,

>    _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const int*, 

>std::

>    vector<int> >]

>

>Backtrace:

>    0x402718 

>__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, 

>std::vector<int> >> std::__debug::vector<int>::insert<int*, 

>void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int 

>const*, std::vector<int> >>, int*, int*)

>/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606

>    0x402718 test01()

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29

>    0x401428 main

>/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

>

>Error: attempt to insert with an iterator range [__first, __last) from this

>container.

>

>Objects involved in the operation:

>    iterator "__first" @ 0x0x7fff730b96b0 {

>      type = int* (mutable iterator);

>    }

>    iterator "__last" @ 0x0x7fff730b96b8 {

>      type = int* (mutable iterator);

>    }

>    sequence "this" @ 0x0x7fff730b9720 {

>      type = std::__debug::vector<int>;

>    }

>XFAIL: 23_containers/vector/debug/57779_neg.cc execution test

>

>

>    * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]: Include

>    <backtrace-supported.h> and <backtrace.h>.

>    [!_GLIBCXX_DEBUG_BACKTRACE]: Include <stdint.h>.

>    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_error_callback): New.

>    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_full_callback): New.

>    [!_GLIBCXX_DEBUG_BACKTRACE](struct backtrace_state): New declaration.

>    (_Error_formatter::_Bt_full_t): New function pointer type.

>    (_Error_formatter::_M_print_backtrace): New.

>    (_Error_formatter::_M_backtrace_state): New.

>    (_Error_formatter::_M_backtrace_full_func): New.

>    * src/c++11/debug.cc: Include <cstring> and <string>.

>    (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.

>    * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.

>

>Tested under Linux x86_64.

>

>Ok to commit ?

>

>François

>

>

>On 12/21/18 10:03 PM, Jonathan Wakely wrote:

>>On 21/12/18 22:47 +0200, Ville Voutilainen wrote:

>>>On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely <jwakely@redhat.com> 

>>>wrote:

>>>>>    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure

>>>>>libstdc++ has no libbacktrace dependency after usual build.

>>>

>>>>I'm concerned about the requirement to link to libbacktrace

>>>>explicitly (which will break existing makefiles and build systems that

>>>>currently use debug mode in testing).

>>>

>>>But see what Francois wrote, "I also explcitely define

>>>BACKTRACE_SUPPORTED to 0 to make sure

>>>libstdc++ has no libbacktrace dependency after usual build."

>>

>>Yes, but if you happen to install libbacktrace headers, the behaviour

>>for users building their own code changes. I agree that if you install

>>those headers, it's probably for a reason, but it might be a different

>>reason to "so that libstdc++ prints better backtraces".

>>

>>>>Also, some of the glibc team pointed out to me that running *any*

>>>>extra code after undefined behaviour has been detected is a potential

>>>>risk. The less that you do between detecting UB and calling abort(),

>>>>the better. Giving the users more information is helpful, but comes

>>>>with some additional risk.

>>>

>>>Ditto. Having said those things, I think we need to figure out a good

>>>way to provide this sensibly

>>>as an opt-in. The backtrace support is bloody useful, and dovetails

>>>into a possible Contracts-aware

>>>implementation of our library, but I think we need to do some more

>>>thought-work on this, thus I agree

>>>that it's not stage3 material. I do think it's something that we need

>>>to keep in mind, thanks

>>>for working on it, Francois!

>>

>>Yes, I agree that making it available via a more explicit opt-in would

>>be good. Maybe require users to define _GLIBCXX_DEBUG_BACKTRACE as well

>>as _GLIBCXX_DEBUG, or something like that.

>>

>>

>>

>


>diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>index 570c17ba28a..27873151dae 100644

>--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml

>+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml

>@@ -104,9 +104,11 @@

> <para>The following library components provide extra debugging

>   capabilities in debug mode:</para>

> <itemizedlist>

>+  <listitem><para><code>std::array</code> (no safe iterators)</para></listitem>

>   <listitem><para><code>std::basic_string</code> (no safe iterators and see note below)</para></listitem>

>   <listitem><para><code>std::bitset</code></para></listitem>

>   <listitem><para><code>std::deque</code></para></listitem>

>+  <listitem><para><code>std::forward_list</code></para></listitem>

>   <listitem><para><code>std::list</code></para></listitem>

>   <listitem><para><code>std::map</code></para></listitem>

>   <listitem><para><code>std::multimap</code></para></listitem>

>@@ -160,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++ has integrated

>+  <link xmlns:xlink="http://www.w3.org/1999/xlink"

>+  xlink:href="https://github.com/ianlancetaylor/libbacktrace">libbacktrace</link>

>+  to produce backtrace 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 the

>+  <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/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h

>index 220379994c0..690750f42be 100644

>--- a/libstdc++-v3/include/debug/formatter.h

>+++ b/libstdc++-v3/include/debug/formatter.h

>@@ -31,6 +31,29 @@

> 

> #include <bits/c++config.h>

> 

>+#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.

>+// 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.
François Dumont June 6, 2019, 8:33 p.m. | #11
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: ");
Jonathan Wakely June 6, 2019, 9:43 p.m. | #12
On 06/06/19 22:33 +0200, François Dumont wrote:
>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 


I was only referring to these strings, which allocated memory on every
call to print_function, but you don't modify:

+    const string cxx1998 = "__cxx1998::";
+    const string allocator = ", std::allocator<";
+    const string safe_iterator = "__gnu_debug::_Safe_iterator<";

I see you've changed them now though.

>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.


Ugh, right, of course, sorry.

Then I guess <stdint.h> is better than relying on TR1 (even though
<stdint.h> isn't technically part of C++98 either).
François Dumont June 7, 2019, 4:21 p.m. | #13
I forgot to comment on the chosen behavior of _GLIBCXX_DEBUG_BACKTRACE.

So, if the user define it, it means that he has install libbacktrace on 
its system. However we silently ignore it if libbacktrace is eventually 
not supported. I think it is the simplest thing to do. I am not 
convinced by the #warning. Personnaly I always try to build without any 
warnings and most of the time I consider warnings as errors.

Regarding the post-treatment I introduced here is an example of 
transformation.

BEFORE:
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:
In function:
     std::__debug::vector<_Tp, _Allocator>::iterator
     std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,
     _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
     _InputIterator =
std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int>*, 

     std::__cxx1998::vector<std::unique_ptr<int>,
     std::allocator<std::unique_ptr<int> > > >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag> >; <template-parameter-2-2> = 
void; _Tp
     = std::unique_ptr<int>; _Allocator = 
std::allocator<std::unique_ptr<int>
     >; std::__debug::vector<_Tp, _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int>*, 

     std::__cxx1998::vector<std::unique_ptr<int>,
     std::allocator<std::unique_ptr<int> > > >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::__cxx1998::vector<_Tp, _Alloc>::iterator>::iterator_category =
     std::random_access_iterator_tag; typename std::__cxx1998::vector<_Tp,
     _Alloc>::iterator = 
__gnu_cxx::__normal_iterator<std::unique_ptr<int>*,
     std::__cxx1998::vector<std::unique_ptr<int>,
     std::allocator<std::unique_ptr<int> > > >; std::__debug::vector<_Tp,
     _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const
     std::unique_ptr<int>*, std::__cxx1998::vector<std::unique_ptr<int>,
     std::allocator<std::unique_ptr<int> > > >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::__cxx1998::vector<_Tp, 
_Alloc>::const_iterator>::iterator_category
     = std::random_access_iterator_tag; typename 
std::__cxx1998::vector<_Tp,
     _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const
     std::unique_ptr<int>*, std::__cxx1998::vector<std::unique_ptr<int>,
     std::allocator<std::unique_ptr<int> > > >]

Backtrace:
     0x4029ff 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, 
std::default_delete<int> >, std::allocator<std::unique_ptr<int, 
std::default_delete<int> > > > >, 
std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, 
std::allocator<std::unique_ptr<int, std::default_delete<int> > > >, 
std::random_access_iterator_tag> 
std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, 
std::allocator<std::unique_ptr<int, std::default_delete<int> > > 
 >::insert<std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > > >, std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >, std::random_access_iterator_tag> >, void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> > const*, std::__cxx1998::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > > >, std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >, std::random_access_iterator_tag>, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > > >, std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >, std::random_access_iterator_tag> >, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > > >, std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >, std::random_access_iterator_tag> >)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606
     0x4030cc test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:34
     0x401458 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:42

Error: attempt to insert with an iterator range [__first, __last) from this
container.

Objects involved in the operation:
     iterator "__first" @ 0x0x7ffd719fc0f0 {
       type = 
std::move_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, 
std::default_delete<int> >, std::allocator<std::unique_ptr<int, 
std::default_delete<int> > > > > > (mutable iterator);
       state = dereferenceable (start-of-sequence);
       references sequence with type 
'std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, 
std::allocator<std::unique_ptr<int, std::default_delete<int> > > >' @ 
0x0x7ffd719fc4d0
     }
     iterator "__last" @ 0x0x7ffd719fc0f0 {
       type = 
std::move_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::__cxx1998::vector<std::unique_ptr<int, 
std::default_delete<int> >, std::allocator<std::unique_ptr<int, 
std::default_delete<int> > > > > > (mutable iterator);
       state = past-the-end;
       references sequence with type 
'std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >, 
std::allocator<std::unique_ptr<int, std::default_delete<int> > > >' @ 
0x0x7ffd719fc4d0
     }
     sequence "this" @ 0x0x7ffd719fc4d0 {
       type = std::__debug::vector<std::unique_ptr<int, 
std::default_delete<int> >, std::allocator<std::unique_ptr<int, 
std::default_delete<int> > > >;
     }
XFAIL: 23_containers/vector/debug/insert7_neg.cc execution test

AFTER:
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:
In function:
     std::__debug::vector<_Tp, _Allocator>::iterator
     std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,
     _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
     _InputIterator =
std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int>*, 

     std::vector<std::unique_ptr<int>> >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag> >; <template-parameter-2-2> = 
void; _Tp
     = std::unique_ptr<int>; _Allocator = 
std::allocator<std::unique_ptr<int>
     >; std::__debug::vector<_Tp, _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int>*, 

     std::vector<std::unique_ptr<int>> >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::vector<_Tp, _Alloc>::iterator>::iterator_category =
     std::random_access_iterator_tag; typename std::vector<_Tp,
     _Alloc>::iterator = 
__gnu_cxx::__normal_iterator<std::unique_ptr<int>*,
     std::vector<std::unique_ptr<int>> >; std::__debug::vector<_Tp,
     _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const
     std::unique_ptr<int>*, std::vector<std::unique_ptr<int>> >,
     std::__debug::vector<std::unique_ptr<int> >,
     std::random_access_iterator_tag>; typename 
std::iterator_traits<typename
     std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =
     std::random_access_iterator_tag; typename std::vector<_Tp,
     _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const
     std::unique_ptr<int>*, std::vector<std::unique_ptr<int>> >]

Backtrace:
     0x4029ff 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::vector<std::unique_ptr<int, 
std::default_delete<int> >> >> std::__debug::vector<std::unique_ptr<int, 
std::default_delete<int> 
 >>::insert<std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::vector<std::unique_ptr<int, std::default_delete<int> >> >> >, void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> > const*, std::vector<std::unique_ptr<int, std::default_delete<int> >> >>, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::vector<std::unique_ptr<int, std::default_delete<int> >> >> >, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, std::default_delete<int> >*, std::vector<std::unique_ptr<int, std::default_delete<int> >> >> >)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606
     0x4030cc test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:34
     0x401458 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:42

Error: attempt to insert with an iterator range [__first, __last) from this
container.

Objects involved in the operation:
     iterator "__first" @ 0x0x7fffdbf5ea10 {
       type = 
std::move_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::vector<std::unique_ptr<int, 
std::default_delete<int> >> > > (mutable iterator);
       state = dereferenceable (start-of-sequence);
       references sequence with type 
'std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >>' 
@ 0x0x7fffdbf5edf0
     }
     iterator "__last" @ 0x0x7fffdbf5ea10 {
       type = 
std::move_iterator<__gnu_cxx::__normal_iterator<std::unique_ptr<int, 
std::default_delete<int> >*, std::vector<std::unique_ptr<int, 
std::default_delete<int> >> > > (mutable iterator);
       state = past-the-end;
       references sequence with type 
'std::__debug::vector<std::unique_ptr<int, std::default_delete<int> >>' 
@ 0x0x7fffdbf5edf0
     }
     sequence "this" @ 0x0x7fffdbf5edf0 {
       type = std::__debug::vector<std::unique_ptr<int, 
std::default_delete<int> >>;
     }
XFAIL: 23_containers/vector/debug/insert7_neg.cc execution test

It is just better, not ideal of course. And you are right, current code 
can fail to do the proper thing.

I am clearly not the first one to try such a thing and to struggle to do 
so. So just tell me if you prefer I keep the original labels and I'll do so.

François


On 6/6/19 11:43 PM, Jonathan Wakely wrote:
> On 06/06/19 22:33 +0200, François Dumont wrote:

>> 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 

>

> I was only referring to these strings, which allocated memory on every

> call to print_function, but you don't modify:

>

> +    const string cxx1998 = "__cxx1998::";

> +    const string allocator = ", std::allocator<";

> +    const string safe_iterator = "__gnu_debug::_Safe_iterator<";

>

> I see you've changed them now though.

>

>> 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.

>

> Ugh, right, of course, sorry.

>

> Then I guess <stdint.h> is better than relying on TR1 (even though

> <stdint.h> isn't technically part of C++98 either).

>

>
François Dumont June 13, 2019, 8:46 p.m. | #14
Here is a new proposal which I think take into account all your remarks.

I discovered the great "%.*s" printf format so I was able to do some 
cleanup on the function name without any allocation.

I also agree that counting the '>' or '<' is not reliable so I remove 
this and limit the cleanup to the __cxx1998 namespace and the __ 
uglification, it is still better than nothing.

I complete the doc as advised. I also added a note about making sure 
that _GLIBCXX_DEBUG_BACKTRACE is defined consistently throughout the 
application otherwise it would break the famous ODR rule.

I introduced a _GLIBCXX_DEBUG_USE_LIBBACKTRACE to know when the system 
is able to handle libbacktrace even if the user didn't activate it. I 
could undef if when I am not building the library but I don't remember 
if there is a macro to signal that library is being built ?

Here is an output sample now:

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:177:
In function:
     gnu_debug::basic_string<_CharT, _Traits, 
_Allocator>::basic_string(const
     _CharT*, gnu_debug::basic_string<_CharT, _Traits,
     _Allocator>::size_type, const _Allocator&) [with _CharT = char; 
_Traits
     = std::char_traits<char>; _Allocator = std::allocator<char>;
     gnu_debug::basic_string<_CharT, _Traits, _Allocator>::size_type = long
     unsigned int]

Backtrace:
     0x400afd char const* gnu_debug::check_string<char, unsigned 
long>(char const*, unsigned long, char const*, unsigned int, char const*)
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:56
     0x400afd char const* gnu_debug::check_string<char, unsigned 
long>(char const*, unsigned long, char const*, unsigned int, char const*)
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:49
     0x400afd gnu_debug::basic_string<char, std::char_traits<char>, 
std::allocator<char> >::basic_string(char const*, unsigned long, 
std::allocator<char> const&)
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:177
     0x400afd test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/21_strings/basic_string/debug/1_neg.cc:27
     0x4009c8 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/21_strings/basic_string/debug/1_neg.cc:32

Error: __s != 0 || __n == 0.
XFAIL: 21_strings/basic_string/debug/1_neg.cc execution test

     * 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>.
     [BACKTRACE_SUPPORTED || _GLIBCXX_USE_C99_STDINT_TR1]
     (_GLIBCXX_DEBUG_USE_LIBBACKTRACE): New.
     [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_error_cb): New.
     [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_full_cb): New.
     [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_state): New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_Bt_full_t):
     New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_print_backtrace):
     New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_backtrace_state):
     New.
     [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
     (_Error_formatter::_M_backtrace_full_func): New.
     * src/c++11/debug.cc: Include <cstring>.
     (_Print_func_t): New.
     (print_word): Use '%.*s' format in fprintf to render only expected
     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.
     * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.
     * doc/xml/manual/using.xml: Likewise.

Tested under Linux x86_64 normal and debug modes.

Ok to commit ?

François
diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 23a5df975a2..ebfc1b89fd7 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 when defined, if libbacktrace is not properly installed,
+  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..a4cf920ee67 100644
--- a/libstdc++-v3/doc/xml/manual/using.xml
+++ b/libstdc++-v3/doc/xml/manual/using.xml
@@ -1128,6 +1128,18 @@ 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.
+	Note that contrarily to _GLIBCXX_DEBUG this macro shall be defined
+	consistently throughout the application translation units.
+      </para>
+    </listitem></varlistentry>
     <varlistentry><term><code>_GLIBCXX_PARALLEL</code></term>
     <listitem>
       <para>Undefined by default. When defined, compiles user code
@@ -1634,6 +1646,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..3bab508e10e 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,29 @@
 
 #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>
+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_LIBBACKTRACE 0
+# include <stdint.h> // For uintptr_t.
+typedef int (*__backtrace_full_cb) (void*, uintptr_t, const char*,
+				    int, const char*);
+
+typedef void (*__backtrace_error_cb) (void*, const char*, int);
+
+struct __backtrace_state;
+#endif
+
 #if __cpp_rtti
 # include <typeinfo>
 # define _GLIBCXX_TYPEID(_Type) &typeid(_Type)
@@ -156,6 +179,12 @@ namespace __gnu_debug
 
   class _Error_formatter
   {
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    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 +587,25 @@ 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
+    { _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)
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+# 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 +621,10 @@ namespace __gnu_debug
     unsigned int	_M_num_parameters;
     const char*		_M_text;
     const char*		_M_function;
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    __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..c1ac1bc844b 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"
 
@@ -580,16 +576,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;
 
@@ -615,15 +612,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')
 	  {
@@ -636,7 +630,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;
+	  }
       }
   }
 
@@ -653,7 +697,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);
 	}
     }
@@ -921,61 +968,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
@@ -984,8 +1032,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;
 	  }
 
@@ -993,26 +1042,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
@@ -1054,41 +1157,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)
+#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE
+    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: ");
 
     // 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

Patch

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: ");