diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 5323c7f0604..2606d67d8a9 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2397,6 +2397,15 @@ GLIBCXX_3.4.29 { } GLIBCXX_3.4.28; +GLIBCXX_3.4.30 { + + # __gnu_debug::__create_backtrace + _ZN11__gnu_debug24__create_backtrace_stateEv; + _ZN11__gnu_debug18__render_backtraceEPvPFiS0_mPKciS2_ES0_; + +} GLIBCXX_3.4.29; + + # Symbols in the support library (libsupc++) have their own tag. CXXABI_1.3 { diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml index 883e8cb4f03..931b09710f3 100644 --- a/libstdc++-v3/doc/xml/manual/debug_mode.xml +++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml @@ -160,6 +160,12 @@ which always works correctly. GLIBCXX_DEBUG_MESSAGE_LENGTH can be used to request a different length. +Note that libstdc++ is able to use + libbacktrace + to produce backtraces on error. Use -D_GLIBCXX_DEBUG_BACKTRACE to + activate it. You'll also have to link with libbacktrace + () to build your application.
Using a Specific Debug Container diff --git a/libstdc++-v3/doc/xml/manual/using.xml b/libstdc++-v3/doc/xml/manual/using.xml index 24543e9526e..9bd0da8c1c5 100644 --- a/libstdc++-v3/doc/xml/manual/using.xml +++ b/libstdc++-v3/doc/xml/manual/using.xml @@ -1128,6 +1128,16 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2 hello.cc -o test.exe extensions and libstdc++-specific behavior into errors. + _GLIBCXX_DEBUG_BACKTRACE + + + Undefined by default. Considered only if _GLIBCXX_DEBUG + is defined. When defined, checks for libbacktrace + support and use it to display backtraces on + debug mode assertions. + + _GLIBCXX_PARALLEL 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.
+ +
External Libraries + + + libstdc++ debug mode is able to + produce backtraces thanks to libbacktrace. + To use the library you should define _GLIBCXX_DEBUG_BACKTRACE along + with _GLIBCXX_DEBUG and link with . + +
Concurrency diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h index 7140fed5e83..ec0984e438f 100644 --- a/libstdc++-v3/include/debug/formatter.h +++ b/libstdc++-v3/include/debug/formatter.h @@ -31,6 +31,31 @@ #include +#if defined(_GLIBCXX_DEBUG_BACKTRACE) +# if !defined(BACKTRACE_SUPPORTED) +# include +# endif +#endif + +#if defined(_GLIBCXX_DEBUG_BACKTRACE) && BACKTRACE_SUPPORTED +# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 1 +# include + +namespace __gnu_debug +{ + typedef backtrace_full_callback __backtrace_full_cb; +} +#elif defined (_GLIBCXX_USE_C99_STDINT_TR1) +# define _GLIBCXX_DEBUG_USE_LIBBACKTRACE 0 +# include // For uintptr_t. + +namespace __gnu_debug +{ + typedef int (*__backtrace_full_cb) (void*, uintptr_t, const char*, + int, const char*); +} +#endif + #if __cpp_rtti # include # define _GLIBCXX_TYPEID(_Type) &typeid(_Type) @@ -155,6 +180,16 @@ namespace __gnu_debug __msg_irreflexive_ordering }; +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + + void* + __create_backtrace_state(); + + void + __render_backtrace(void*, __backtrace_full_cb, void*); + +#endif + class _Error_formatter { // Tags denoting the type of parameter for construction @@ -558,12 +593,14 @@ namespace __gnu_debug _M_print_string(const char* __string) const _GLIBCXX_DEPRECATED; #endif +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + void + _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const; +#endif + private: _Error_formatter(const char* __file, unsigned int __line, - const char* __function) - : _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0) - , _M_function(__function) - { } + const char* __function); #if !_GLIBCXX_INLINE_VERSION void @@ -578,6 +615,9 @@ namespace __gnu_debug unsigned int _M_num_parameters; const char* _M_text; const char* _M_function; +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + void* _M_backtrace_state; +#endif public: static _Error_formatter& @@ -587,6 +627,36 @@ namespace __gnu_debug return __formatter; } }; + +#if _GLIBCXX_DEBUG_USE_LIBBACKTRACE + + void* + __create_backtrace_state() + { return backtrace_create_state(NULL, 0, NULL, NULL); } + + void + __render_backtrace(void* __bt, __backtrace_full_cb __cb, void* __data) + { backtrace_full((backtrace_state*)__bt, 1, __cb, 0, __data); } + +#endif + + _Error_formatter:: + _Error_formatter(const char* __file, unsigned int __line, + const char* __function) + : _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0) + , _M_function(__function) +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + , _M_backtrace_state(__create_backtrace_state()) +#endif + { } + +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + void + _Error_formatter:: + _M_print_backtrace(__backtrace_full_cb __cb, void* __data) const + { __render_backtrace(_M_backtrace_state, __cb, __data); } +#endif + } // namespace __gnu_debug #undef _GLIBCXX_TYPEID diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc index 5a642097d17..790810fdab4 100644 --- a/libstdc++-v3/src/c++11/debug.cc +++ b/libstdc++-v3/src/c++11/debug.cc @@ -34,16 +34,12 @@ #include #include -#include // for std::isspace +#include // for std::isspace. +#include // for std::strstr. -#include // for std::min +#include // for std::min. -#include // for __cxa_demangle - -// libstdc++/85768 -#if 0 // defined _GLIBCXX_HAVE_EXECINFO_H -# include // for backtrace -#endif +#include // for __cxa_demangle. #include "mutex_pool.h" @@ -574,7 +570,7 @@ namespace struct PrintContext { PrintContext() - : _M_max_length(78), _M_column(1), _M_first_line(true), _M_wordwrap(false) + : _M_max_length(78), _M_column(1), _M_first_line(true), _M_wordwrap(false) { get_max_length(_M_max_length); } std::size_t _M_max_length; @@ -584,16 +580,17 @@ namespace bool _M_wordwrap; }; + typedef void (*_Print_func_t) (PrintContext&, const char*, ptrdiff_t); + template void print_literal(PrintContext& ctx, const char(&word)[Length]) { print_word(ctx, word, Length - 1); } void - print_word(PrintContext& ctx, const char* word, - std::ptrdiff_t count = -1) + print_word(PrintContext& ctx, const char* word, ptrdiff_t nbc = -1) { - size_t length = count >= 0 ? count : __builtin_strlen(word); + size_t length = nbc >= 0 ? nbc : __builtin_strlen(word); if (length == 0) return; @@ -619,15 +616,12 @@ namespace // If this isn't the first line, indent if (ctx._M_column == 1 && !ctx._M_first_line) { - char spacing[ctx._M_indent + 1]; - for (int i = 0; i < ctx._M_indent; ++i) - spacing[i] = ' '; - spacing[ctx._M_indent] = '\0'; + const char spacing[ctx._M_indent + 1] = " "; fprintf(stderr, "%s", spacing); ctx._M_column += ctx._M_indent; } - int written = fprintf(stderr, "%s", word); + int written = fprintf(stderr, "%.*s", (int)length, word); if (word[length - 1] == '\n') { @@ -640,7 +634,57 @@ namespace else { print_literal(ctx, "\n"); - print_word(ctx, word, count); + print_word(ctx, word, nbc); + } + } + + void + print_raw(PrintContext&, const char* str, ptrdiff_t nbc) + { + if (nbc >= 0) + fprintf(stderr, "%.*s", (int)nbc, str); + else + fprintf(stderr, "%s", str); + } + + void + print_function(PrintContext& ctx, const char* func, + _Print_func_t print_func) + { + const char cxx1998[] = "__cxx1998::"; + const char uglification[] = "__"; + for (;;) + { + auto idx1 = strstr(func, cxx1998); + auto idx2 = strstr(func, uglification); + if (idx1 || idx2) + { + if (idx1 && (!idx2 || idx1 <= idx2)) + { + if (idx1 != func) + print_func(ctx, func, idx1 - func); + + func = idx1 + sizeof(cxx1998) - 1; + } + else if (idx2) + { + if (idx2 != func) + print_func(ctx, func, idx2 - func); + + func = idx2 + sizeof(uglification) - 1; + } + + while (*func && isspace(*func)) + ++func; + + if (!*func) + break; + } + else + { + print_func(ctx, func, -1); + break; + } } } @@ -657,7 +701,10 @@ namespace int status; char* demangled_name = __cxxabiv1::__cxa_demangle(info->name(), NULL, NULL, &status); - print_word(ctx, status == 0 ? demangled_name : info->name()); + if (status == 0) + print_function(ctx, demangled_name, &print_word); + else + print_word(ctx, info->name(), -1); free(demangled_name); } } @@ -925,61 +972,62 @@ namespace } void - print_string(PrintContext& ctx, const char* string, + print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc, const _Parameter* parameters, std::size_t num_parameters) { - const char* start = string; - const int bufsize = 128; - char buf[bufsize]; - int bufindex = 0; + const char* start = str; + const char* end = nbc >= 0 ? start + nbc : nullptr; + char buf[64]; - while (*start) + while ((end && str != end) || (!end && *str)) { - if (isspace(*start)) + if (isspace(*str)) { - buf[bufindex++] = *start++; - buf[bufindex] = '\0'; - print_word(ctx, buf, bufindex); - bufindex = 0; + ++str; + print_word(ctx, start, str - start); + start = str; continue; } - if (!num_parameters || *start != '%') + if (!parameters || *str != '%') { // Normal char or no parameter to look for. - buf[bufindex++] = *start++; + ++str; continue; } - if (*++start == '%') + if (*++str == '%') { // Escaped '%' - buf[bufindex++] = *start++; + print_word(ctx, start, str - start); + ++str; + start = str; continue; } // We are on a parameter property reference, we need to flush buffer // first. - if (bufindex != 0) + if (str != start) { - buf[bufindex] = '\0'; - print_word(ctx, buf, bufindex); - bufindex = 0; + // Avoid to print the '%'. + if (str - start > 1) + print_word(ctx, start, str - start - 1); + start = str; } // Get the parameter number - assert(*start >= '1' && *start <= '9'); - size_t param_index = *start - '0' - 1; + assert(*str >= '1' && *str <= '9'); + size_t param_index = *str - '0' - 1; assert(param_index < num_parameters); const auto& param = parameters[param_index]; // '.' separates the parameter number from the field // name, if there is one. - ++start; - if (*start != '.') + ++str; + if (*str != '.') { - assert(*start == ';'); - ++start; + assert(*str == ';'); + ++str; if (param._M_kind == _Parameter::__integer) { int written @@ -988,8 +1036,9 @@ namespace print_word(ctx, buf, written); } else if (param._M_kind == _Parameter::__string) - print_string(ctx, param._M_variant._M_string._M_value, + print_string(ctx, param._M_variant._M_string._M_value, -1, parameters, num_parameters); + start = str; continue; } @@ -997,26 +1046,80 @@ namespace const int max_field_len = 16; char field[max_field_len]; int field_idx = 0; - ++start; - while (*start != ';') + ++str; + while (*str != ';') { - assert(*start); + assert(*str); assert(field_idx < max_field_len - 1); - field[field_idx++] = *start++; + field[field_idx++] = *str++; } - ++start; + ++str; field[field_idx] = '\0'; print_field(ctx, param, field); + start = str; } // Might need to flush. - if (bufindex) + if (str != start) + print_word(ctx, start, str - start); + } + + void + print_string(PrintContext& ctx, const char* str, ptrdiff_t nbc) + { print_string(ctx, str, nbc, nullptr, 0); } + +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + int + print_backtrace(void* data, uintptr_t pc, const char* filename, + int lineno, const char* function) + { + const int bufsize = 64; + char buf[bufsize]; + + PrintContext& ctx = *static_cast(data); + + int written = __builtin_sprintf(buf, "%p ", (void*)pc); + print_word(ctx, buf, written); + + int ret = 0; + if (function) { - buf[bufindex] = '\0'; - print_word(ctx, buf, bufindex); + int status; + char* demangled_name = + __cxxabiv1::__cxa_demangle(function, NULL, NULL, &status); + if (status == 0) + print_function(ctx, demangled_name, &print_raw); + else + print_word(ctx, function); + + free(demangled_name); + ret = strstr(function, "main") ? 1 : 0; } + + print_literal(ctx, "\n"); + + if (filename) + { + bool wordwrap = false; + swap(wordwrap, ctx._M_wordwrap); + print_word(ctx, filename); + + if (lineno) + { + written = __builtin_sprintf(buf, ":%u\n", lineno); + print_word(ctx, buf, written); + } + else + print_literal(ctx, "\n"); + swap(wordwrap, ctx._M_wordwrap); + } + else + print_literal(ctx, "???:0\n"); + + return ret; } +#endif } namespace __gnu_debug @@ -1058,41 +1161,27 @@ namespace __gnu_debug if (_M_function) { print_literal(ctx, "In function:\n"); - print_string(ctx, _M_function, nullptr, 0); + print_function(ctx, _M_function, &print_string); print_literal(ctx, "\n"); ctx._M_first_line = true; print_literal(ctx, "\n"); } -// libstdc++/85768 -#if 0 //defined _GLIBCXX_HAVE_EXECINFO_H - { - void* stack[32]; - int nb = backtrace(stack, 32); - - // Note that we skip current method symbol. - if (nb > 1) - { - print_literal(ctx, "Backtrace:\n"); - auto symbols = backtrace_symbols(stack, nb); - for (int i = 1; i < nb; ++i) - { - print_word(ctx, symbols[i]); - print_literal(ctx, "\n"); - } - - free(symbols); - ctx._M_first_line = true; - print_literal(ctx, "\n"); - } - } +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + if (_M_backtrace_state) + { + print_literal(ctx, "Backtrace:\n"); + _M_print_backtrace(print_backtrace, &ctx); + ctx._M_first_line = true; + print_literal(ctx, "\n"); + } #endif print_literal(ctx, "Error: "); // Print the error message assert(_M_text); - print_string(ctx, _M_text, _M_parameters, _M_num_parameters); + print_string(ctx, _M_text, -1, _M_parameters, _M_num_parameters); print_literal(ctx, ".\n"); // Emit descriptions of the objects involved in the operation @@ -1123,6 +1212,18 @@ namespace __gnu_debug abort(); } +#ifdef _GLIBCXX_DEBUG_USE_LIBBACKTRACE + + _GLIBCXX_WEAK_DEFINITION void* + __create_backtrace_state() + { return nullptr; } + + _GLIBCXX_WEAK_DEFINITION void + __render_backtrace(void*, __backtrace_full_cb, void*) + { } + +#endif + #if !_GLIBCXX_INLINE_VERSION // Deprecated methods kept for backward compatibility. void diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc index 3af5dc593c2..2c3204e57a5 100644 --- a/libstdc++-v3/testsuite/util/testsuite_abi.cc +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -210,6 +210,7 @@ check_version(symbol& test, bool added) known_versions.push_back("GLIBCXX_3.4.27"); known_versions.push_back("GLIBCXX_3.4.28"); known_versions.push_back("GLIBCXX_3.4.29"); + known_versions.push_back("GLIBCXX_3.4.30"); known_versions.push_back("GLIBCXX_LDBL_3.4.29"); known_versions.push_back("GLIBCXX_IEEE128_3.4.29"); known_versions.push_back("CXXABI_1.3");