First pass at adding ability for Memcheck to print all output in XML
form.  The relevant flag is --xml=yes.  Currently this only works with
Memcheck.

Specifying this flag fixes various other options relating to verbosity
and behaviour of the leak checker, so that the resulting output is in
a relatively fixed form suitable for parsing by GUIs.

Still to do:

* Add mechanism to show error counts
* Add regression test
* Document the resulting format



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3773 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c
index df05c20..79b2d15 100644
--- a/coregrind/m_errormgr.c
+++ b/coregrind/m_errormgr.c
@@ -251,11 +251,18 @@
 
 static void pp_Error ( Error* err, Bool printCount )
 {
-   if (printCount)
-      VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
-   if (err->tid > 0 && err->tid != last_tid_printed) {
-      VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
-      last_tid_printed = err->tid;
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "<error>");
+      VG_(message)(Vg_UserMsg, "  <tid>%d</tid>", err->tid);
+   }
+
+   if (!VG_(clo_xml)) {
+      if (printCount)
+         VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
+      if (err->tid > 0 && err->tid != last_tid_printed) {
+         VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
+         last_tid_printed = err->tid;
+      }
    }
 
    switch (err->ekind) {
@@ -274,6 +281,9 @@
             VG_(tool_panic)("unhandled error type");
          }
    }
+
+   if (VG_(clo_xml))
+      VG_(message)(Vg_UserMsg, "</error>");
 }
 
 /* Figure out if we want to perform a given action for this error, possibly
@@ -629,6 +639,31 @@
 /*--- Exported fns                                         ---*/
 /*------------------------------------------------------------*/
 
+/* Show the used suppressions.  Returns False if no suppression
+   got used. */
+static Bool show_used_suppressions ( void )
+{
+   Supp  *su;
+   Bool  any_supp;
+
+   any_supp = False;
+   for (su = suppressions; su != NULL; su = su->next) {
+      if (su->count <= 0)
+         continue;
+      any_supp = True;
+      if (VG_(clo_xml)) {
+         VG_(message)(Vg_DebugMsg, 
+                      "<supp><count>%d</count><name>%s</name></supp>", 
+                      su->count, su->sname);
+      } else {
+         VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
+      }
+   }
+
+   return any_supp;
+}
+
+
 /* This is called not from generated code but from the scheduler */
 void VG_(show_all_errors) ( void )
 {
@@ -652,6 +687,15 @@
       if (su->count > 0)
          n_supp_contexts++;
    }
+
+   /* If we're printing XML, just show the suppressions and stop.
+    */
+   if (VG_(clo_xml)) {
+      (void)show_used_suppressions();
+      return;
+   }
+
+   /* We only get here if not printing XML. */
    VG_(message)(Vg_UserMsg,
                 "ERROR SUMMARY: "
                 "%d errors from %d contexts (suppressed: %d from %d)",
@@ -691,13 +735,7 @@
 
    if (n_supp_contexts > 0) 
       VG_(message)(Vg_DebugMsg, "");
-   any_supp = False;
-   for (su = suppressions; su != NULL; su = su->next) {
-      if (su->count > 0) {
-         any_supp = True;
-         VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
-      }
-   }
+   any_supp = show_used_suppressions();
 
    if (n_err_contexts > 0) {
       if (any_supp) 
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 154cfbf..40f2251 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -43,6 +43,7 @@
 Int    VG_(clo_gen_suppressions) = 0;
 Int    VG_(clo_sanity_level)   = 1;
 Int    VG_(clo_verbosity)      = 1;
+Bool   VG_(clo_xml)            = False;
 Bool   VG_(clo_demangle)       = True;
 Bool   VG_(clo_trace_children) = False;
 Int    VG_(clo_log_fd)         = 2;
diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c
index 2c56d66..7dab5be 100644
--- a/coregrind/m_stacktrace.c
+++ b/coregrind/m_stacktrace.c
@@ -191,14 +191,26 @@
    static UChar buf[VG_ERRTXT_LEN];
 
    VG_(describe_IP)(ip, buf, VG_ERRTXT_LEN);
-   VG_(message)(Vg_UserMsg, "   %s %s", ( n == 0 ? "at" : "by" ), buf);
+
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "    %s", buf);
+   } else {
+      VG_(message)(Vg_UserMsg, "   %s %s", ( n == 0 ? "at" : "by" ), buf);
+   }
 }
 
 /* Print a StackTrace. */
 void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips )
 {
    vg_assert( n_ips > 0 );
+
+   if (VG_(clo_xml))
+      VG_(message)(Vg_UserMsg, "  <stack>");
+
    VG_(apply_StackTrace)( printIpDesc, ips, n_ips );
+
+   if (VG_(clo_xml))
+      VG_(message)(Vg_UserMsg, "  </stack>");
 }
 
 /* Get and immediately print a StackTrace. */
diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c
index fd5b286..ee20139 100644
--- a/coregrind/m_translate.c
+++ b/coregrind/m_translate.c
@@ -407,7 +407,7 @@
       ok = VG_(get_fnname_w_offset)(redir, name2, 64);
       if (!ok) VG_(strcpy)(name2, "???");
       VG_(message)(Vg_DebugMsg, 
-                   "TRANSLATE: 0x%llx (%s) redirected to 0x%llx (%s)",
+                   "REDIR: 0x%llx (%s) redirected to 0x%llx (%s)",
                    orig_addr, name1,
                    redir, name2 );
    }
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index 23450d8..4120180 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -1392,6 +1392,12 @@
    vg_assert(0 == res);
 }
 
+/*====================================================================*/
+/*=== Command-line: variables, processing, etc                     ===*/
+/*====================================================================*/
+
+// See pub_{core,tool}_options.h for explanations of all these.
+
 static void usage ( Bool debug_help )
 {
    Char* usage1 = 
@@ -1404,6 +1410,7 @@
 "    --version                 show version\n"
 "    -q --quiet                run silently; only print error msgs\n"
 "    -v --verbose              be more verbose, incl counts of errors\n"
+"    --xml=yes                 show errors as XML\n"
 "    --trace-children=no|yes   Valgrind-ise child processes? [no]\n"
 "    --track-fds=no|yes        track open file descriptors? [no]\n"
 "    --time-stamp=no|yes       add timestamps to log messages? [no]\n"
@@ -1627,6 +1634,7 @@
          /* do nothing */
       }
 
+      else VG_BOOL_CLO(arg, "--xml",              VG_(clo_xml))
       else VG_BOOL_CLO(arg, "--branchpred",       VG_(clo_branchpred))
       else VG_BOOL_CLO(arg, "--db-attach",        VG_(clo_db_attach))
       else VG_BOOL_CLO(arg, "--demangle",         VG_(clo_demangle))
@@ -1798,6 +1806,29 @@
       VG_(bad_option)("--gen-suppressions=");
    }
 
+   /* If we've been asked to emit XML, mash around various other
+      options so as to constrain the output somewhat, and to remove
+      any need for user input during the run. */
+   if (VG_(clo_xml)) {
+      /* Disable suppression generation (requires user input) */
+      VG_(clo_gen_suppressions) = 0;
+      /* Disable attaching to GDB (requires user input) */
+      VG_(clo_db_attach) = False;
+      /* Set a known verbosity level */
+      VG_(clo_verbosity) = 1;
+      /* Disable error limits (this might be a bad idea!) */
+      VG_(clo_error_limit) = False;
+      /* Disable emulation warnings */
+      VG_(clo_show_emwarns) = False;
+      /* Disable waiting for GDB to debug Valgrind */
+      VG_(clo_wait_for_gdb) = False;
+      /* No file-descriptor leak checking yet */
+      VG_(clo_track_fds) = False;
+      /* Also, we want to set options for the leak checker, but that
+         will have to be done in Memcheck's flag-handling code, not
+         here. */
+   }
+
    /* All non-logging-related options have been checked.  If the logging
       option specified is ok, we can switch to it, as we know we won't
       have to generate any other command-line-related error messages.
@@ -1914,6 +1945,16 @@
       }
    }
 
+
+   /* Check that the requested tool actually supports XML output. */
+   if (VG_(clo_xml) && 0 != VG_(strcmp)(toolname, "memcheck")) {
+      VG_(clo_xml) = False;
+      VG_(message)(Vg_UserMsg, 
+         "Currently only Memcheck supports XML output."); 
+      VG_(bad_option)("--xml=yes");
+      /*NOTREACHED*/
+   }
+
    // Move log_fd into the safe range, so it doesn't conflict with any app fds.
    // XXX: this is more or less duplicating the behaviour of the calls to
    // VG_(safe_fd)() above, although this does not close the original fd.
@@ -1932,27 +1973,43 @@
       command line args, to help people trying to interpret the
       results of a run which encompasses multiple processes. */
 
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg, "<valgrindoutput>");
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg, "<protocolversion>1</protocolversion>");
+      VG_(message)(Vg_UserMsg, "");
+   }
+
+   HChar* xpre  = VG_(clo_xml) ? "<preamble>" : "";
+   HChar* xpost = VG_(clo_xml) ? "</preamble>" : "";
+
    if (VG_(clo_verbosity > 0)) {
       /* Tool details */
-      VG_(message)(Vg_UserMsg, "%s%s%s, %s.",
+      VG_(message)(Vg_UserMsg, "%s%s%s%s, %s.%s",
+                   xpre,
                    VG_(details).name, 
                    NULL == VG_(details).version ? "" : "-",
                    NULL == VG_(details).version 
                       ? (Char*)"" : VG_(details).version,
-                   VG_(details).description);
-      VG_(message)(Vg_UserMsg, "%s", VG_(details).copyright_author);
+                   VG_(details).description,
+                   xpost);
+      VG_(message)(Vg_UserMsg, "%s%s%s", 
+                               xpre, VG_(details).copyright_author, xpost);
 
       /* Core details */
       VG_(message)(Vg_UserMsg,
-         "Using LibVEX rev %s, a library for dynamic binary translation.",
-         LibVEX_Version() );
+         "%sUsing LibVEX rev %s, a library for dynamic binary translation.%s",
+         xpre, LibVEX_Version(), xpost );
       VG_(message)(Vg_UserMsg, 
-         "Copyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP.");
+         "%sCopyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP.%s",
+         xpre, xpost );
       VG_(message)(Vg_UserMsg,
-         "Using valgrind-%s, a dynamic binary instrumentation framework.",
-         VERSION);
+         "%sUsing valgrind-%s, a dynamic binary instrumentation framework.%s",
+         xpre, VERSION, xpost);
       VG_(message)(Vg_UserMsg, 
-         "Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.");
+         "%sCopyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.%s",
+         xpre, xpost );
    }
 
    if (VG_(clo_verbosity) > 0 && log_to != VgLogTo_Fd) {
@@ -1963,6 +2020,17 @@
       for (i = 0; i < VG_(client_argc); i++) 
          VG_(message)(Vg_UserMsg, "   %s", VG_(client_argv)[i]);
    }
+   else
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg, "<pid>%d</pid>", VG_(getpid)());
+      VG_(message)(Vg_UserMsg, "<ppid>%d</ppid>", VG_(getppid)());
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg, "<argv>");   
+      for (i = 0; i < VG_(client_argc); i++) 
+         VG_(message)(Vg_UserMsg, "  <arg>%s</arg>", VG_(client_argv)[i]);
+      VG_(message)(Vg_UserMsg, "</argv>");   
+   }
 
    if (VG_(clo_verbosity) > 1) {
       Int fd;
@@ -1983,7 +2051,7 @@
       if (fd < 0) {
          VG_(message)(Vg_DebugMsg, "  can't open /proc/version");
       } else {
-         #define BUF_LEN    256
+#        define BUF_LEN    256
          Char version_buf[BUF_LEN];
          Int n = VG_(read) ( fd, version_buf, BUF_LEN );
          vg_assert(n <= BUF_LEN);
@@ -1994,7 +2062,7 @@
             VG_(message)(Vg_DebugMsg, "  (empty?)");
          }
          VG_(close)(fd);
-         #undef BUF_LEN
+#        undef BUF_LEN
       }
    }
 
@@ -2291,8 +2359,10 @@
 
 	 remains = VGA_(stack_unused)(tid);
 	 if (remains < VKI_PAGE_SIZE)
-	    VG_(message)(Vg_DebugMsg, "WARNING: Thread %d is within %d bytes of running out of stack!",
-			 tid, remains);
+	    VG_(message)(Vg_DebugMsg, 
+                         "WARNING: Thread %d is within %d bytes "
+                         "of running out of stack!",
+		         tid, remains);
       }
 
       /* 
@@ -2726,7 +2796,7 @@
    // Verbosity message
    //   p: end_rdtsc_calibration [so startup message is printed first]
    //--------------------------------------------------------------
-   if (VG_(clo_verbosity) == 1)
+   if (VG_(clo_verbosity) == 1 && !VG_(clo_xml))
       VG_(message)(Vg_UserMsg, "For more details, rerun with: -v");
    if (VG_(clo_verbosity) > 0)
       VG_(message)(Vg_UserMsg, "");
@@ -2745,6 +2815,11 @@
 
    vg_assert(VG_(master_tid) == 1);
 
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "<status>RUNNING</status>");
+      VG_(message)(Vg_UserMsg, "");
+   }
+
    VG_(debugLog)(1, "main", "Running thread 1\n");
    VGA_(main_thread_wrapper)(1);
 
@@ -2780,6 +2855,11 @@
    if (VG_(clo_verbosity) > 0)
       VG_(message)(Vg_UserMsg, "");
 
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "<status>FINISHED</status>");
+      VG_(message)(Vg_UserMsg, "");
+   }
+
    /* Print out file descriptor summary and stats. */
    if (VG_(clo_track_fds))
       VG_(show_open_fds)();
@@ -2789,6 +2869,12 @@
 
    VG_TDICT_CALL(tool_fini, 0/*exitcode*/);
 
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg, "</valgrindoutput>");
+      VG_(message)(Vg_UserMsg, "");
+   }
+
    VG_(sanity_check_general)( True /*include expensive checks*/ );
 
    if (VG_(clo_verbosity) > 1)
diff --git a/coregrind/vg_messages.c b/coregrind/vg_messages.c
index ad5928f..fe4004c 100644
--- a/coregrind/vg_messages.c
+++ b/coregrind/vg_messages.c
@@ -59,7 +59,9 @@
 
    // Print the message
    count = 0;
-   count += VG_(printf) ("%s%c%c", pfx_s, c,c);
+
+   if (!VG_(clo_xml))
+      count += VG_(printf) ("%s%c%c", pfx_s, c,c);
 
    if (VG_(clo_time_stamp)) {
       struct timeval tv;
@@ -75,7 +77,9 @@
       }
    }
 
-   count += VG_(printf) ("%d%c%c ", VG_(getpid)(), c,c);
+   if (!VG_(clo_xml))
+      count += VG_(printf) ("%d%c%c ", VG_(getpid)(), c,c);
+
    count += VG_(vprintf)(format, vargs);
    count += VG_(printf) ("\n");
    return count;
diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c
index a05f130..eb6a3f3 100644
--- a/coregrind/vg_symtab2.c
+++ b/coregrind/vg_symtab2.c
@@ -2436,7 +2436,7 @@
      buf[n] = '\0';                                         \
    }
    UInt  lineno; 
-   UChar ibuf[20];
+   UChar ibuf[50];
    UInt  n = 0;
    static UChar buf_fn[VG_ERRTXT_LEN];
    static UChar buf_obj[VG_ERRTXT_LEN];
@@ -2445,29 +2445,62 @@
    Bool  know_objname = VG_(get_objname)(eip, buf_obj, VG_ERRTXT_LEN);
    Bool  know_srcloc  = VG_(get_filename_linenum)(eip, buf_srcloc,
                                                   VG_ERRTXT_LEN, &lineno);
-   VG_(sprintf)(ibuf,"0x%llx: ", (ULong)eip);
-   APPEND(ibuf);
-   if (know_fnname) { 
-      APPEND(buf_fn);
-      if (!know_srcloc && know_objname) {
-         APPEND(" (in ");
+
+   if (VG_(clo_xml)) {
+
+      /* Print in XML format, dumping in as much info as we know. */
+      APPEND("<frame>");
+      VG_(sprintf)(ibuf,"<ip>0x%llx</ip>", (ULong)eip);
+      APPEND(ibuf);
+      if (know_objname) {
+         APPEND("<obj>");
+         APPEND(buf_obj);
+         APPEND("</obj>");
+      }
+      if (know_fnname) {
+         APPEND("<fn>");
+         APPEND(buf_fn);
+         APPEND("</fn>");
+      }
+      if (know_srcloc) {
+         APPEND("<file>");
+         APPEND(buf_srcloc);
+         APPEND("</file>");
+         APPEND("<line>");
+         VG_(sprintf)(ibuf,"%d",lineno);
+         APPEND(ibuf);
+         APPEND("</line>");
+      }
+      APPEND("</frame>");
+
+   } else {
+
+      /* Print for humans to read */
+      VG_(sprintf)(ibuf,"0x%llx: ", (ULong)eip);
+      APPEND(ibuf);
+      if (know_fnname) { 
+         APPEND(buf_fn);
+         if (!know_srcloc && know_objname) {
+            APPEND(" (in ");
+            APPEND(buf_obj);
+            APPEND(")");
+         }
+      } else if (know_objname && !know_srcloc) {
+         APPEND("(within ");
          APPEND(buf_obj);
          APPEND(")");
+      } else {
+         APPEND("???");
       }
-   } else if (know_objname && !know_srcloc) {
-      APPEND("(within ");
-      APPEND(buf_obj);
-      APPEND(")");
-   } else {
-      APPEND("???");
-   }
-   if (know_srcloc) {
-      APPEND(" (");
-      APPEND(buf_srcloc);
-      APPEND(":");
-      VG_(sprintf)(ibuf,"%d",lineno);
-      APPEND(ibuf);
-      APPEND(")");
+      if (know_srcloc) {
+         APPEND(" (");
+         APPEND(buf_srcloc);
+         APPEND(":");
+         VG_(sprintf)(ibuf,"%d",lineno);
+         APPEND(ibuf);
+         APPEND(")");
+      }
+
    }
    return buf;
 
diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h
index e321635..e27e158 100644
--- a/include/pub_tool_options.h
+++ b/include/pub_tool_options.h
@@ -61,10 +61,15 @@
    }
 
 /* Verbosity level: 0 = silent, 1 (default), > 1 = more verbose. */
-extern Int   VG_(clo_verbosity);
+extern Int  VG_(clo_verbosity);
 
-/* Profile? */
-extern Bool  VG_(clo_profile);
+/* Profile?  default: NO */
+extern Bool VG_(clo_profile);
+
+/* Emit all messages as XML? default: NO */
+/* If clo_xml is set, various other options are set in a non-default
+   way.  See vg_main.c and mc_main.c. */
+extern Bool VG_(clo_xml);
 
 /* Call this if a recognised option was bad for some reason.
    Note: don't use it just because an option was unrecognised -- return 'False'
diff --git a/memcheck/mac_leakcheck.c b/memcheck/mac_leakcheck.c
index 38e6d49..d97730d 100644
--- a/memcheck/mac_leakcheck.c
+++ b/memcheck/mac_leakcheck.c
@@ -184,40 +184,79 @@
 static Bool	  (*lc_is_within_valid_secondary) (Addr addr);
 static Bool	  (*lc_is_valid_aligned_word)     (Addr addr);
 
-static const Char *str_lossmode(Reachedness lossmode)
+static const HChar* str_lossmode ( Reachedness lossmode )
 {
-   const Char *loss = "?";
-
-   switch(lossmode) {
-   case Unreached:	loss = "definitely lost"; break;
-   case IndirectLeak:	loss = "indirectly lost"; break;
-   case Interior:	loss = "possibly lost"; break;
-   case Proper:		loss = "still reachable"; break;
+   const HChar *loss = "?";
+   switch (lossmode) {
+      case Unreached:    loss = "definitely lost"; break;
+      case IndirectLeak: loss = "indirectly lost"; break;
+      case Interior:     loss = "possibly lost"; break;
+      case Proper:       loss = "still reachable"; break;
    }
-
    return loss;
 }
 
+static const HChar* xml_kind ( Reachedness lossmode )
+{
+   const HChar *loss = "?";
+   switch (lossmode) {
+      case Unreached:    loss = "Leak_DefinitelyLost"; break;
+      case IndirectLeak: loss = "Leak_IndirectlyLost"; break;
+      case Interior:     loss = "Leak_PossiblyLost"; break;
+      case Proper:       loss = "Leak_StillReachable"; break;
+   }
+   return loss;
+}
+
+
 /* Used for printing leak errors, avoids exposing the LossRecord type (which
    comes in as void*, requiring a cast. */
 void MAC_(pp_LeakError)(void* vextra)
 {
+   HChar* xpre  = VG_(clo_xml) ? "  <what>" : "";
+   HChar* xpost = VG_(clo_xml) ? "</what>"  : "";
+
    LeakExtra* extra = (LeakExtra*)vextra;
    LossRecord* l    = extra->lossRecord;
    const Char *loss = str_lossmode(l->loss_mode);
 
-   VG_(message)(Vg_UserMsg, "");
+   if (VG_(clo_xml)) {
+      VG_(message)(Vg_UserMsg, "  <kind>%s</kind>", xml_kind(l->loss_mode));
+   } else {
+      VG_(message)(Vg_UserMsg, "");
+   }
+
    if (l->indirect_bytes) {
       VG_(message)(Vg_UserMsg, 
-		   "%d (%d direct, %d indirect) bytes in %d blocks are %s in loss record %d of %d",
-		   l->total_bytes + l->indirect_bytes, 
-		   l->total_bytes, l->indirect_bytes, l->num_blocks,
-		   loss, extra->n_this_record, extra->n_total_records);
+         "%s%d (%d direct, %d indirect) bytes in %d blocks"
+         " are %s in loss record %d of %d%s",
+         xpre,
+         l->total_bytes + l->indirect_bytes, 
+         l->total_bytes, l->indirect_bytes, l->num_blocks,
+         loss, extra->n_this_record, extra->n_total_records,
+         xpost
+      );
+      if (VG_(clo_xml)) {
+         VG_(message)(Vg_UserMsg, "  <leakedbytes>%d</leakedbytes>", 
+                                  l->total_bytes + l->indirect_bytes);
+         VG_(message)(Vg_UserMsg, "  <leakedblocks>%d</leakedblocks>", 
+                                  l->num_blocks);
+      }
    } else {
-      VG_(message)(Vg_UserMsg, 
-		   "%d bytes in %d blocks are %s in loss record %d of %d",
-		   l->total_bytes, l->num_blocks,
-		   loss, extra->n_this_record, extra->n_total_records);
+      VG_(message)(
+         Vg_UserMsg, 
+         "%s%d bytes in %d blocks are %s in loss record %d of %d%s",
+         xpre,
+         l->total_bytes, l->num_blocks,
+         loss, extra->n_this_record, extra->n_total_records,
+         xpost
+      );
+      if (VG_(clo_xml)) {
+         VG_(message)(Vg_UserMsg, "  <leakedbytes>%d</leakedbytes>", 
+                                  l->total_bytes);
+         VG_(message)(Vg_UserMsg, "  <leakedblocks>%d</leakedblocks>", 
+                                  l->num_blocks);
+      }
    }
    VG_(pp_ExeContext)(l->allocated_at);
 }
@@ -612,14 +651,14 @@
 
    if (lc_n_shadows == 0) {
       tl_assert(lc_shadows == NULL);
-      if (VG_(clo_verbosity) >= 1) {
+      if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) {
          VG_(message)(Vg_UserMsg, 
                       "No malloc'd blocks -- no leaks are possible.");
       }
       return;
    }
 
-   if (VG_(clo_verbosity) > 0)
+   if (VG_(clo_verbosity) > 0 && !VG_(clo_xml))
       VG_(message)(Vg_UserMsg, 
                    "searching for pointers to %d not-freed blocks.", 
                    lc_n_shadows );
@@ -650,7 +689,7 @@
    /* Keep walking the heap until everything is found */
    lc_do_leakcheck(-1);
 
-   if (VG_(clo_verbosity) > 0)
+   if (VG_(clo_verbosity) > 0 && !VG_(clo_xml))
       VG_(message)(Vg_UserMsg, "checked %d bytes.", lc_scanned);
 
    blocks_leaked     = MAC_(bytes_leaked)     = 0;
@@ -664,7 +703,7 @@
    else
       make_summary();
 
-   if (VG_(clo_verbosity) > 0) {
+   if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) {
       VG_(message)(Vg_UserMsg, "");
       VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
       VG_(message)(Vg_UserMsg, "   definitely lost: %d bytes in %d blocks.", 
diff --git a/memcheck/mac_malloc_wrappers.c b/memcheck/mac_malloc_wrappers.c
index 8119a02..2167293 100644
--- a/memcheck/mac_malloc_wrappers.c
+++ b/memcheck/mac_malloc_wrappers.c
@@ -549,6 +549,8 @@
    
    if (VG_(clo_verbosity) == 0)
       return;
+   if (VG_(clo_xml))
+      return;
 
    /* Count memory still in use. */
    VG_(HT_apply_to_all_nodes)(MAC_(malloc_list), malloc_stats_count_chunk, &ms);
diff --git a/memcheck/mac_shared.c b/memcheck/mac_shared.c
index 2fc4b31..bcd440a 100644
--- a/memcheck/mac_shared.c
+++ b/memcheck/mac_shared.c
@@ -222,23 +222,27 @@
 
 void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
 {
+   HChar* xpre  = VG_(clo_xml) ? "  <auxwhat>" : " ";
+   HChar* xpost = VG_(clo_xml) ? "</auxwhat>"  : "";
+
    switch (ai->akind) {
       case Stack: 
          VG_(message)(Vg_UserMsg, 
-                      " Address 0x%lx is on thread %d's stack", 
-                      (ULong)a, ai->stack_tid);
+                      "%sAddress 0x%llx is on thread %d's stack%s", 
+                      xpre, (ULong)a, ai->stack_tid, xpost);
          break;
       case Unknown:
          if (ai->maybe_gcc) {
             VG_(message)(Vg_UserMsg, 
-               " Address 0x%lx is just below %%esp.  Possibly a bug in GCC/G++",
-               (ULong)a);
-            VG_(message)(Vg_UserMsg, 
-               "  v 2.96 or 3.0.X.  To suppress, use: --workaround-gcc296-bugs=yes");
+               "%sAddress 0x%llx is just below the stack ptr.  "
+               "To suppress, use: --workaround-gcc296-bugs=yes%s",
+               xpre, (ULong)a, xpost
+            );
 	 } else {
             VG_(message)(Vg_UserMsg, 
-               " Address 0x%lx is not stack'd, malloc'd or (recently) free'd",
-               (ULong)a);
+               "%sAddress 0x%llx "
+               "is not stack'd, malloc'd or (recently) free'd%s",
+               xpre, (ULong)a, xpost);
          }
          break;
       case Freed: case Mallocd: case UserG: case Mempool: {
@@ -264,12 +268,14 @@
             relative = "inside";
          }
          VG_(message)(Vg_UserMsg, 
-            " Address 0x%lx is %llu bytes %s a %s of size %d %s",
+            "%sAddress 0x%llx is %llu bytes %s a %s of size %d %s%s",
+            xpre,
             (ULong)a, (ULong)delta, relative, kind,
             ai->blksize,
             ai->akind==Mallocd ? "alloc'd" 
                : ai->akind==Freed ? "free'd" 
-                                  : "client-defined");
+                                  : "client-defined",
+            xpost);
          VG_(pp_ExeContext)(ai->lastchange);
          break;
       }
@@ -288,14 +294,26 @@
 {
    MAC_Error* err_extra = VG_(get_error_extra)(err);
 
+   HChar* xpre  = VG_(clo_xml) ? "  <what>" : "";
+   HChar* xpost = VG_(clo_xml) ? "</what>"  : "";
+
    switch (VG_(get_error_kind)(err)) {
       case FreeErr:
-         VG_(message)(Vg_UserMsg, "Invalid free() / delete / delete[]");
-         /* fall through */
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>InvalidFree</kind>");
+         VG_(message)(Vg_UserMsg, 
+                      "%sInvalid free() / delete / delete[]%s",
+                      xpre, xpost);
+         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+         MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+         break;
+
       case FreeMismatchErr:
-         if (VG_(get_error_kind)(err) == FreeMismatchErr)
-            VG_(message)(Vg_UserMsg, 
-                         "Mismatched free() / delete / delete []");
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>MismatchedFree</kind>");
+         VG_(message)(Vg_UserMsg, 
+                      "%sMismatched free() / delete / delete []%s",
+                      xpre, xpost);
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
          break;
@@ -303,16 +321,26 @@
       case AddrErr:
          switch (err_extra->axskind) {
             case ReadAxs:
-               VG_(message)(Vg_UserMsg, "Invalid read of size %d", 
-                                        err_extra->size ); 
+               if (VG_(clo_xml))
+                  VG_(message)(Vg_UserMsg, "  <kind>InvalidRead</kind>");
+               VG_(message)(Vg_UserMsg,
+                            "%sInvalid read of size %d%s", 
+                            xpre, err_extra->size, xpost ); 
                break;
             case WriteAxs:
-               VG_(message)(Vg_UserMsg, "Invalid write of size %d", 
-                                        err_extra->size ); 
+               if (VG_(clo_xml))
+                  VG_(message)(Vg_UserMsg, "  <kind>InvalidWrite</kind>");
+               VG_(message)(Vg_UserMsg, 
+                           "%sInvalid write of size %d%s", 
+                           xpre, err_extra->size, xpost ); 
                break;
             case ExecAxs:
-               VG_(message)(Vg_UserMsg, "Jump to the invalid address "
-                                        "stated on the next line");
+               if (VG_(clo_xml))
+                  VG_(message)(Vg_UserMsg, "  <kind>InvalidJump</kind>");
+               VG_(message)(Vg_UserMsg, 
+                            "%sJump to the invalid address "
+                            "stated on the next line%s",
+                            xpre, xpost);
                break;
             default: 
                VG_(tool_panic)("MAC_(pp_shared_Error)(axskind)");
@@ -323,16 +351,22 @@
 
       case OverlapErr: {
          OverlapExtra* ov_extra = (OverlapExtra*)VG_(get_error_extra)(err);
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>Overlap</kind>");
          if (ov_extra->len == -1)
             VG_(message)(Vg_UserMsg,
-                         "Source and destination overlap in %s(%p, %p)",
+                         "%sSource and destination overlap in %s(%p, %p)%s",
+                         xpre,
                          VG_(get_error_string)(err),
-                         ov_extra->dst, ov_extra->src);
+                         ov_extra->dst, ov_extra->src,
+                         xpost);
          else
             VG_(message)(Vg_UserMsg,
-                         "Source and destination overlap in %s(%p, %p, %d)",
+                         "%sSource and destination overlap in %s(%p, %p, %d)%s",
+                         xpre,
                          VG_(get_error_string)(err),
-                         ov_extra->dst, ov_extra->src, ov_extra->len);
+                         ov_extra->dst, ov_extra->src, ov_extra->len,
+                         xpost);
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          break;
       }
@@ -342,7 +376,10 @@
       }
 
       case IllegalMempoolErr:
-         VG_(message)(Vg_UserMsg, "Illegal memory pool address");
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>InvalidMemPool</kind>");
+         VG_(message)(Vg_UserMsg, "%sIllegal memory pool address%s",
+                                  xpre, xpost);
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
          break;
@@ -860,7 +897,7 @@
 {
    MAC_(print_malloc_stats)();
 
-   if (VG_(clo_verbosity) == 1) {
+   if (VG_(clo_verbosity) == 1 && !VG_(clo_xml)) {
       if (MAC_(clo_leak_check) == LC_Off)
          VG_(message)(Vg_UserMsg, 
              "For a detailed leak analysis,  rerun with: --leak-check=yes");
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index d28c91f..3244214 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -1313,11 +1313,18 @@
 {
    MAC_Error* err_extra = VG_(get_error_extra)(err);
 
+   HChar* xpre  = VG_(clo_xml) ? "  <what>" : "";
+   HChar* xpost = VG_(clo_xml) ? "</what>"  : "";
+
    switch (VG_(get_error_kind)(err)) {
       case CoreMemErr: {
          Char* s = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
-         VG_(message)(Vg_UserMsg, "%s contains %s byte(s)", 
-                      VG_(get_error_string)(err), s);
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>CoreMemError</kind>");
+            /* What the hell *is* a CoreMemError? jrs 2005-May-18 */
+         VG_(message)(Vg_UserMsg, "%s%s contains %s byte(s)%s", 
+                      xpre, VG_(get_error_string)(err), s, xpost);
+
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          break;
       
@@ -1325,12 +1332,17 @@
       
       case ValueErr:
          if (err_extra->size == 0) {
-             VG_(message)(Vg_UserMsg,
-                "Conditional jump or move depends on uninitialised value(s)");
+            if (VG_(clo_xml))
+               VG_(message)(Vg_UserMsg, "  <kind>UninitCondition</kind>");
+            VG_(message)(Vg_UserMsg, "%sConditional jump or move depends"
+                                     " on uninitialised value(s)%s", 
+                                     xpre, xpost);
          } else {
-             VG_(message)(Vg_UserMsg,
-                          "Use of uninitialised value of size %d",
-                          err_extra->size);
+            if (VG_(clo_xml))
+               VG_(message)(Vg_UserMsg, "  <kind>UninitValue</kind>");
+            VG_(message)(Vg_UserMsg,
+                         "%sUse of uninitialised value of size %d%s",
+                         xpre, err_extra->size, xpost);
          }
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          break;
@@ -1341,8 +1353,10 @@
          Char* s2 = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
          if (isReg) tl_assert(!err_extra->isUnaddr);
 
-         VG_(message)(Vg_UserMsg, "Syscall param %s %s %s byte(s)",
-                      VG_(get_error_string)(err), s1, s2);
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>SyscallParam</kind>");
+         VG_(message)(Vg_UserMsg, "%sSyscall param %s %s %s byte(s)%s",
+                      xpre, VG_(get_error_string)(err), s1, s2, xpost);
 
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
@@ -1351,8 +1365,11 @@
       case UserErr: {
          Char* s = ( err_extra->isUnaddr ? "Unaddressable" : "Uninitialised" );
 
+         if (VG_(clo_xml))
+            VG_(message)(Vg_UserMsg, "  <kind>ClientCheck</kind>");
          VG_(message)(Vg_UserMsg, 
-            "%s byte(s) found during client check request", s);
+            "%s%s byte(s) found during client check request%s", 
+            xpre, s, xpost);
 
          VG_(pp_ExeContext)( VG_(get_error_where)(err) );
          MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
@@ -2426,6 +2443,13 @@
 
 static void mc_post_clo_init ( void )
 {
+   /* If we've been asked to emit XML, mash around various other
+      options so as to constrain the output somewhat. */
+   if (VG_(clo_xml)) {
+      /* Extract as much info as possible from the leak checker. */
+      MAC_(clo_show_reachable) = True;
+      MAC_(clo_leak_check) = LC_Full;
+   }
 }
 
 static void mc_fini ( Int exitcode )