diff mbox

debug mode symbols cleanup

Message ID 55EF495E.1090003@gmail.com
State New
Headers show

Commit Message

François Dumont Sept. 8, 2015, 8:47 p.m. UTC
On 07/09/2015 13:03, Jonathan Wakely wrote:
> 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
>
>
Sorry, I though policy regarding debug mode symbols was even more relax.
It is not so here is another patch that doesn"t break abi checks.

I eventually made all methods that should not be used deprecated, they
were normally not used explicitely anyway. Their implementation is now
empty. I just needed to add a symbol for the not const _M_message method
which is the correct signature.

François
diff mbox

Patch

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 8f9f99a..ac9a66b 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -1879,6 +1879,8 @@  GLIBCXX_3.4.22 {
 
     _ZNSt6vectorIPSt12Catalog_info*;
 
+    _ZN11__gnu_debug16_Error_formatter10_M_message*;
+
 } GLIBCXX_3.4.21;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index f0ac694..1826e94 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
     {
@@ -376,15 +376,16 @@  namespace __gnu_debug
 
       void
       _M_print_field(const _Error_formatter* __formatter,
-		     const char* __name) const;
+		     const char* __name) const _GLIBCXX_DEPRECATED;
 
       void
-      _M_print_description(const _Error_formatter* __formatter) const;
+      _M_print_description(const _Error_formatter* __formatter)
+	const _GLIBCXX_DEPRECATED;
     };
 
     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 +394,99 @@  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; }
 
+    _Error_formatter&
+    _M_message(_Debug_msg_id __id) throw ();
+
     const _Error_formatter&
-    _M_message(_Debug_msg_id __id) const throw ();
+    _M_message(_Debug_msg_id __id) const throw () _GLIBCXX_DEPRECATED;
 
     _GLIBCXX_NORETURN void
     _M_error() const;
 
     template<typename _Tp>
       void
-      _M_format_word(char*, int, const char*, _Tp) const throw ();
+      _M_format_word(char*, int, const char*, _Tp)
+      const throw () _GLIBCXX_DEPRECATED;
 
     void
-    _M_print_word(const char* __word) const;
+    _M_print_word(const char* __word) const _GLIBCXX_DEPRECATED;
 
     void
-    _M_print_string(const char* __string) const;
+    _M_print_string(const char* __string) const _GLIBCXX_DEPRECATED;
 
   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(); }
+    _Error_formatter(const char* __file, unsigned int __line)
+    : _M_file(__file), _M_line(__line), _M_num_parameters(0), _M_text(0)
+    { }
 
     void
-    _M_get_max_length() const throw ();
+    _M_get_max_length() const throw () _GLIBCXX_DEPRECATED;
 
     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..0a2f3d4 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,136 +764,293 @@  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];
+    if (type._M_name)
+      {
+	const int bufsize = 64;
+	char buf[bufsize];
+	int written
+	  = format_word(buf, bufsize, "\"%s\"", type._M_name);
+	print_word(ctx, buf, written);
+      }
+
+    print_word(ctx, " {\n");
 
-    switch (_M_kind)
+    if (type._M_type)
       {
-      case __iterator:
-	__formatter->_M_print_word("iterator ");
-	print_description(__formatter, _M_variant._M_iterator);
+	print_word(ctx, "  type = ");
+	print_type(ctx, type._M_type, "<unknown type>");
+	print_word(ctx, ";\n");
+      }
+  }
 
-	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");
-	  }
+  void
+  print_description(PrintContext& ctx, const _Parameter::_Instance& inst)
+  {
+    const int bufsize = 64;
+    char buf[bufsize];
 
-	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 (inst._M_name)
+      {
+	int written
+	  = format_word(buf, bufsize, "\"%s\" ", inst._M_name);
+	print_word(ctx, buf, written);
+      }
 
-	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("' ");
-	      }
+    int written
+      = __builtin_sprintf(buf, "@ 0x%p {\n", inst._M_address);
+    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);
-	  }
+    if (inst._M_type)
+      {
+	print_word(ctx, "  type = ");
+	print_type(ctx, inst._M_type, "<unknown type>");
+      }
+  }
+
+  void
+  print_description(PrintContext& ctx, const _Parameter& param)
+  {
+    const int bufsize = 128;
+    char buf[bufsize];
+
+    const auto& variant = param._M_variant;
+    switch (param._M_kind)
+      {
+      case _Parameter::__iterator:
+	{
+	  const auto& ite = variant._M_iterator;
+
+	  print_word(ctx, "iterator ");
+	  print_description(ctx, ite);
 
-	__formatter->_M_print_word("}\n");
+	  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_word(ctx, ";\n");
+	    }
+
+	  if (ite._M_state != _Error_formatter::__unknown_state)
+	    {
+	      print_word(ctx, "  state = ");
+	      print_field(ctx, param, "state");
+	      print_word(ctx, ";\n");
+	    }
+
+	  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;
-      case __sequence:
-	__formatter->_M_print_word("sequence ");
-	print_description(__formatter, _M_variant._M_sequence);
 
-	if (_M_variant._M_sequence._M_type)
-	  __formatter->_M_print_word(";\n");
+      case _Parameter::__sequence:
+	print_word(ctx, "sequence ");
+	print_description(ctx, variant._M_sequence);
+
+	if (variant._M_sequence._M_type)
+	  print_word(ctx, ";\n", 2);
 
-	__formatter->_M_print_word("}\n");
+	print_word(ctx, "}\n", 2);
 	break;
-      case __instance:
-	__formatter->_M_print_word("instance ");
-	print_description(__formatter, _M_variant._M_instance);
 
-	if (_M_variant._M_instance._M_type)
-	  __formatter->_M_print_word(";\n");
+      case _Parameter::__instance:
+	print_word(ctx, "instance ");
+	print_description(ctx, variant._M_instance);
 
-	__formatter->_M_print_word("}\n");
+	if (variant._M_instance._M_type)
+	  print_word(ctx, ";\n", 2);
+
+	print_word(ctx, "}\n", 2);
 	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");
+
+      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;
       }
   }
 
-  const _Error_formatter&
-  _Error_formatter::_M_message(_Debug_msg_id __id) const throw ()
+  void
+  print_string(PrintContext& ctx, const char* string,
+	       const _Parameter* parameters, std::size_t num_parameters)
+  {
+    const char* start = string;
+    const int bufsize = 128;
+    char buf[bufsize];
+    int bufindex = 0;
+
+    while (*start)
+      {
+	if (isspace(*start))
+	  {
+	    buf[bufindex++] = *start++;
+	    buf[bufindex] = '\0';
+	    print_word(ctx, buf, bufindex);
+	    bufindex = 0;
+	    continue;
+	  }
+
+	if (*start != '%')
+	  {
+	    // Normal char.
+	    buf[bufindex++] = *start++;
+	    continue;
+	  }
+
+	if (*++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 < num_parameters);
+	const auto& param = parameters[param_index];
+
+	// '.' separates the parameter number from the field
+	// name, if there is one.
+	++start;
+	if (*start != '.')
+	  {
+	    assert(*start == ';');
+	    ++start;
+	    if (param._M_kind == _Parameter::__integer)
+	      {
+		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)
+	      print_string(ctx, param._M_variant._M_string._M_value,
+			   parameters, num_parameters);
+	    continue;
+	  }
+
+	// Extract the field name we want
+	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++;
+	  }
+	++start;
+	field[field_idx] = '\0';
+
+	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_error() const
   {
-    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;
+    bool go_to_next_line = false;
+    PrintContext ctx;
     if (_M_file)
       {
-	_M_format_word(__buf, __bufsize, "%s:", _M_file);
-	_M_print_word(__buf);
-	_M_column += strlen(__buf);
+	int written = format_word(buf, bufsize, "%s:", _M_file);
+	print_word(ctx, buf, written);
+	go_to_next_line = true;
       }
 
     if (_M_line > 0)
       {
-	_M_format_word(__buf, __bufsize, "%u:", _M_line);
-	_M_print_word(__buf);
-	_M_column += strlen(__buf);
+	int written = __builtin_sprintf(buf, "%u:", _M_line);
+	print_word(ctx, buf, written);
+	go_to_next_line = true;
       }
 
-    if (_M_max_length)
-      _M_wordwrap = true;
-    _M_print_word("error: ");
+    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);
-    _M_print_string(_M_text);
-    _M_print_word(".\n");
+    print_string(ctx, _M_text, _M_parameters, _M_num_parameters);
+    print_word(ctx, ".\n", 2);
 
     // 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)
+    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)
+	switch (_M_parameters[i]._M_kind)
 	  {
 	  case _Parameter::__iterator:
 	  case _Parameter::__sequence:
 	  case _Parameter::__instance:
 	  case _Parameter::__iterator_value_type:
-	    if (!__has_noninteger_parameters)
+	    if (!has_header)
 	      {
-		_M_first_line = true;
-		_M_print_word("\nObjects involved in the operation:\n");
-		__has_noninteger_parameters = true;
+		print_word(ctx, "\nObjects involved in the operation:\n");
+		has_header = true;
 	      }
-	    _M_parameters[__i]._M_print_description(this);
+	    print_description(ctx, _M_parameters[i]);
 	    break;
+
 	  default:
 	    break;
 	  }
@@ -867,172 +1059,43 @@  namespace __gnu_debug
     abort();
   }
 
-  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
-    }
-
+  // Deprecated methods kept for backward compatibility.
   void
-  _Error_formatter::_M_print_word(const char* __word) const
-  {
-    if (!_M_wordwrap)
-      {
-	fprintf(stderr, "%s", __word);
-	return;
-      }
-
-    size_t __length = strlen(__word);
-    if (__length == 0)
-      return;
-
-    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;
-	  }
-
-	fprintf(stderr, "%s", __word);
-
-	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);
-      }
-  }
+  _Error_formatter::_Parameter::_M_print_field(
+	const _Error_formatter*, const char*) const
+  { }
 
   void
-  _Error_formatter::
-  _M_print_string(const char* __string) const
-  {
-    const char* __start = __string;
-    const char* __finish = __start;
-    const int __bufsize = 128;
-    char __buf[__bufsize];
+  _Error_formatter::_Parameter::_M_print_description(const _Error_formatter*) const
+  { }
 
-    while (*__start)
-      {
-	if (*__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;
-
-	    continue;
-	  }
-
-	++__start;
-	assert(*__start);
-	if (*__start == '%')
-	  {
-	    _M_print_word("%");
-	    ++__start;
-	    continue;
-	  }
-
-	// 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];
+  const _Error_formatter&
+  _Error_formatter::_M_message(_Debug_msg_id) const throw ()
+  { return *this; }
 
-	// '.' separates the parameter number from the field
-	// name, if there is one.
-	++__start;
-	if (*__start != '.')
-	  {
-	    assert(*__start == ';');
-	    ++__start;
-	    __buf[0] = '\0';
-	    if (__param._M_kind == _Parameter::__integer)
-	      {
-		_M_format_word(__buf, __bufsize, "%ld",
-			       __param._M_variant._M_integer._M_value);
-		_M_print_word(__buf);
-	      }
-	    else if (__param._M_kind == _Parameter::__string)
-	      _M_print_string(__param._M_variant._M_string._M_value);
-	    continue;
-	  }
+  template<typename _Tp>
+    void
+    _Error_formatter::_M_format_word(char*, int, const char*, _Tp)
+    const throw ()
+    { }
 
-	// Extract the field name we want
-	enum { __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++;
-	  }
-	++__start;
-	__field[__field_idx] = 0;
+  void
+  _Error_formatter::_M_print_word(const char*) const
+  { }
 
-	__param._M_print_field(this, __field);
-      }
-  }
+  void
+  _Error_formatter::_M_print_string(const char*) const
+  { }
 
   void
   _Error_formatter::_M_get_max_length() const throw ()
-  {
-    const char* __nptr = std::getenv("GLIBCXX_DEBUG_MESSAGE_LENGTH");
-    if (__nptr)
-      {
-	char* __endptr;
-	const unsigned long __ret = std::strtoul(__nptr, &__endptr, 0);
-	if (*__nptr != '\0' && *__endptr == '\0')
-	  _M_max_length = __ret;
-      }
-  }
+  { }
 
   // Instantiations.
   template
     void
     _Error_formatter::_M_format_word(char*, int, const char*,
-				     const void*) const;
+                                    const void*) const;
 
   template
     void
@@ -1041,10 +1104,10 @@  namespace __gnu_debug
   template
     void
     _Error_formatter::_M_format_word(char*, int, const char*,
-				     std::size_t) const;
+                                    std::size_t) const;
 
   template
     void
     _Error_formatter::_M_format_word(char*, int, const char*,
-				     const char*) const;
+                                    const char*) const;
 } // namespace __gnu_debug