This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

libbacktrace integration for _GLIBCXX_DEBUG mode


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


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]