When XML mode is selected messages from VALGRIND_PRINTF and friends
should go to the XML stream not stderr.  Fixes BZ 322807.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13516 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 76939f8..d5770a5 100644
--- a/NEWS
+++ b/NEWS
@@ -443,6 +443,9 @@
 321960  pthread_create() then alloca() causing invalid stack write errors
         FIXED 13467
 
+322807  VALGRIND_PRINTF_BACKTRACE writes callstack to xml and text to stderr
+        FIXED 13516
+
 322851  0bXXX binary literal syntax is not standard 
         FIXED 2736
 
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index eb3351e..b20ab91 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -1654,6 +1654,76 @@
 }
 
 
+/* Write out a client message, possibly including a back trace. Return
+   the number of characters written. In case of XML output, the format
+   string as well as any arguments it requires will be XML'ified. 
+   I.e. special characters such as the angle brackets will be translated
+   into proper escape sequences. */
+static
+Int print_client_message( ThreadId tid, const HChar *format,
+                          va_list *vargsp, Bool include_backtrace)
+{
+   Int count;
+
+   if (VG_(clo_xml)) {
+      /* Translate the format string as follows:
+         <  -->  &lt;
+         >  -->  &gt;
+         &  -->  &amp;
+         %s -->  %pS
+         Yes, yes, it's simplified but in synch with 
+         myvprintf_str_XML_simplistic and VG_(debugLog_vprintf).
+      */
+
+      /* Allocate a buffer that is for sure large enough. */
+      HChar xml_format[VG_(strlen)(format) * 5 + 1];
+
+      const HChar *p;
+      HChar *q = xml_format;
+
+      for (p = format; *p; ++p) {
+         switch (*p) {
+         case '<': VG_(strcpy)(q, "&lt;");  q += 4; break;
+         case '>': VG_(strcpy)(q, "&gt;");  q += 4; break;
+         case '&': VG_(strcpy)(q, "&amp;"); q += 5; break;
+         case '%':
+            /* Careful: make sure %%s stays %%s */
+            *q++ = *p++;
+            if (*p == 's') {
+              *q++ = 'p';
+              *q++ = 'S';
+            } else {
+              *q++ = *p;
+            }
+            break;
+
+         default:
+            *q++ = *p;
+            break;
+         }
+      }
+      *q = '\0';
+
+      VG_(printf_xml)( "<clientmsg>\n" );
+      VG_(printf_xml)( "  <tid>%d</tid>\n", tid );
+      VG_(printf_xml)( "  <text>" );
+      count = VG_(vprintf_xml)( xml_format, *vargsp );
+      VG_(printf_xml)( "  </text>\n" );
+   } else {
+      count = VG_(vmessage)( Vg_ClientMsg, format, *vargsp );
+      VG_(message_flush)();
+   }
+
+   if (include_backtrace)
+      VG_(get_and_pp_StackTrace)( tid, VG_(clo_backtrace_size) );
+   
+   if (VG_(clo_xml))
+      VG_(printf_xml)( "</clientmsg>\n" );
+
+   return count;
+}
+
+
 /* Do a client request for the thread tid.  After the request, tid may
    or may not still be runnable; if not, the scheduler will have to
    choose a new thread to run.  
@@ -1708,6 +1778,7 @@
          break;
 
       case VG_USERREQ__PRINTF: {
+         const HChar* format = (HChar *)arg[1];
          /* JRS 2010-Jan-28: this is DEPRECATED; use the
             _VALIST_BY_REF version instead */
          if (sizeof(va_list) != sizeof(UWord))
@@ -1718,13 +1789,14 @@
          } u;
          u.uw = (unsigned long)arg[2];
          Int count = 
-            VG_(vmessage)( Vg_ClientMsg, (HChar *)arg[1], u.vargs );
-         VG_(message_flush)();
+            print_client_message( tid, format, &u.vargs,
+                                  /* include_backtrace */ False );
          SET_CLREQ_RETVAL( tid, count );
          break;
       }
 
       case VG_USERREQ__PRINTF_BACKTRACE: {
+         const HChar* format = (HChar *)arg[1];
          /* JRS 2010-Jan-28: this is DEPRECATED; use the
             _VALIST_BY_REF version instead */
          if (sizeof(va_list) != sizeof(UWord))
@@ -1735,28 +1807,29 @@
          } u;
          u.uw = (unsigned long)arg[2];
          Int count =
-            VG_(vmessage)( Vg_ClientMsg, (HChar *)arg[1], u.vargs );
-         VG_(message_flush)();
-         VG_(get_and_pp_StackTrace)( tid, VG_(clo_backtrace_size) );
+            print_client_message( tid, format, &u.vargs,
+                                  /* include_backtrace */ True );
          SET_CLREQ_RETVAL( tid, count );
          break;
       }
 
       case VG_USERREQ__PRINTF_VALIST_BY_REF: {
+         const HChar* format = (HChar *)arg[1];
          va_list* vargsp = (va_list*)arg[2];
-         Int count = 
-            VG_(vmessage)( Vg_ClientMsg, (HChar *)arg[1], *vargsp );
-         VG_(message_flush)();
+         Int count =
+            print_client_message( tid, format, vargsp,
+                                  /* include_backtrace */ False );
+
          SET_CLREQ_RETVAL( tid, count );
          break;
       }
 
       case VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF: {
+         const HChar* format = (HChar *)arg[1];
          va_list* vargsp = (va_list*)arg[2];
          Int count =
-            VG_(vmessage)( Vg_ClientMsg, (HChar *)arg[1], *vargsp );
-         VG_(message_flush)();
-         VG_(get_and_pp_StackTrace)( tid, VG_(clo_backtrace_size) );
+            print_client_message( tid, format, vargsp,
+                                  /* include_backtrace */ True );
          SET_CLREQ_RETVAL( tid, count );
          break;
       }
diff --git a/docs/internals/xml-output-protocol4.txt b/docs/internals/xml-output-protocol4.txt
index 0dec54f..861d8ca 100644
--- a/docs/internals/xml-output-protocol4.txt
+++ b/docs/internals/xml-output-protocol4.txt
@@ -190,7 +190,7 @@
   human-understandable.  In current Valgrind versions it is the
   elapsed wallclock time since process start.
 
-* Zero or more of (either ERRORCOUNTS or TOOLSPECIFIC).
+* Zero or more of (either ERRORCOUNTS, TOOLSPECIFIC, or CLIENTMSG).
 
 * The following, indicating that the program has now finished, and
   that the any final wrapup (eg, for Memcheck, leak checking) is happening.
@@ -699,3 +699,36 @@
       One of various miscellaneous noteworthy conditions was observed
       (eg, thread exited whilst holding locks, "impossible" behaviour
       from the underlying threading library, etc)
+
+====================================================================
+
+CLIENTMSG
+
+CLIENTMSG defines a message that was caused by one of the following
+client requests:
+
+- VALGRIND_PRINTF
+- VALGRIND_PRINTF_BACKTRACE
+
+Definition:
+
+<clientmsg>
+  <tid>INT</tid>
+  <text>...</text>
+</clientmsg>
+
+OR
+
+<clientmsg>
+  <tid>INT</tid>
+  <text>...</text>
+  STACK
+</clientmsg>
+
+* The <tid> tag indicates the Valgrind thread number.
+
+* The <text> tag indicates the message as specified in the client request
+  (properly translated to XML).
+
+* STACK is only present in case of VALGRIND_PRINTF_BACKTRACE. See above
+  for a definition of STACK.
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index e363df4..2938133 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -74,6 +74,8 @@
 		buflen_check.stderr.exp-kfail \
 	bug287260.stderr.exp bug287260.vgtest \
 	calloc-overflow.stderr.exp calloc-overflow.vgtest\
+	client-msg.stderr.exp client-msg.vgtest \
+	client-msg-as-xml.stderr.exp client-msg-as-xml.vgtest \
 	clientperm.stderr.exp \
 	clientperm.stdout.exp clientperm.vgtest \
 	clireq_nofill.stderr.exp \
@@ -273,6 +275,7 @@
 	buflen_check \
 	bug287260 \
 	calloc-overflow \
+	client-msg \
 	clientperm \
 	clireq_nofill \
 	clo_redzone \
diff --git a/memcheck/tests/client-msg-as-xml.stderr.exp b/memcheck/tests/client-msg-as-xml.stderr.exp
new file mode 100644
index 0000000..af97c61
--- /dev/null
+++ b/memcheck/tests/client-msg-as-xml.stderr.exp
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+
+<valgrindoutput>
+
+<protocolversion>4</protocolversion>
+<protocoltool>memcheck</protocoltool>
+
+<preamble>
+  <line>...</line>
+  <line>...</line>
+  <line>...</line>
+  <line>...</line>
+</preamble>
+
+<pid>...</pid>
+<ppid>...</ppid>
+<tool>memcheck</tool>
+
+<args>
+  <vargv>...</vargv>
+  <argv>
+    <exe>./client-msg</exe>
+  </argv>
+</args>
+
+<status>
+  <state>RUNNING</state>
+  <time>...</time>
+</status>
+
+<clientmsg>
+  <tid>...</tid>
+  <text>hello &lt;&gt; &lt;&amp;&gt;%s world
+  </text>
+  <stack>
+    <frame>
+      <ip>0x........</ip>
+      <obj>...</obj>
+      <fn>VALGRIND_PRINTF_BACKTRACE</fn>
+      <dir>...</dir>
+      <file>valgrind.h</file>
+      <line>...</line>
+    </frame>
+    <frame>
+      <ip>0x........</ip>
+      <obj>...</obj>
+      <fn>baz</fn>
+      <dir>...</dir>
+      <file>client-msg.c</file>
+      <line>...</line>
+    </frame>
+    <frame>
+      <ip>0x........</ip>
+      <obj>...</obj>
+      <fn>bar</fn>
+      <dir>...</dir>
+      <file>client-msg.c</file>
+      <line>...</line>
+    </frame>
+    <frame>
+      <ip>0x........</ip>
+      <obj>...</obj>
+      <fn>foo</fn>
+      <dir>...</dir>
+      <file>client-msg.c</file>
+      <line>...</line>
+    </frame>
+    <frame>
+      <ip>0x........</ip>
+      <obj>...</obj>
+      <fn>main</fn>
+      <dir>...</dir>
+      <file>client-msg.c</file>
+      <line>...</line>
+    </frame>
+  </stack>
+</clientmsg>
+
+<status>
+  <state>FINISHED</state>
+  <time>...</time>
+</status>
+
+<errorcounts>
+</errorcounts>
+
+<suppcounts>...</suppcounts>
+
+</valgrindoutput>
+
diff --git a/memcheck/tests/client-msg-as-xml.vgtest b/memcheck/tests/client-msg-as-xml.vgtest
new file mode 100644
index 0000000..b3e8121
--- /dev/null
+++ b/memcheck/tests/client-msg-as-xml.vgtest
@@ -0,0 +1,3 @@
+prog: client-msg
+vgopts: --xml=yes --xml-fd=2 --log-file=/dev/null
+stderr_filter: filter_xml
diff --git a/memcheck/tests/client-msg.c b/memcheck/tests/client-msg.c
new file mode 100644
index 0000000..cdcf43d
--- /dev/null
+++ b/memcheck/tests/client-msg.c
@@ -0,0 +1,24 @@
+#include "valgrind.h"
+
+void baz()
+{
+   VALGRIND_PRINTF_BACKTRACE("hello <> %s%%s world\n","<&>");
+}
+
+void bar()
+{
+   baz();
+}
+
+void foo()
+{
+     bar();
+}
+
+int main()
+{
+   foo();
+
+   return 0;
+}
+
diff --git a/memcheck/tests/client-msg.stderr.exp b/memcheck/tests/client-msg.stderr.exp
new file mode 100644
index 0000000..075d321
--- /dev/null
+++ b/memcheck/tests/client-msg.stderr.exp
@@ -0,0 +1,6 @@
+hello <> <&>%s world
+   ...
+   by 0x........: baz (client-msg.c:5)
+   by 0x........: bar (client-msg.c:10)
+   by 0x........: foo (client-msg.c:15)
+   by 0x........: main (client-msg.c:20)
diff --git a/memcheck/tests/client-msg.vgtest b/memcheck/tests/client-msg.vgtest
new file mode 100644
index 0000000..ace0364
--- /dev/null
+++ b/memcheck/tests/client-msg.vgtest
@@ -0,0 +1,2 @@
+prog: client-msg
+vgopts: -q