diff mbox

debug mode symbols cleanup

Message ID 55EB562F.9090607@gmail.com
State New
Headers show

Commit Message

François Dumont Sept. 5, 2015, 8:53 p.m. UTC
Hi

    I wanted to enhance line wrapping during rendering of debug messages
and ended up with this patch.

    I clean up _Error_formatter of all useless symbols, only _M_message
and _M_error need to be exposed. Additional I got rid  of the ugly
mutable fields of _Error_formatter. _Error_formatter instance in _M_at
method can be made static because we only create an instance when we
detect an issue so just before application abort.

    I remember Paolo saying once that we were not guarantiing any abi
compatibility for debug mode. I haven't found any section for
unversioned symbols in gnu.ver so I simply uncomment the global export.

Tested under Linux x86_64.

    * include/debug/formatter.h
    (_Error_formatter::_Parameter::_M_print_field): Delete.
    (_Error_formatter::_Parameter::_M_print_description): Likewise.
    (_Error_formatter::_M_format_word): Likewise.
    (_Error_formatter::_M_print_word): Likewise.
    (_Error_formatter::_M_print_string): Likewise.
    (_Error_formatter::_M_get_max_length): Likewise.
    (_Error_formatter::_M_max_length): Likewise.
    (_Error_formatter::_M_indent): Likewise.
    (_Error_formatter::_M_column): Likewise.
    (_Error_formatter::_M_first_line): Likewise.
    (_Error_formatter::_M_wordwrap): Likewise.
    * src/c++11/debug.cc: Adapt.

François

Comments

Jonathan Wakely Sept. 7, 2015, 11:03 a.m. UTC | #1
On 05/09/15 22:53 +0200, François Dumont wrote:
>    I remember Paolo saying once that we were not guarantiing any abi
>compatibility for debug mode. I haven't found any section for
>unversioned symbols in gnu.ver so I simply uncomment the global export.

There is no section, because all exported symbols are versioned.

It's OK if objects compiled with Debug Mode using one version of GCC
don't link to objects compiled with Debug Mode using a different
version of GCC, but you can't change the exported symbols in the DSO.


Your changelog doesn't include the changes to config/abi/pre/gnu.ver,
but those changes are not OK anyway, they fail the abi-check:

FAIL: libstdc++-abi/abi_check

                === libstdc++ Summary ===

# of unexpected failures        1
Jonathan Wakely Sept. 18, 2015, 11:05 a.m. UTC | #2
On 18/09/15 13:00 +0200, Ulrich Weigand wrote:
>Francois Dumont wrote:
>
>>     * include/debug/formatter.h
>>     (_Error_formatter::_Parameter::_M_print_field): Delete.
>>     (_Error_formatter::_Parameter::_M_print_description): Likewise.
>>     (_Error_formatter::_M_format_word): Likewise.
>>     (_Error_formatter::_M_print_word): Likewise.
>>     (_Error_formatter::_M_print_string): Likewise.
>>     (_Error_formatter::_M_get_max_length): Likewise.
>>     (_Error_formatter::_M_max_length): Likewise.
>>     (_Error_formatter::_M_indent): Likewise.
>>     (_Error_formatter::_M_column): Likewise.
>>     (_Error_formatter::_M_first_line): Likewise.
>>     (_Error_formatter::_M_wordwrap): Likewise.
>>     * src/c++11/debug.cc: Adapt.
>
>This seems to break building an spu-elf cross-compiler:
>
>/home/uweigand/dailybuild/spu-tc-2015-09-17/gcc-head/src/libstdc++-v3/src/c++11/debug.cc: In function 'void {anonymous}::print_word({anonymous}::PrintContext&, const char*, std::ptrdiff_t)':
>/home/uweigand/dailybuild/spu-tc-2015-09-17/gcc-head/src/libstdc++-v3/src/c++11/debug.cc:573:10: error: 'stderr' was not declared in this scope
>  fprintf(stderr, "\n");
>          ^
>/home/uweigand/dailybuild/spu-tc-2015-09-17/gcc-head/src/libstdc++-v3/src/c++11/debug.cc:573:22: error: 'fprintf' was not declared in this scope
>  fprintf(stderr, "\n");
>                      ^

Catherine fixed this with r227888.
diff mbox

Patch

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 8f9f99a..6d57cab 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -194,7 +194,7 @@  GLIBCXX_3.4 {
       std::__moneypunct_cache*;
       std::__numpunct_cache*;
       std::__timepunct_cache*;
-#     __gnu_debug::_Error_formatter*
+      __gnu_debug::_Error_formatter*
     };
 
     # Names not in an 'extern' block are mangled names.  Character classes
@@ -687,13 +687,6 @@  GLIBCXX_3.4 {
     _ZNK11__gnu_debug19_Safe_iterator_base11_M_singularEv;
     _ZNK11__gnu_debug19_Safe_iterator_base14_M_can_compareERKS0_;
 
-    # __gnu_debug::_Error_formatter
-    _ZNK11__gnu_debug16_Error_formatter10_M_message*;
-    _ZNK11__gnu_debug16_Error_formatter10_Parameter*;
-    _ZNK11__gnu_debug16_Error_formatter13_M_print_word*;
-    _ZNK11__gnu_debug16_Error_formatter15_M_print_string*;
-    _ZNK11__gnu_debug16_Error_formatter8_M_error*;
-
     # exceptions as functions
     _ZSt16__throw_bad_castv;
     _ZSt17__throw_bad_allocv;
@@ -1185,8 +1178,6 @@  GLIBCXX_3.4.9 {
 
 GLIBCXX_3.4.10 {
 
-    _ZNK11__gnu_debug16_Error_formatter17_M_get_max_lengthEv;
-
     _ZNKSt3tr14hashIRKSbIwSt11char_traitsIwESaIwEEEclES6_;
     _ZNKSt3tr14hashIRKSsEclES2_;
     _ZNKSt3tr14hashISbIwSt11char_traitsIwESaIwEEEclES4_;
diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index f0ac694..e22247c 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -133,6 +133,13 @@  namespace __gnu_debug
 
   class _Error_formatter
   {
+    // Tags denoting the type of parameter for construction
+    struct _Is_iterator { };
+    struct _Is_iterator_value_type { };
+    struct _Is_sequence { };
+    struct _Is_instance { };
+
+  public:
     /// Whether an iterator is constant, mutable, or unknown
     enum _Constness
     {
@@ -154,13 +161,6 @@  namespace __gnu_debug
       __last_state
     };
 
-    // Tags denoting the type of parameter for construction
-    struct _Is_iterator { };
-    struct _Is_iterator_value_type { };
-    struct _Is_sequence { };
-    struct _Is_instance { };
-
-  public:
     // A parameter that may be referenced by an error message
     struct _Parameter
     {
@@ -373,18 +373,11 @@  namespace __gnu_debug
 	  _M_variant._M_instance._M_address = &__inst;
 	  _M_variant._M_instance._M_type = _GLIBCXX_TYPEID(_Type);
 	}
-
-      void
-      _M_print_field(const _Error_formatter* __formatter,
-		     const char* __name) const;
-
-      void
-      _M_print_description(const _Error_formatter* __formatter) const;
     };
 
     template<typename _Iterator>
-      const _Error_formatter&
-      _M_iterator(const _Iterator& __it, const char* __name = 0)  const
+      _Error_formatter&
+      _M_iterator(const _Iterator& __it, const char* __name = 0)
       {
 	if (_M_num_parameters < std::size_t(__max_parameters))
 	  _M_parameters[_M_num_parameters++] = _Parameter(__it, __name,
@@ -393,98 +386,82 @@  namespace __gnu_debug
       }
 
     template<typename _Iterator>
-      const _Error_formatter&
+      _Error_formatter&
       _M_iterator_value_type(const _Iterator& __it,
-			     const char* __name = 0)  const
+			     const char* __name = 0)
       {
-	if (_M_num_parameters < std::size_t(__max_parameters))
+	if (_M_num_parameters < __max_parameters)
 	  _M_parameters[_M_num_parameters++] =
 	    _Parameter(__it, __name, _Is_iterator_value_type());
 	return *this;
       }
 
-    const _Error_formatter&
-    _M_integer(long __value, const char* __name = 0) const
+    _Error_formatter&
+    _M_integer(long __value, const char* __name = 0)
     {
-      if (_M_num_parameters < std::size_t(__max_parameters))
+      if (_M_num_parameters < __max_parameters)
 	_M_parameters[_M_num_parameters++] = _Parameter(__value, __name);
       return *this;
     }
 
-    const _Error_formatter&
-    _M_string(const char* __value, const char* __name = 0) const
+    _Error_formatter&
+    _M_string(const char* __value, const char* __name = 0)
     {
-      if (_M_num_parameters < std::size_t(__max_parameters))
+      if (_M_num_parameters < __max_parameters)
 	_M_parameters[_M_num_parameters++] = _Parameter(__value, __name);
       return *this;
     }
 
     template<typename _Sequence>
-      const _Error_formatter&
-      _M_sequence(const _Sequence& __seq, const char* __name = 0) const
+      _Error_formatter&
+      _M_sequence(const _Sequence& __seq, const char* __name = 0)
       {
-	if (_M_num_parameters < std::size_t(__max_parameters))
+	if (_M_num_parameters < __max_parameters)
 	  _M_parameters[_M_num_parameters++] = _Parameter(__seq, __name,
 							  _Is_sequence());
 	return *this;
       }
 
     template<typename _Type>
-      const _Error_formatter&
-      _M_instance(const _Type& __inst, const char* __name = 0) const
+      _Error_formatter&
+      _M_instance(const _Type& __inst, const char* __name = 0)
       {
-	if (_M_num_parameters < std::size_t(__max_parameters))
+	if (_M_num_parameters < __max_parameters)
 	  _M_parameters[_M_num_parameters++] = _Parameter(__inst, __name,
 							  _Is_instance());
 	return *this;
       }
 
-    const _Error_formatter&
-    _M_message(const char* __text) const
+    _Error_formatter&
+    _M_message(const char* __text)
     { _M_text = __text; return *this; }
 
-    const _Error_formatter&
-    _M_message(_Debug_msg_id __id) const throw ();
+    _Error_formatter&
+    _M_message(_Debug_msg_id __id) throw ();
 
     _GLIBCXX_NORETURN void
     _M_error() const;
 
-    template<typename _Tp>
-      void
-      _M_format_word(char*, int, const char*, _Tp) const throw ();
-
-    void
-    _M_print_word(const char* __word) const;
-
-    void
-    _M_print_string(const char* __string) const;
-
   private:
-    _Error_formatter(const char* __file, std::size_t __line)
-    : _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0),
-      _M_max_length(78), _M_column(1), _M_first_line(true), _M_wordwrap(false)
-    { _M_get_max_length(); }
-
-    void
-    _M_get_max_length() const throw ();
+    _Error_formatter(const char* __file, unsigned int __line)
+    : _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0)
+    { }
 
     enum { __max_parameters = 9 };
 
     const char*		_M_file;
-    std::size_t		_M_line;
-    mutable _Parameter	_M_parameters[__max_parameters];
-    mutable std::size_t	_M_num_parameters;
-    mutable const char*	_M_text;
-    mutable std::size_t	_M_max_length;
-    enum { _M_indent = 4 } ;
-    mutable std::size_t	_M_column;
-    mutable bool	_M_first_line;
-    mutable bool	_M_wordwrap;
+    unsigned int	_M_line;
+    _Parameter		_M_parameters[__max_parameters];
+    unsigned int	_M_num_parameters;
+    const char*		_M_text;
 
   public:
-    static _Error_formatter
-    _M_at(const char* __file, std::size_t __line)
-    { return _Error_formatter(__file, __line); }
+    static _Error_formatter&
+    _M_at(const char* __file, unsigned int __line)
+    {
+      static _Error_formatter __formatter(__file, __line);
+      return __formatter;
+    }
   };
 } // namespace __gnu_debug
 
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index d3f8cae..6a41e2c 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -22,18 +22,19 @@ 
 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 // <http://www.gnu.org/licenses/>.
 
-#include <debug/debug.h>
+#include <bits/move.h>
+#include <bits/stl_iterator_base_types.h>
+
+#include <debug/formatter.h>
 #include <debug/safe_base.h>
 #include <debug/safe_unordered_base.h>
 #include <debug/safe_iterator.h>
 #include <debug/safe_local_iterator.h>
-#include <algorithm>
+
 #include <cassert>
-#include <cstring>
-#include <cctype>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
+
+#include <algorithm> // for std::min
+#include <functional> // for _Hash_impl
 
 #include <cxxabi.h> // for __cxa_demangle
 
@@ -526,202 +527,236 @@  namespace __gnu_debug
 
 namespace
 {
+  using _Error_formatter = __gnu_debug::_Error_formatter;
+  using _Parameter = __gnu_debug::_Error_formatter::_Parameter;
+
+  template<typename _Tp>
+    int
+    format_word(char* buf, int n, const char* fmt, _Tp s)
+    { return std::min(__builtin_snprintf(buf, n, fmt, s), n - 1); }
+
   void
-  print_type(const __gnu_debug::_Error_formatter* __formatter,
-	     const type_info* __info,
-	     const char* __unknown_name)
+  get_max_length(std::size_t& max_length)
   {
-    if (!__info)
-      __formatter->_M_print_word(__unknown_name);
-    else
+    const char* nptr = std::getenv("GLIBCXX_DEBUG_MESSAGE_LENGTH");
+    if (nptr)
       {
-	int __status;
-	char* __demangled_name =
-	  __cxxabiv1::__cxa_demangle(__info->name(), NULL, NULL, &__status);
-	__formatter->_M_print_word(__status == 0
-				   ? __demangled_name : __info->name());
-	free(__demangled_name);
+	char* endptr;
+	const unsigned long ret = std::strtoul(nptr, &endptr, 0);
+	if (*nptr != '\0' && *endptr == '\0')
+	  max_length = ret;
       }
   }
 
-  bool
-  print_field(
-    const __gnu_debug::_Error_formatter* __formatter,
-    const char* __name,
-    const __gnu_debug::_Error_formatter::_Parameter::_Type& __variant)
+  struct PrintContext
   {
-    if (strcmp(__name, "name") == 0)
-      {
-	assert(__variant._M_name);
-	__formatter->_M_print_word(__variant._M_name);
-      }
-    else if (strcmp(__name, "type") == 0)
-      print_type(__formatter, __variant._M_type, "<unknown type>");
-    else
-      return false;
-
-    return true;
-  }
+    PrintContext()
+      : _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;
+    enum { _M_indent = 4 } ;
+    std::size_t	_M_column;
+    bool	_M_first_line;
+    bool	_M_wordwrap;
+  };
 
-  bool
-  print_field(
-    const __gnu_debug::_Error_formatter* __formatter,
-    const char* __name,
-    const __gnu_debug::_Error_formatter::_Parameter::_Instance& __variant)
+  void
+  print_word(PrintContext& ctx, const char* word,
+	     std::ptrdiff_t count = -1)
   {
-    const __gnu_debug::_Error_formatter::_Parameter::_Type& __type = __variant;
-    if (print_field(__formatter, __name, __type))
-      { }
-    else if (strcmp(__name, "address") == 0)
+    size_t length = count >= 0 ? count : __builtin_strlen(word);
+    if (length == 0)
+      return;
+
+    // Consider first '\n' at begining cause it impacts column.
+    if (word[0] == '\n')
       {
-	const int __bufsize = 64;
-	char __buf[__bufsize];
-	__formatter->_M_format_word(__buf, __bufsize, "%p",
-				    __variant._M_address);
-	__formatter->_M_print_word(__buf);
-      }
-    else
-      return false;
+	fprintf(stderr, "\n");
+	ctx._M_column = 1;
+	++word;
+	--length;
 
-    return true;
-  }
+	if (length == 0)
+	  return;
+      }
 
-  void
-  print_description(
-	const __gnu_debug::_Error_formatter* __formatter,
-	const __gnu_debug::_Error_formatter::_Parameter::_Type& __variant)
-  {
-    if (__variant._M_name)
+    size_t visual_length
+      = isspace(word[length - 1]) ? length - 1 : length;
+    if (visual_length == 0
+	|| !ctx._M_wordwrap
+	|| (ctx._M_column + visual_length < ctx._M_max_length)
+	|| (visual_length >= ctx._M_max_length && ctx._M_column == 1))
       {
-	const int __bufsize = 64;
-	char __buf[__bufsize];
-	__formatter->_M_format_word(__buf, __bufsize, "\"%s\"",
-				    __variant._M_name);
-	__formatter->_M_print_word(__buf);
-      }
+	// 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';
+	    fprintf(stderr, "%s", spacing);
+	    ctx._M_column += ctx._M_indent;
+	  }
 
-    __formatter->_M_print_word(" {\n");
+	int written = fprintf(stderr, "%s", word);
 
-    if (__variant._M_type)
+	if (word[length - 1] == '\n')
+	  {
+	    ctx._M_first_line = false;
+	    ctx._M_column = 1;
+	  }
+	else
+	  ctx._M_column += written;
+      }
+    else
       {
-	__formatter->_M_print_word("  type = ");
-	print_type(__formatter, __variant._M_type, "<unknown type>");
-	__formatter->_M_print_word(";\n");
+	print_word(ctx, "\n", 1);
+	print_word(ctx, word, count);
       }
   }
 
-
   void
-  print_description(
-	const __gnu_debug::_Error_formatter* __formatter,
-	const __gnu_debug::_Error_formatter::_Parameter::_Instance& __variant)
+  print_type(PrintContext& ctx,
+	     const type_info* info,
+	     const char* unknown_name)
   {
-    const int __bufsize = 64;
-    char __buf[__bufsize];
+    if (!info)
+      print_word(ctx, unknown_name);
+    else
+      {
+	int status;
+	char* demangled_name =
+	  __cxxabiv1::__cxa_demangle(info->name(), NULL, NULL, &status);
+	print_word(ctx, status == 0 ? demangled_name : info->name());
+	free(demangled_name);
+      }
+  }
 
-    if (__variant._M_name)
+  bool
+  print_field(PrintContext& ctx,
+	      const char* name, const _Parameter::_Type& type)
+  {
+    if (__builtin_strcmp(name, "name") == 0)
       {
-	__formatter->_M_format_word(__buf, __bufsize, "\"%s\" ",
-				    __variant._M_name);
-	__formatter->_M_print_word(__buf);
+	assert(type._M_name);
+	print_word(ctx, type._M_name);
       }
+    else if (__builtin_strcmp(name, "type") == 0)
+      print_type(ctx, type._M_type, "<unknown type>");
+    else
+      return false;
 
-    __formatter->_M_format_word(__buf, __bufsize, "@ 0x%p {\n",
-				__variant._M_address);
-    __formatter->_M_print_word(__buf);
+    return true;
+  }
 
-    if (__variant._M_type)
+  bool
+  print_field(PrintContext& ctx,
+	      const char* name, const _Parameter::_Instance& inst)
+  {
+    const _Parameter::_Type& type = inst;
+    if (print_field(ctx, name, type))
+      { }
+    else if (__builtin_strcmp(name, "address") == 0)
       {
-	__formatter->_M_print_word("  type = ");
-	print_type(__formatter, __variant._M_type, "<unknown type>");
+	char buf[64];
+	int ret = __builtin_sprintf(buf, "%p", inst._M_address);
+	print_word(ctx, buf, ret);
       }
+    else
+      return false;
+
+    return true;
   }
-}
 
-namespace __gnu_debug
-{
   void
-  _Error_formatter::_Parameter::
-  _M_print_field(const _Error_formatter* __formatter, const char* __name) const
+  print_field(PrintContext& ctx, const _Parameter& param, const char* name)
   {
-    assert(this->_M_kind != _Parameter::__unused_param);
-    const int __bufsize = 64;
-    char __buf[__bufsize];
+    assert(param._M_kind != _Parameter::__unused_param);
+    const int bufsize = 64;
+    char buf[bufsize];
 
-    switch (_M_kind)
+    const auto& variant = param._M_variant;
+    switch (param._M_kind)
     {
-    case __iterator:
-      if (print_field(__formatter, __name, _M_variant._M_iterator))
-	{ }
-      else if (strcmp(__name, "constness") == 0)
-	{
-	  static const char* __constness_names[__last_constness] =
-	    {
-	      "<unknown>",
-	      "constant",
-	      "mutable"
-	    };
-	  __formatter->_M_print_word(__constness_names[_M_variant.
-						       _M_iterator.
-						       _M_constness]);
-	}
-      else if (strcmp(__name, "state") == 0)
-	{
-	  static const char* __state_names[__last_state] =
-	    {
-	      "<unknown>",
-	      "singular",
-	      "dereferenceable (start-of-sequence)",
-	      "dereferenceable",
-	      "past-the-end",
-	      "before-begin"
-	    };
-	  __formatter->_M_print_word(__state_names[_M_variant.
-						   _M_iterator._M_state]);
-	}
-      else if (strcmp(__name, "sequence") == 0)
-	{
-	  assert(_M_variant._M_iterator._M_sequence);
-	  __formatter->_M_format_word(__buf, __bufsize, "%p",
-				      _M_variant._M_iterator._M_sequence);
-	  __formatter->_M_print_word(__buf);
-	}
-      else if (strcmp(__name, "seq_type") == 0)
-	print_type(__formatter, _M_variant._M_iterator._M_seq_type,
-		   "<unknown seq_type>");
-      else
-	assert(false);
+    case _Parameter::__iterator:
+      {
+	const auto& iterator = variant._M_iterator;
+	if (print_field(ctx, name, iterator))
+	  { }
+	else if (__builtin_strcmp(name, "constness") == 0)
+	  {
+	    static const char*
+	      constness_names[_Error_formatter::__last_constness] =
+	      {
+		"<unknown>",
+		"constant",
+		"mutable"
+	      };
+	    print_word(ctx, constness_names[iterator._M_constness]);
+	  }
+	else if (__builtin_strcmp(name, "state") == 0)
+	  {
+	    static const char*
+	      state_names[_Error_formatter::__last_state] =
+	      {
+		"<unknown>",
+		"singular",
+		"dereferenceable (start-of-sequence)",
+		"dereferenceable",
+		"past-the-end",
+		"before-begin"
+	      };
+	    print_word(ctx, state_names[iterator._M_state]);
+	  }
+	else if (__builtin_strcmp(name, "sequence") == 0)
+	  {
+	    assert(iterator._M_sequence);
+	    int written = __builtin_sprintf(buf, "%p", iterator._M_sequence);
+	    print_word(ctx, buf, written);
+	  }
+	else if (__builtin_strcmp(name, "seq_type") == 0)
+	  print_type(ctx, iterator._M_seq_type, "<unknown seq_type>");
+	else
+	  assert(false);
+      }
       break;
-    case __sequence:
-      if (!print_field(__formatter, __name, _M_variant._M_sequence))
+
+    case _Parameter::__sequence:
+      if (!print_field(ctx, name, variant._M_sequence))
 	assert(false);
       break;
-    case __integer:
-      if (strcmp(__name, "name") == 0)
+
+    case _Parameter::__integer:
+      if (__builtin_strcmp(name, "name") == 0)
 	{
-	  assert(_M_variant._M_integer._M_name);
-	  __formatter->_M_print_word(_M_variant._M_integer._M_name);
+	  assert(variant._M_integer._M_name);
+	  print_word(ctx, variant._M_integer._M_name);
 	}
       else
 	assert(false);
       break;
-    case __string:
-      if (strcmp(__name, "name") == 0)
+
+    case _Parameter::__string:
+      if (__builtin_strcmp(name, "name") == 0)
 	{
-	  assert(_M_variant._M_string._M_name);
-	  __formatter->_M_print_word(_M_variant._M_string._M_name);
+	  assert(variant._M_string._M_name);
+	  print_word(ctx, variant._M_string._M_name);
 	}
       else
 	assert(false);
       break;
-    case __instance:
-      if (!print_field(__formatter, __name, _M_variant._M_instance))
+
+    case _Parameter::__instance:
+      if (!print_field(ctx, name, variant._M_instance))
 	assert(false);
       break;
-    case __iterator_value_type:
-      if (!print_field(__formatter, __name, _M_variant._M_iterator_value_type))
+
+    case _Parameter::__iterator_value_type:
+      if (!print_field(ctx, name, variant._M_iterator_value_type))
 	assert(false);
       break;
+
     default:
       assert(false);
       break;
@@ -729,322 +764,298 @@  namespace __gnu_debug
   }
 
   void
-  _Error_formatter::_Parameter::
-  _M_print_description(const _Error_formatter* __formatter) const
+  print_description(PrintContext& ctx, const _Parameter::_Type& type)
   {
-    const int __bufsize = 128;
-    char __buf[__bufsize];
-
-    switch (_M_kind)
+    if (type._M_name)
       {
-      case __iterator:
-	__formatter->_M_print_word("iterator ");
-	print_description(__formatter, _M_variant._M_iterator);
-
-	if (_M_variant._M_iterator._M_type)
-	  {
-	    if (_M_variant._M_iterator._M_constness != __unknown_constness)
-	      {
-		__formatter->_M_print_word(" (");
-		_M_print_field(__formatter, "constness");
-		__formatter->_M_print_word(" iterator)");
-	      }
-	    __formatter->_M_print_word(";\n");
-	  }
-
-	if (_M_variant._M_iterator._M_state != __unknown_state)
-	  {
-	    __formatter->_M_print_word("  state = ");
-	    _M_print_field(__formatter, "state");
-	    __formatter->_M_print_word(";\n");
-	  }
-
-	if (_M_variant._M_iterator._M_sequence)
-	  {
-	    __formatter->_M_print_word("  references sequence ");
-	    if (_M_variant._M_iterator._M_seq_type)
-	      {
-		__formatter->_M_print_word("with type `");
-		_M_print_field(__formatter, "seq_type");
-		__formatter->_M_print_word("' ");
-	      }
+	const int bufsize = 64;
+	char buf[bufsize];
+	int written
+	  = format_word(buf, bufsize, "\"%s\"", type._M_name);
+	print_word(ctx, buf, written);
+      }
 
-	    __formatter->_M_format_word(__buf, __bufsize, "@ 0x%p\n",
-					_M_variant._M_iterator._M_sequence);
-	    __formatter->_M_print_word(__buf);
-	  }
+    print_word(ctx, " {\n");
 
-	__formatter->_M_print_word("}\n");
-	break;
-      case __sequence:
-	__formatter->_M_print_word("sequence ");
-	print_description(__formatter, _M_variant._M_sequence);
+    if (type._M_type)
+      {
+	print_word(ctx, "  type = ");
+	print_type(ctx, type._M_type, "<unknown type>");
+	print_word(ctx, ";\n");
+      }
+  }
 
-	if (_M_variant._M_sequence._M_type)
-	  __formatter->_M_print_word(";\n");
+  void
+  print_description(PrintContext& ctx, const _Parameter::_Instance& inst)
+  {
+    const int bufsize = 64;
+    char buf[bufsize];
 
-	__formatter->_M_print_word("}\n");
-	break;
-      case __instance:
-	__formatter->_M_print_word("instance ");
-	print_description(__formatter, _M_variant._M_instance);
+    if (inst._M_name)
+      {
+	int written
+	  = format_word(buf, bufsize, "\"%s\" ", inst._M_name);
+	print_word(ctx, buf, written);
+      }
 
-	if (_M_variant._M_instance._M_type)
-	  __formatter->_M_print_word(";\n");
+    int written
+      = __builtin_sprintf(buf, "@ 0x%p {\n", inst._M_address);
+    print_word(ctx, buf, written);
 
-	__formatter->_M_print_word("}\n");
-	break;
-      case __iterator_value_type:
-	__formatter->_M_print_word("iterator::value_type ");
-	print_description(__formatter, _M_variant._M_iterator_value_type);
-	__formatter->_M_print_word("}\n");
-	break;
-      default:
-	break;
+    if (inst._M_type)
+      {
+	print_word(ctx, "  type = ");
+	print_type(ctx, inst._M_type, "<unknown type>");
       }
   }
 
-  const _Error_formatter&
-  _Error_formatter::_M_message(_Debug_msg_id __id) const throw ()
-  { return this->_M_message(_S_debug_messages[__id]); }
-
   void
-  _Error_formatter::_M_error() const
+  print_description(PrintContext& ctx, const _Parameter& param)
   {
-    const int __bufsize = 128;
-    char __buf[__bufsize];
+    const int bufsize = 128;
+    char buf[bufsize];
 
-    // Emit file & line number information
-    _M_column = 1;
-    _M_wordwrap = false;
-    if (_M_file)
+    const auto& variant = param._M_variant;
+    switch (param._M_kind)
       {
-	_M_format_word(__buf, __bufsize, "%s:", _M_file);
-	_M_print_word(__buf);
-	_M_column += strlen(__buf);
-      }
+      case _Parameter::__iterator:
+	{
+	  const auto& ite = variant._M_iterator;
 
-    if (_M_line > 0)
-      {
-	_M_format_word(__buf, __bufsize, "%u:", _M_line);
-	_M_print_word(__buf);
-	_M_column += strlen(__buf);
-      }
+	  print_word(ctx, "iterator ");
+	  print_description(ctx, ite);
 
-    if (_M_max_length)
-      _M_wordwrap = true;
-    _M_print_word("error: ");
+	  if (ite._M_type)
+	    {
+	      if (ite._M_constness != _Error_formatter::__unknown_constness)
+		{
+		  print_word(ctx, " (");
+		  print_field(ctx, param, "constness");
+		  print_word(ctx, " iterator)");
+		}
 
-    // Print the error message
-    assert(_M_text);
-    _M_print_string(_M_text);
-    _M_print_word(".\n");
+	      print_word(ctx, ";\n");
+	    }
 
-    // Emit descriptions of the objects involved in the operation
-    _M_wordwrap = false;
-    bool __has_noninteger_parameters = false;
-    for (unsigned int __i = 0; __i < _M_num_parameters; ++__i)
-      {
-	switch (_M_parameters[__i]._M_kind)
-	  {
-	  case _Parameter::__iterator:
-	  case _Parameter::__sequence:
-	  case _Parameter::__instance:
-	  case _Parameter::__iterator_value_type:
-	    if (!__has_noninteger_parameters)
-	      {
-		_M_first_line = true;
-		_M_print_word("\nObjects involved in the operation:\n");
-		__has_noninteger_parameters = true;
-	      }
-	    _M_parameters[__i]._M_print_description(this);
-	    break;
-	  default:
-	    break;
-	  }
-      }
+	  if (ite._M_state != _Error_formatter::__unknown_state)
+	    {
+	      print_word(ctx, "  state = ");
+	      print_field(ctx, param, "state");
+	      print_word(ctx, ";\n");
+	    }
 
-    abort();
-  }
+	  if (ite._M_sequence)
+	    {
+	      print_word(ctx, "  references sequence ");
+	      if (ite._M_seq_type)
+		{
+		  print_word(ctx, "with type '");
+		  print_field(ctx, param, "seq_type");
+		  print_word(ctx, "' ");
+		}
+
+	      int written
+		= __builtin_sprintf(buf, "@ 0x%p\n", ite._M_sequence);
+	      print_word(ctx, buf, written);
+	    }
+
+	  print_word(ctx, "}\n", 2);
+	}
+	break;
 
-  template<typename _Tp>
-    void
-    _Error_formatter::_M_format_word(char* __buf,
-				     int __n __attribute__ ((__unused__)),
-				     const char* __fmt, _Tp __s) const throw ()
-    {
-#ifdef _GLIBCXX_USE_C99
-      std::snprintf(__buf, __n, __fmt, __s);
-#else
-      std::sprintf(__buf, __fmt, __s);
-#endif
-    }
+      case _Parameter::__sequence:
+	print_word(ctx, "sequence ");
+	print_description(ctx, variant._M_sequence);
 
-  void
-  _Error_formatter::_M_print_word(const char* __word) const
-  {
-    if (!_M_wordwrap)
-      {
-	fprintf(stderr, "%s", __word);
-	return;
-      }
+	if (variant._M_sequence._M_type)
+	  print_word(ctx, ";\n", 2);
 
-    size_t __length = strlen(__word);
-    if (__length == 0)
-      return;
+	print_word(ctx, "}\n", 2);
+	break;
 
-    size_t __visual_length
-      = __word[__length - 1] == '\n' ? __length - 1 : __length;
-    if (__visual_length == 0
-	|| (_M_column + __visual_length < _M_max_length)
-	|| (__visual_length >= _M_max_length && _M_column == 1))
-      {
-	// If this isn't the first line, indent
-	if (_M_column == 1 && !_M_first_line)
-	  {
-	    char __spacing[_M_indent + 1];
-	    for (int i = 0; i < _M_indent; ++i)
-	      __spacing[i] = ' ';
-	    __spacing[_M_indent] = '\0';
-	    fprintf(stderr, "%s", __spacing);
-	    _M_column += _M_indent;
-	  }
+      case _Parameter::__instance:
+	print_word(ctx, "instance ");
+	print_description(ctx, variant._M_instance);
 
-	fprintf(stderr, "%s", __word);
+	if (variant._M_instance._M_type)
+	  print_word(ctx, ";\n", 2);
 
-	if (__word[__length - 1] == '\n')
-	  {
-	    _M_first_line = false;
-	    _M_column = 1;
-	  }
-	else
-	  _M_column += __length;
-      }
-    else
-      {
-	_M_print_word("\n");
-	_M_print_word(__word);
+	print_word(ctx, "}\n", 2);
+	break;
+
+      case _Parameter::__iterator_value_type:
+	print_word(ctx, "iterator::value_type ");
+	print_description(ctx, variant._M_iterator_value_type);
+	print_word(ctx, "}\n", 2);
+	break;
+
+      default:
+	break;
       }
   }
 
   void
-  _Error_formatter::
-  _M_print_string(const char* __string) const
+  print_string(PrintContext& ctx, const char* string,
+	       const _Parameter* parameters, std::size_t num_parameters)
   {
-    const char* __start = __string;
-    const char* __finish = __start;
-    const int __bufsize = 128;
-    char __buf[__bufsize];
+    const char* start = string;
+    const int bufsize = 128;
+    char buf[bufsize];
+    int bufindex = 0;
 
-    while (*__start)
+    while (*start)
       {
-	if (*__start != '%')
+	if (isspace(*start))
 	  {
-	    // [__start, __finish) denotes the next word
-	    __finish = __start;
-	    while (isalnum(*__finish))
-	      ++__finish;
-	    if (__start == __finish)
-	      ++__finish;
-	    if (isspace(*__finish))
-	      ++__finish;
-
-	    const ptrdiff_t __len = __finish - __start;
-	    assert(__len < __bufsize);
-	    memcpy(__buf, __start, __len);
-	    __buf[__len] = '\0';
-	    _M_print_word(__buf);
-	    __start = __finish;
-
-	    // Skip extra whitespace
-	    while (*__start == ' ')
-	      ++__start;
+	    buf[bufindex++] = *start++;
+	    buf[bufindex] = '\0';
+	    print_word(ctx, buf, bufindex);
+	    bufindex = 0;
+	    continue;
+	  }
 
+	if (*start != '%')
+	  {
+	    // Normal char.
+	    buf[bufindex++] = *start++;
 	    continue;
 	  }
 
-	++__start;
-	assert(*__start);
-	if (*__start == '%')
+	if (*++start == '%')
 	  {
-	    _M_print_word("%");
-	    ++__start;
+	    // Escaped '%'
+	    buf[bufindex++] = *start++;
 	    continue;
 	  }
 
+	// We are on a parameter property reference, we need to flush buffer
+	// first.
+	if (bufindex != 0)
+	  {
+	    buf[bufindex] = '\0';
+	    print_word(ctx, buf, bufindex);
+	    bufindex = 0;
+	  }
+
 	// Get the parameter number
-	assert(*__start >= '1' && *__start <= '9');
-	size_t __param_index = *__start - '0' - 1;
-	assert(__param_index < _M_num_parameters);
-	const auto& __param = _M_parameters[__param_index];
+	assert(*start >= '1' && *start <= '9');
+	size_t param_index = *start - '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 != '.')
+	++start;
+	if (*start != '.')
 	  {
-	    assert(*__start == ';');
-	    ++__start;
-	    __buf[0] = '\0';
-	    if (__param._M_kind == _Parameter::__integer)
+	    assert(*start == ';');
+	    ++start;
+	    if (param._M_kind == _Parameter::__integer)
 	      {
-		_M_format_word(__buf, __bufsize, "%ld",
-			       __param._M_variant._M_integer._M_value);
-		_M_print_word(__buf);
+		int written
+		  = __builtin_sprintf(buf, "%ld",
+				      param._M_variant._M_integer._M_value);
+		print_word(ctx, buf, written);
 	      }
-	    else if (__param._M_kind == _Parameter::__string)
-	      _M_print_string(__param._M_variant._M_string._M_value);
+	    else if (param._M_kind == _Parameter::__string)
+	      print_string(ctx, param._M_variant._M_string._M_value,
+			   parameters, num_parameters);
 	    continue;
 	  }
 
 	// Extract the field name we want
-	enum { __max_field_len = 16 };
-	char __field[__max_field_len];
-	int __field_idx = 0;
-	++__start;
-	while (*__start != ';')
+	const int max_field_len = 16;
+	char field[max_field_len];
+	int field_idx = 0;
+	++start;
+	while (*start != ';')
 	  {
-	    assert(*__start);
-	    assert(__field_idx < __max_field_len-1);
-	    __field[__field_idx++] = *__start++;
+	    assert(*start);
+	    assert(field_idx < max_field_len - 1);
+	    field[field_idx++] = *start++;
 	  }
-	++__start;
-	__field[__field_idx] = 0;
+	++start;
+	field[field_idx] = '\0';
 
-	__param._M_print_field(this, __field);
+	print_field(ctx, param, field);
+      }
+
+    // Might need to flush.
+    if (bufindex)
+      {
+	buf[bufindex] = '\0';
+	print_word(ctx, buf, bufindex);
       }
   }
+}
+
+namespace __gnu_debug
+{
+  _Error_formatter&
+  _Error_formatter::_M_message(_Debug_msg_id __id) throw ()
+  { return this->_M_message(_S_debug_messages[__id]); }
 
   void
-  _Error_formatter::_M_get_max_length() const throw ()
+  _Error_formatter::_M_error() const
   {
-    const char* __nptr = std::getenv("GLIBCXX_DEBUG_MESSAGE_LENGTH");
-    if (__nptr)
+    const int bufsize = 128;
+    char buf[bufsize];
+
+    // Emit file & line number information
+    bool go_to_next_line = false;
+    PrintContext ctx;
+    if (_M_file)
       {
-	char* __endptr;
-	const unsigned long __ret = std::strtoul(__nptr, &__endptr, 0);
-	if (*__nptr != '\0' && *__endptr == '\0')
-	  _M_max_length = __ret;
+	int written = format_word(buf, bufsize, "%s:", _M_file);
+	print_word(ctx, buf, written);
+	go_to_next_line = true;
       }
-  }
 
-  // Instantiations.
-  template
-    void
-    _Error_formatter::_M_format_word(char*, int, const char*,
-				     const void*) const;
-
-  template
-    void
-    _Error_formatter::_M_format_word(char*, int, const char*, long) const;
-
-  template
-    void
-    _Error_formatter::_M_format_word(char*, int, const char*,
-				     std::size_t) const;
-
-  template
-    void
-    _Error_formatter::_M_format_word(char*, int, const char*,
-				     const char*) const;
+    if (_M_line > 0)
+      {
+	int written = __builtin_sprintf(buf, "%u:", _M_line);
+	print_word(ctx, buf, written);
+	go_to_next_line = true;
+      }
+
+    if (go_to_next_line)
+      print_word(ctx, "\n", 1);
+
+    if (ctx._M_max_length)
+      ctx._M_wordwrap = true;
+
+    print_word(ctx, "Error: ");
+
+    // Print the error message
+    assert(_M_text);
+    print_string(ctx, _M_text, _M_parameters, _M_num_parameters);
+    print_word(ctx, ".\n", 2);
+
+    // Emit descriptions of the objects involved in the operation
+    ctx._M_first_line = true;
+    ctx._M_wordwrap = false;
+    bool has_header = false;
+    for (unsigned int i = 0; i < _M_num_parameters; ++i)
+      {
+	switch (_M_parameters[i]._M_kind)
+	  {
+	  case _Parameter::__iterator:
+	  case _Parameter::__sequence:
+	  case _Parameter::__instance:
+	  case _Parameter::__iterator_value_type:
+	    if (!has_header)
+	      {
+		print_word(ctx, "\nObjects involved in the operation:\n");
+		has_header = true;
+	      }
+	    print_description(ctx, _M_parameters[i]);
+	    break;
+
+	  default:
+	    break;
+	  }
+      }
+
+    abort();
+  }
 } // namespace __gnu_debug