From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 68721 invoked by alias); 10 Dec 2018 23:08:28 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 68690 invoked by uid 89); 10 Dec 2018 23:08:27 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RCVD_IN_DNSWL_NONE,SPF_PASS,TIME_LIMIT_EXCEEDED autolearn=unavailable version=3.3.2 spammy=displayed, 1st, portable, brackets X-HELO: mail-wm1-f45.google.com Received: from mail-wm1-f45.google.com (HELO mail-wm1-f45.google.com) (209.85.128.45) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 10 Dec 2018 23:08:17 +0000 Received: by mail-wm1-f45.google.com with SMTP id y139so323900wmc.5; Mon, 10 Dec 2018 15:08:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=to:from:subject:message-id:date:user-agent:mime-version :content-language; bh=OT2C6WNxzJzpSIJhhtQ9Kn5mbGAze2bcHMjalnXp1ps=; b=UsVMjl5+kZSe3/ctGdDhlEfuAhTC/V5syKiHggBTb3aWV4KVYOWZcRV7olpAxAAms3 LkyOJEFlWnGP0cbephiM5pS0Z5vRPs59s6V3zkGX93zjGnBPoMuX8lG9qZP+WZ/iHrSI TlU5QlcMP8+RkFff2cApDZyZNr3NH6lYq+82MwxnXzCgD9BXA4mBn2DhmyV/M2m78Ipp XjhfV0q6vd6ENzOY48aF79t+qLZueE7HNQQnDukf1Nke2ZIRPN22aE/HR8eePdTkKTB3 pe7x2+V7SNSTfggnjneTW8IJmbbqsidix+O3M1LMVuEsIcM1X91Wwmcr9PRTe3wAS701 K85g== Return-Path: Received: from [10.20.205.177] ([109.144.208.98]) by smtp.googlemail.com with ESMTPSA id x8sm840600wmd.45.2018.12.10.15.08.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 10 Dec 2018 15:08:13 -0800 (PST) To: "libstdc++@gcc.gnu.org" , gcc-patches From: =?UTF-8?Q?Fran=c3=a7ois_Dumont?= Subject: libbacktrace integration for _GLIBCXX_DEBUG mode Message-ID: Date: Mon, 10 Dec 2018 23:08:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------0D9CD5B3B18B7C37443A426A" X-SW-Source: 2018-12/txt/msg00607.txt.bz2 This is a multi-part message in MIME format. --------------0D9CD5B3B18B7C37443A426A Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-length: 5036 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;     _Sequence = std::__debug::list; _Category =     std::forward_iterator_tag] Backtrace:     0x40275f __gnu_debug::_Safe_iterator>::operator++() /home/fdt/dev/gcc/install/include/c++/9.0.0/debug/safe_iterator.h:321     0x402181 __gnu_debug::_Safe_iterator>::operator++() /home/fdt/dev/gcc/install/include/c++/9.0.0/debug/safe_iterator.h:570     0x404082 std::iterator_traits<__gnu_debug::_Safe_iterator> >::difference_type std::__distance<__gnu_debug::_Safe_iterator> >(__gnu_debug::_Safe_iterator>, __gnu_debug::_Safe_iterator>, 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> >::difference_type std::distance<__gnu_debug::_Safe_iterator> >(__gnu_debug::_Safe_iterator>, __gnu_debug::_Safe_iterator>) /home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_iterator_base_funcs.h:141     0x4030b9 void std::vector::_M_range_initialize<__gnu_debug::_Safe_iterator> >(__gnu_debug::_Safe_iterator>, __gnu_debug::_Safe_iterator>, std::forward_iterator_tag) /home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_vector.h:1541     0x402a2d std::vector::vector<__gnu_debug::_Safe_iterator>, void>(__gnu_debug::_Safe_iterator>, __gnu_debug::_Safe_iterator>) /home/fdt/dev/gcc/install/include/c++/9.0.0/bits/stl_vector.h:618     0x4022ec std::__debug::vector::vector<__gnu_debug::_Safe_iterator>, void>(__gnu_debug::_Safe_iterator>, __gnu_debug::_Safe_iterator>)     /home/fdt/dev/gcc/install/include/c++/9.0.0/debug/vector:195     0x401e2c void __gnu_test::check_construct3 >()     ./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 (mutable iterator);       state = past-the-end;       references sequence with type 'std::__debug::list' @ 0x0x7fff068ae080     }     * include/debug/formatter.h: Check for backtrace-supported.h access     and include it.     [BACKTRACE_SUPPORTED] Include     (_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 .     (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 --------------0D9CD5B3B18B7C37443A426A Content-Type: text/x-patch; name="libbacktrace.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="libbacktrace.patch" Content-length: 10654 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 +#if !defined(BACKTRACE_SUPPORTED) \ + && defined(__has_include) && __has_include() +# include +#endif + +#if BACKTRACE_SUPPORTED +# include +#else +# include // 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 # 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 // . +// Make sure we won't have libbacktrace dependency when building the library. +#define BACKTRACE_SUPPORTED 0 + #include #include @@ -34,16 +37,13 @@ #include #include -#include // for std::isspace - -#include // for std::min +#include // for std::isspace. +#include // for std::strstr. -#include // for __cxa_demangle +#include // for std::min. +#include -// libstdc++/85768 -#if 0 // defined _GLIBCXX_HAVE_EXECINFO_H -# include // for backtrace -#endif +#include // 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 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 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(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: "); --------------0D9CD5B3B18B7C37443A426A--