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/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.",