Completely overhauled the internals of Memcheck's error handling.  All the
different error kinds were reusing the same struct for storing their
details.  Each one used some but not all the fields, and the AddrInfo was
similar, and it was very confusing.

So I changed MC_Error and AddrInfo to be tagged unions, like Vex's IRExpr and
IRStmt types.  The resulting code is a little more verbose but much easier
to understand.  I also split up several error kinds, which also made things
simpler.  The user-visible behaviour is identical except for a couple of
very minor things that I've documented in the NEWS file for the 3.3.0
release.

Ideally I'd get rid of the Addr and Char* fields in the core Error type,
which are not always used, and do them similarly within tools.  But that
would require changing the core/tool interface, so I'm leaving it for the
moment.




git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6402 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index cfae208..38b4677 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,24 @@
+Release 3.2.0 (XX XXX 2007)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3.2.0 is a feature release with ...
+
+XXX:
+* Note in NEWS that behaviour of GET_VBITS and SET_BBITS have changed --
+  they no longer issue addressability errors if either array is partially
+  unaddressable, they just return 3.  Also, SET_VBITS doesn't report
+  definedness errors if any of the V bits are undefined.
+
+Other user-visible changes:
+
+- A new suppression kind has been introduced: "Jump".  This is for
+  suppressing jump-to-invalid-address errors.  Previously you had to use an
+  "Addr1" suppression, which didn't make much sense.
+
+- The behaviour of Memcheck's client requests VALGRIND_GET_VBITS and
+  VALGRIND_SET_VBITS have changed slightly.  They no longer issue
+  addressability errors -- if either array is partially unaddressable,
+  they just return 3 (as before).  Also, SET_VBITS doesn't report
+  definedness errors if any of the V bits are undefined.
 
 Release 3.2.1 (16 Sept 2006)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c
index 0c08225..85db96c 100644
--- a/coregrind/m_errormgr.c
+++ b/coregrind/m_errormgr.c
@@ -101,9 +101,6 @@
 /*--- Error type                                           ---*/
 /*------------------------------------------------------------*/
 
-/* Note: it is imperative this doesn't overlap with (0..) at all, as tools
- * effectively extend it by defining their own enums in the (0..) range. */
-
 /* Errors.  Extensible (via the 'extra' field).  Tools can use a normal
    enum (with element values in the normal range (0..)) for 'ekind'. 
    Functions for getting/setting the tool-relevant fields are in
diff --git a/glibc-2.2.supp b/glibc-2.2.supp
index 5af5c09..cfe6719 100644
--- a/glibc-2.2.supp
+++ b/glibc-2.2.supp
@@ -16,7 +16,7 @@
 #
 # For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #
diff --git a/glibc-2.3.supp b/glibc-2.3.supp
index 6206680..0fd33c7 100644
--- a/glibc-2.3.supp
+++ b/glibc-2.3.supp
@@ -16,7 +16,7 @@
 #
 # For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #
diff --git a/glibc-2.4.supp b/glibc-2.4.supp
index 812b89e..d7c3668 100644
--- a/glibc-2.4.supp
+++ b/glibc-2.4.supp
@@ -16,7 +16,7 @@
 #
 # For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #
diff --git a/glibc-2.5.supp b/glibc-2.5.supp
index 809047b..d9c097d 100644
--- a/glibc-2.5.supp
+++ b/glibc-2.5.supp
@@ -16,7 +16,7 @@
 #
 # For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 3beb225..26aafe0 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -635,6 +635,11 @@
   </listitem>
 
   <listitem>
+    <para>Or: <varname>Jump</varname>, meaning an
+    jump to an unaddressable location error.</para>
+  </listitem>
+
+  <listitem>
     <para>Or: <varname>Param</varname>, meaning an
     invalid system call parameter error.</para>
   </listitem>
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index 23a6b2c..0c79c44 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -56,7 +56,7 @@
    struct _MC_Chunk {
       struct _MC_Chunk* next;
       Addr         data;            // ptr to actual block
-      SizeT        size : (sizeof(UWord)*8)-2; // size requested; 30 or 62 bits
+      SizeT        szB : (sizeof(UWord)*8)-2; // size requested; 30 or 62 bits
       MC_AllocKind allockind : 2;   // which wrapper did the allocation
       ExeContext*  where;           // where it was allocated
    }
@@ -120,28 +120,6 @@
 
 
 /*------------------------------------------------------------*/
-/*--- Errors and suppressions                              ---*/
-/*------------------------------------------------------------*/
-
-/* Extra info for overlap errors */
-typedef
-   struct {
-      Addr src;
-      Addr dst;
-      Int  len;   // -1 if unused
-   }
-   OverlapExtra;
-
-extern void MC_(record_free_error)            ( ThreadId tid, Addr a ); 
-extern void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a );
-extern void MC_(record_freemismatch_error)    ( ThreadId tid, Addr a,
-                                                MC_Chunk* mc );
-extern Bool MC_(record_leak_error)            ( ThreadId tid, 
-                                                void* leak_extra,
-                                                ExeContext* where,
-                                                Bool print_record );
-
-/*------------------------------------------------------------*/
 /*--- Profiling of memory events                           ---*/
 /*------------------------------------------------------------*/
 
@@ -201,6 +179,21 @@
 /*--- Leak checking                                        ---*/
 /*------------------------------------------------------------*/
 
+/* A block is either 
+   -- Proper-ly reached; a pointer to its start has been found
+   -- Interior-ly reached; only an interior pointer to it has been found
+   -- Unreached; so far, no pointers to any part of it have been found. 
+   -- IndirectLeak; leaked, but referred to by another leaked block
+*/
+typedef 
+   enum { 
+      Unreached    =0, 
+      IndirectLeak =1,
+      Interior     =2, 
+      Proper       =3
+  }
+  Reachedness;
+
 /* For VALGRIND_COUNT_LEAKS client request */
 extern SizeT MC_(bytes_leaked);
 extern SizeT MC_(bytes_indirect);
@@ -208,9 +201,6 @@
 extern SizeT MC_(bytes_reachable);
 extern SizeT MC_(bytes_suppressed);
 
-/* For leak checking */
-extern void MC_(pp_LeakError)(void* extra);
-                           
 typedef
    enum {
       LC_Off,
@@ -219,12 +209,44 @@
    }
    LeakCheckMode;
 
+/* A block record, used for generating err msgs. */
+typedef
+   struct _LossRecord {
+      struct _LossRecord* next;
+      /* Where these lost blocks were allocated. */
+      ExeContext*  allocated_at;
+      /* Their reachability. */
+      Reachedness  loss_mode;
+      /* Number of blocks and total # bytes involved. */
+      SizeT        total_bytes;
+      SizeT        indirect_bytes;
+      UInt         num_blocks;
+   }
+   LossRecord;
+
 extern void MC_(do_detect_memory_leaks) (
           ThreadId tid, LeakCheckMode mode,
           Bool (*is_within_valid_secondary) ( Addr ),
           Bool (*is_valid_aligned_word)     ( Addr )
        );
 
+extern void MC_(pp_LeakError)(UInt n_this_record, UInt n_total_records,
+                              LossRecord* l);
+                          
+
+/*------------------------------------------------------------*/
+/*--- Errors and suppressions                              ---*/
+/*------------------------------------------------------------*/
+
+extern void MC_(record_free_error)            ( ThreadId tid, Addr a ); 
+extern void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a );
+extern void MC_(record_freemismatch_error)    ( ThreadId tid, MC_Chunk* mc );
+extern Bool MC_(record_leak_error)            ( ThreadId tid,
+                                                UInt n_this_record,
+                                                UInt n_total_records,
+                                                LossRecord* lossRecord,
+                                                Bool print_record );
+
 /*------------------------------------------------------------*/
 /*--- Command line options + defaults                      ---*/
 /*------------------------------------------------------------*/
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index 71256e9..6faae9a 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -109,21 +109,6 @@
 /*--- Detecting leaked (unreachable) malloc'd blocks.      ---*/
 /*------------------------------------------------------------*/
 
-/* A block is either 
-   -- Proper-ly reached; a pointer to its start has been found
-   -- Interior-ly reached; only an interior pointer to it has been found
-   -- Unreached; so far, no pointers to any part of it have been found. 
-   -- IndirectLeak; leaked, but referred to by another leaked block
-*/
-typedef 
-   enum { 
-      Unreached    =0, 
-      IndirectLeak =1,
-      Interior     =2, 
-      Proper       =3
-  }
-  Reachedness;
-
 /* An entry in the mark stack */
 typedef 
    struct {
@@ -133,30 +118,6 @@
    } 
    MarkStack;
 
-/* A block record, used for generating err msgs. */
-typedef
-   struct _LossRecord {
-      struct _LossRecord* next;
-      /* Where these lost blocks were allocated. */
-      ExeContext*  allocated_at;
-      /* Their reachability. */
-      Reachedness  loss_mode;
-      /* Number of blocks and total # bytes involved. */
-      SizeT        total_bytes;
-      SizeT        indirect_bytes;
-      UInt         num_blocks;
-   }
-   LossRecord;
-
-/* The 'extra' struct for leak errors. */
-typedef 
-   struct {
-      UInt        n_this_record;
-      UInt        n_total_records;
-      LossRecord* lossRecord;
-   }
-   LeakExtra;
-
 /* Find the i such that ptr points at or inside the block described by
    shadows[i].  Return -1 if none found.  This assumes that shadows[]
    has been sorted on the ->data field. */
@@ -175,7 +136,7 @@
    for (i = 0; i < n_shadows; i++) {
       PROF_EVENT(71, "find_shadow_for_OLD(loop)");
       a_lo = shadows[i]->data;
-      a_hi = ((Addr)shadows[i]->data) + shadows[i]->size;
+      a_hi = ((Addr)shadows[i]->data) + shadows[i]->szB;
       if (a_lo <= ptr && ptr < a_hi)
          return i;
    }
@@ -201,14 +162,14 @@
 
       mid      = (lo + hi) / 2;
       a_mid_lo = shadows[mid]->data;
-      a_mid_hi = shadows[mid]->data + shadows[mid]->size;
+      a_mid_hi = shadows[mid]->data + shadows[mid]->szB;
       /* Extent of block 'mid' is [a_mid_lo .. a_mid_hi).
          Special-case zero-sized blocks - treat them as if they had
          size 1.  Not doing so causes them to not cover any address
          range at all and so will never be identified as the target of
          any pointer, which causes them to be incorrectly reported as
          definitely leaked. */
-      if (shadows[mid]->size == 0)
+      if (shadows[mid]->szB == 0)
          a_mid_hi++;
 
       if (ptr < a_mid_lo) {
@@ -243,83 +204,6 @@
 static Bool	  (*lc_is_within_valid_secondary) (Addr addr);
 static Bool	  (*lc_is_valid_aligned_word)     (Addr addr);
 
-static const HChar* str_lossmode ( Reachedness lossmode )
-{
-   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 MC_(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);
-
-   if (VG_(clo_xml)) {
-      VG_(message)(Vg_UserMsg, "  <kind>%t</kind>", xml_kind(l->loss_mode));
-   } else {
-      VG_(message)(Vg_UserMsg, "");
-   }
-
-   if (l->indirect_bytes) {
-      VG_(message)(Vg_UserMsg, 
-         "%s%,lu (%,lu direct, %,lu indirect) bytes in %,u blocks"
-         " are %s in loss record %,u of %,u%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)) {
-         // Nb: don't put commas in these XML numbers 
-         VG_(message)(Vg_UserMsg, "  <leakedbytes>%lu</leakedbytes>", 
-                                  l->total_bytes + l->indirect_bytes);
-         VG_(message)(Vg_UserMsg, "  <leakedblocks>%u</leakedblocks>", 
-                                  l->num_blocks);
-      }
-   } else {
-      VG_(message)(
-         Vg_UserMsg, 
-         "%s%,lu bytes in %,u blocks are %s in loss record %,u of %,u%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);
-}
 
 SizeT MC_(bytes_leaked)     = 0;
 SizeT MC_(bytes_indirect)   = 0;
@@ -356,13 +240,13 @@
    tl_assert(sh_no >= 0 && sh_no < lc_n_shadows);
    tl_assert(ptr >= lc_shadows[sh_no]->data);
    tl_assert(ptr < lc_shadows[sh_no]->data 
-                   + lc_shadows[sh_no]->size
-                   + (lc_shadows[sh_no]->size==0  ? 1  : 0));
+                   + lc_shadows[sh_no]->szB
+                   + (lc_shadows[sh_no]->szB==0  ? 1  : 0));
 
    if (lc_markstack[sh_no].state == Unreached) {
       if (0)
 	 VG_(printf)("pushing %p-%p\n", lc_shadows[sh_no]->data, 
-		     lc_shadows[sh_no]->data + lc_shadows[sh_no]->size);
+		     lc_shadows[sh_no]->data + lc_shadows[sh_no]->szB);
 
       tl_assert(lc_markstack[sh_no].next == -1);
       lc_markstack[sh_no].next = lc_markstack_top;
@@ -391,13 +275,13 @@
 	       if (lc_markstack[sh_no].indirect)
 		  VG_(printf)("  clique %d joining clique %d adding %d+%d bytes\n", 
 			      sh_no, clique, 
-			      lc_shadows[sh_no]->size, lc_markstack[sh_no].indirect);
+			      lc_shadows[sh_no]->szB, lc_markstack[sh_no].indirect);
 	       else
 		  VG_(printf)("  %d joining %d adding %d\n", 
-			      sh_no, clique, lc_shadows[sh_no]->size);
+			      sh_no, clique, lc_shadows[sh_no]->szB);
 	    }
 
-	    lc_markstack[clique].indirect += lc_shadows[sh_no]->size;
+	    lc_markstack[clique].indirect += lc_shadows[sh_no]->szB;
 	    lc_markstack[clique].indirect += lc_markstack[sh_no].indirect;
 	    lc_markstack[sh_no].indirect = 0; /* shouldn't matter */
 	 }
@@ -503,7 +387,7 @@
       tl_assert(top >= 0 && top < lc_n_shadows);      
       tl_assert(lc_markstack[top].state != Unreached);
 
-      lc_scan_memory_WRK(lc_shadows[top]->data, lc_shadows[top]->size, clique);
+      lc_scan_memory_WRK(lc_shadows[top]->data, lc_shadows[top]->szB, clique);
    }
 }
 
@@ -520,7 +404,6 @@
    LossRecord* errlist;
    LossRecord* p;
    Bool   is_suppressed;
-   LeakExtra leak_extra;
 
    /* Go through and group lost structures into cliques.  For each
       Unreached block, push it onto the mark stack, and find all the
@@ -530,8 +413,8 @@
       pass), then the cliques are merged. */
    for (i = 0; i < lc_n_shadows; i++) {
       if (VG_DEBUG_CLIQUE)
-	 VG_(printf)("cliques: %d at %p -> %s\n",
-		     i, lc_shadows[i]->data, str_lossmode(lc_markstack[i].state));
+	 VG_(printf)("cliques: %d at %p -> Loss state %d\n",
+		     i, lc_shadows[i]->data, lc_markstack[i].state);
       if (lc_markstack[i].state != Unreached)
 	 continue;
 
@@ -578,14 +461,14 @@
       }
       if (p != NULL) {
          p->num_blocks  ++;
-         p->total_bytes += lc_shadows[i]->size;
+         p->total_bytes += lc_shadows[i]->szB;
 	 p->indirect_bytes += lc_markstack[i].indirect;
       } else {
          n_lossrecords ++;
          p = VG_(malloc)(sizeof(LossRecord));
          p->loss_mode    = lc_markstack[i].state;
          p->allocated_at = where;
-         p->total_bytes  = lc_shadows[i]->size;
+         p->total_bytes  = lc_shadows[i]->szB;
 	 p->indirect_bytes = lc_markstack[i].indirect;
          p->num_blocks   = 1;
          p->next         = errlist;
@@ -619,11 +502,8 @@
       // Nb: because VG_(unique_error) does all the error processing
       // immediately, and doesn't save the error, leakExtra can be
       // stack-allocated.
-      leak_extra.n_this_record   = i+1;
-      leak_extra.n_total_records = n_lossrecords;
-      leak_extra.lossRecord      = p_min;
       is_suppressed = 
-         MC_(record_leak_error) ( tid, &leak_extra, p_min->allocated_at,
+         MC_(record_leak_error) ( tid, i+1, n_lossrecords, p_min,
                                   print_record );
 
       if (is_suppressed) {
@@ -659,7 +539,7 @@
    Int i;
 
    for(i = 0; i < lc_n_shadows; i++) {
-      SizeT size = lc_shadows[i]->size;
+      SizeT size = lc_shadows[i]->szB;
 
       switch(lc_markstack[i].state) {
       case Unreached:
@@ -745,8 +625,8 @@
          }
 
          /* Possibly invalidate the malloc holding the end of this chunk. */
-         if (mc->size > 1) {
-            m = find_shadow_for(mc->data + (mc->size - 1), mallocs, n_mallocs);
+         if (mc->szB > 1) {
+            m = find_shadow_for(mc->data + (mc->szB - 1), mallocs, n_mallocs);
             if (m != -1 && malloc_chunk_holds_a_pool_chunk[m] == False) {
                tl_assert(*n_shadows > 0);
                --(*n_shadows);
@@ -814,7 +694,7 @@
 
    /* Sanity check -- make sure they don't overlap */
    for (i = 0; i < lc_n_shadows-1; i++) {
-      tl_assert( lc_shadows[i]->data + lc_shadows[i]->size
+      tl_assert( lc_shadows[i]->data + lc_shadows[i]->szB
                  <= lc_shadows[i+1]->data );
    }
 
@@ -834,7 +714,7 @@
 
    lc_min_mallocd_addr = lc_shadows[0]->data;
    lc_max_mallocd_addr = lc_shadows[lc_n_shadows-1]->data
-                         + lc_shadows[lc_n_shadows-1]->size;
+                         + lc_shadows[lc_n_shadows-1]->szB;
 
    lc_markstack = VG_(malloc)( lc_n_shadows * sizeof(*lc_markstack) );
    for (i = 0; i < lc_n_shadows; i++) {
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 0db5b62..b24c5e5 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -1138,9 +1138,10 @@
 // Forward declarations
 static void mc_record_address_error  ( ThreadId tid, Addr a,
                                        Int size, Bool isWrite );
-static void mc_record_core_mem_error ( ThreadId tid, Bool isUnaddr, Char* s );
-static void mc_record_param_error    ( ThreadId tid, Addr a, Bool isReg,
-                                       Bool isUnaddr, Char* msg );
+static void mc_record_core_mem_error ( ThreadId tid, Bool isAddrErr, Char* s );
+static void mc_record_regparam_error ( ThreadId tid, Char* msg );
+static void mc_record_memparam_error ( ThreadId tid, Addr a,
+                                       Bool isAddrErr, Char* msg );
 static void mc_record_jump_error     ( ThreadId tid, Addr a );
 
 static
@@ -1303,8 +1304,7 @@
    tl_assert(nBits == 64 || nBits == 32 || nBits == 16 || nBits == 8);
 
    /* Dump vbytes in memory, iterating from least to most significant
-      byte.  At the same time establish addressibility of the
-      location. */
+      byte.  At the same time establish addressibility of the location. */
    for (i = 0; i < szB; i++) {
       PROF_EVENT(36, "mc_STOREVn_slow(loop)");
       ai     = a + byte_offset_w(szB, bigendian, i);
@@ -2464,12 +2464,11 @@
    if (!ok) {
       switch (part) {
       case Vg_CoreSysCall:
-         mc_record_param_error ( tid, bad_addr, /*isReg*/False,
-                                    /*isUnaddr*/True, s );
+         mc_record_memparam_error ( tid, bad_addr, /*isAddrErr*/True, s );
          break;
 
       case Vg_CoreSignal:
-         mc_record_core_mem_error( tid, /*isUnaddr*/True, s );
+         mc_record_core_mem_error( tid, /*isAddrErr*/True, s );
          break;
 
       default:
@@ -2486,18 +2485,13 @@
    MC_ReadResult res = is_mem_defined ( base, size, &bad_addr );
 
    if (MC_Ok != res) {
-      Bool isUnaddr = ( MC_AddrErr == res ? True : False );
+      Bool isAddrErr = ( MC_AddrErr == res ? True : False );
 
       switch (part) {
       case Vg_CoreSysCall:
-         mc_record_param_error ( tid, bad_addr, /*isReg*/False,
-                                 isUnaddr, s );
+         mc_record_memparam_error ( tid, bad_addr, isAddrErr, s );
          break;
       
-      case Vg_CoreClientReq: // Kludge: make this a CoreMemErr
-         mc_record_core_mem_error( tid, isUnaddr, s );
-         break;
-
       /* If we're being asked to jump to a silly address, record an error 
          message before potentially crashing the entire system. */
       case Vg_CoreTranslate:
@@ -2520,8 +2514,8 @@
    tl_assert(part == Vg_CoreSysCall);
    res = mc_is_defined_asciiz ( (Addr)str, &bad_addr );
    if (MC_Ok != res) {
-      Bool isUnaddr = ( MC_AddrErr == res ? True : False );
-      mc_record_param_error ( tid, bad_addr, /*isReg*/False, isUnaddr, s );
+      Bool isAddrErr = ( MC_AddrErr == res ? True : False );
+      mc_record_memparam_error ( tid, bad_addr, isAddrErr, s );
    }
 }
 
@@ -2609,104 +2603,188 @@
    }
 
    if (bad)
-      mc_record_param_error ( tid, 0, /*isReg*/True, /*isUnaddr*/False, s );
+      mc_record_regparam_error ( tid, s );
 }
 
 
 /*------------------------------------------------------------*/
-/*--- Error and suppression types                          ---*/
+/*--- Error types                                          ---*/
 /*------------------------------------------------------------*/
 
+// Different kinds of blocks.
+typedef enum {
+   Block_Mallocd = 111,
+   Block_Freed,
+   Block_Mempool,
+   Block_MempoolChunk,
+   Block_UserG
+} BlockKind;
+
+/* ------------------ Addresses -------------------- */
+
 /* The classification of a faulting address. */
 typedef 
    enum { 
-      Undescribed,   // as-yet unclassified
-      Stack, 
-      Unknown,       // classification yielded nothing useful
-      Freed, Mallocd, 
-      UserG,         // in a user-defined block
-      Mempool,       // in a mempool
-      Register,      // in a register;  for Param errors only
+      Addr_Undescribed,   // as-yet unclassified
+      Addr_Unknown,       // classification yielded nothing useful
+      Addr_Stack,          
+      Addr_Block,
    }
-   AddrKind;
+   AddrTag;
 
-/* Records info about a faulting address. */
 typedef
-   struct {                   // Used by:
-      AddrKind akind;         //   ALL
-      SizeT blksize;          //   Freed, Mallocd
-      OffT rwoffset;          //   Freed, Mallocd
-      ExeContext* lastchange; //   Freed, Mallocd
-      ThreadId stack_tid;     //   Stack
-      const Char *desc;	      //   UserG
-      Bool maybe_gcc;         // True if just below %esp -- could be a gcc bug.
-   }
+   struct _AddrInfo
    AddrInfo;
 
-typedef 
-   enum { 
-      ParamSupp,     // Bad syscall params
-      UserSupp,      // Errors arising from client-request checks
-      CoreMemSupp,   // Memory errors in core (pthread ops, signal handling)
+struct _AddrInfo {
+   AddrTag tag;
+   union {
+      // As-yet unclassified.
+      struct { } Undescribed;
 
-      // Use of invalid values of given size (MemCheck only)
-      Value0Supp, Value1Supp, Value2Supp, Value4Supp, Value8Supp, Value16Supp,
+      // On a stack.
+      struct {
+         ThreadId tid;        // Which thread's stack?
+      } Stack;
 
-      // Invalid read/write attempt at given size
-      Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp,
+      // This covers heap blocks (normal and from mempools) and user-defined
+      // blocks.
+      struct {
+         BlockKind   block_kind;
+         Char*       block_desc;    // "block", "mempool" or user-defined
+         SizeT       block_szB;
+         OffT        rwoffset;
+         ExeContext* lastchange;
+      } Block;
 
-      FreeSupp,      // Invalid or mismatching free
-      OverlapSupp,   // Overlapping blocks in memcpy(), strcpy(), etc
-      LeakSupp,      // Something to be suppressed in a leak check.
-      MempoolSupp,   // Memory pool suppression.
-   } 
-   MC_SuppKind;
+      // Classification yielded nothing useful.
+      struct { } Unknown;
+
+   } Addr;
+};
+
+/* ------------------ Errors ----------------------- */
 
 /* What kind of error it is. */
 typedef 
-   enum { ValueErr,
-          CoreMemErr,   // Error in core op (pthread, signals) or client req
-          AddrErr, 
-          ParamErr, UserErr,  /* behaves like an anonymous ParamErr */
-          FreeErr, FreeMismatchErr,
-          OverlapErr,
-          LeakErr,
-          IllegalMempoolErr,
+   enum { 
+      Err_Value,
+      Err_Cond,
+      Err_CoreMem,
+      Err_Addr, 
+      Err_Jump, 
+      Err_RegParam,
+      Err_MemParam,
+      Err_User,
+      Err_Free,
+      Err_FreeMismatch,
+      Err_Overlap,
+      Err_Leak,
+      Err_IllegalMempool,
    }
-   MC_ErrorKind;
+   MC_ErrorTag;
 
-/* What kind of memory access is involved in the error? */
-typedef
-   enum { ReadAxs, WriteAxs, ExecAxs }
-   AxsKind;
 
-/* Extra context for memory errors */
-typedef
-   struct {                // Used by:
-      AxsKind axskind;     //   AddrErr
-      Int size;            //   AddrErr, ValueErr
-      AddrInfo addrinfo;   //   {Addr,Free,FreeMismatch,Param,User}Err
-      Bool isUnaddr;       //   {CoreMem,Param,User}Err
-   }
-   MC_Error;
+typedef struct _MC_Error MC_Error;
+
+struct _MC_Error {
+   // Nb: we don't need the tag here, as it's stored in the Error type! Yuk.
+   //MC_ErrorTag tag;
+
+   union {
+      // Use of an undefined value:
+      // - as a pointer in a load or store
+      // - as a jump target
+      struct {
+         SizeT szB;     // size of value in bytes
+      } Value;
+
+      // Use of an undefined value in a conditional branch or move.
+      struct {
+      } Cond;
+
+      // Addressability error in core (signal-handling) operation.
+      // It would be good to get rid of this error kind, merge it with
+      // another one somehow.
+      struct {
+      } CoreMem;
+
+      // Use of an unaddressable memory location in a load or store.
+      struct {
+         Bool     isWrite;    // read or write?
+         SizeT    szB;        // not used for exec (jump) errors
+         Bool     maybe_gcc;  // True if just below %esp -- could be a gcc bug
+         AddrInfo ai;
+      } Addr;
+
+      // Jump to an unaddressable memory location.
+      struct {
+         AddrInfo ai;
+      } Jump;
+
+      // System call register input contains undefined bytes.
+      struct {
+      } RegParam;
+
+      // System call memory input contains undefined/unaddressable bytes
+      struct {
+         Bool     isAddrErr;  // Addressability or definedness error?
+         AddrInfo ai;
+      } MemParam;
+
+      // Problem found from a client request like CHECK_MEM_IS_ADDRESSABLE.
+      struct {
+         Bool     isAddrErr;  // Addressability or definedness error?
+         AddrInfo ai;
+      } User;
+
+      // Program tried to free() something that's not a heap block (this
+      // covers double-frees). */
+      struct {
+         AddrInfo ai;
+      } Free;
+
+      // Program allocates heap block with one function
+      // (malloc/new/new[]/custom) and deallocates with not the matching one.
+      struct {
+         AddrInfo ai;
+      } FreeMismatch;
+
+      // Call to strcpy, memcpy, etc, with overlapping blocks.
+      struct {
+         Addr src;   // Source block
+         Addr dst;   // Destination block
+         Int  szB;   // Size in bytes;  0 if unused.
+      } Overlap;
+
+      // A memory leak.
+      struct {
+         UInt        n_this_record;
+         UInt        n_total_records;
+         LossRecord* lossRecord;
+      } Leak;
+
+      // A memory pool error.
+      struct {
+         AddrInfo ai;
+      } IllegalMempool;
+
+   } Err;
+};
+
 
 /*------------------------------------------------------------*/
 /*--- Printing errors                                      ---*/
 /*------------------------------------------------------------*/
 
-static void mc_pp_AddrInfo ( Addr a, AddrInfo* ai )
+static void mc_pp_AddrInfo ( Addr a, AddrInfo* ai, Bool maybe_gcc )
 {
    HChar* xpre  = VG_(clo_xml) ? "  <auxwhat>" : " ";
    HChar* xpost = VG_(clo_xml) ? "</auxwhat>"  : "";
 
-   switch (ai->akind) {
-      case Stack: 
-         VG_(message)(Vg_UserMsg, 
-                      "%sAddress 0x%llx is on thread %d's stack%s", 
-                      xpre, (ULong)a, ai->stack_tid, xpost);
-         break;
-      case Unknown:
-         if (ai->maybe_gcc) {
+   switch (ai->tag) {
+      case Addr_Unknown:
+         if (maybe_gcc) {
             VG_(message)(Vg_UserMsg, 
                "%sAddress 0x%llx is just below the stack ptr.  "
                "To suppress, use: --workaround-gcc296-bugs=yes%s",
@@ -2719,201 +2797,247 @@
                xpre, (ULong)a, xpost);
          }
          break;
-      case Freed: case Mallocd: case UserG: case Mempool: {
+
+      case Addr_Stack: 
+         VG_(message)(Vg_UserMsg, 
+                      "%sAddress 0x%llx is on thread %d's stack%s", 
+                      xpre, (ULong)a, ai->Addr.Stack.tid, xpost);
+         break;
+
+      case Addr_Block: {
+         SizeT block_szB  = ai->Addr.Block.block_szB;
+         OffT  rwoffset   = ai->Addr.Block.rwoffset;
          SizeT delta;
          const Char* relative;
-         const Char* kind;
-         if (ai->akind == Mempool) {
-            kind = "mempool";
-         } else {
-            kind = "block";
-         }
-	 if (ai->desc != NULL)
-	    kind = ai->desc;
 
-         if (ai->rwoffset < 0) {
-            delta    = (SizeT)(- ai->rwoffset);
+         if (rwoffset < 0) {
+            delta    = (SizeT)(-rwoffset);
             relative = "before";
-         } else if (ai->rwoffset >= ai->blksize) {
-            delta    = ai->rwoffset - ai->blksize;
+         } else if (rwoffset >= block_szB) {
+            delta    = rwoffset - block_szB;
             relative = "after";
          } else {
-            delta    = ai->rwoffset;
+            delta    = rwoffset;
             relative = "inside";
          }
          VG_(message)(Vg_UserMsg, 
             "%sAddress 0x%lx is %,lu bytes %s a %s of size %,lu %s%s",
             xpre,
-            a, delta, relative, kind,
-            ai->blksize,
-            ai->akind==Mallocd ? "alloc'd" 
-               : ai->akind==Freed ? "free'd" 
-                                  : "client-defined",
+            a, delta, relative, ai->Addr.Block.block_desc,
+            block_szB,
+            ai->Addr.Block.block_kind==Block_Mallocd ? "alloc'd" 
+            : ai->Addr.Block.block_kind==Block_Freed ? "free'd" 
+                                                     : "client-defined",
             xpost);
-         VG_(pp_ExeContext)(ai->lastchange);
+         VG_(pp_ExeContext)(ai->Addr.Block.lastchange);
          break;
       }
-      case Register:
-         // print nothing
-         tl_assert(0 == a);
-         break;
+
       default:
          VG_(tool_panic)("mc_pp_AddrInfo");
    }
 }
 
-static void mc_pp_Error ( Error* err )
+static const HChar* str_leak_lossmode ( Reachedness lossmode )
 {
-   MC_Error* err_extra = VG_(get_error_extra)(err);
+   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_leak_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;
+}
+
+static void mc_pp_msg( Char* xml_name, Error* err, const HChar* format, ... )
+{
    HChar* xpre  = VG_(clo_xml) ? "  <what>" : "";
    HChar* xpost = VG_(clo_xml) ? "</what>"  : "";
+   Char buf[256];
+   va_list vargs;
+
+   if (VG_(clo_xml))
+      VG_(message)(Vg_UserMsg, "  <kind>%s</kind>", xml_name);
+   // Stick xpre and xpost on the front and back of the format string.
+   VG_(snprintf)(buf, 256, "%s%s%s", xpre, format, xpost);
+   va_start(vargs, format);
+   VG_(vmessage) ( Vg_UserMsg, buf, vargs );
+   va_end(vargs);
+   VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+}
+
+static void mc_pp_Error ( Error* err )
+{
+   MC_Error* extra = VG_(get_error_extra)(err);
 
    switch (VG_(get_error_kind)(err)) {
-      case CoreMemErr: {
-         Char* s = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
-         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) );
+      case Err_CoreMem: {
+         /* What the hell *is* a CoreMemError? jrs 2005-May-18 */
+         /* As of 2006-Dec-14, it's caused by unaddressable bytes in a
+            signal handler frame.  --njn */
+         mc_pp_msg("CoreMemError", err,
+                   "%s contains unaddressable byte(s)", 
+                   VG_(get_error_string)(err));
          break;
-      
       } 
       
-      case ValueErr:
-         if (err_extra->size == 0) {
-            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);
+      case Err_Value:
+         mc_pp_msg("UninitValue", err,
+                   "Use of uninitialised value of size %d",
+                   extra->Err.Value.szB);
+         break;
+
+      case Err_Cond:
+         mc_pp_msg("UninitCondition", err,
+                   "Conditional jump or move depends"
+                   " on uninitialised value(s)");
+         break;
+
+      case Err_RegParam:
+         mc_pp_msg("SyscallParam", err,
+                   "Syscall param %s contains uninitialised byte(s)",
+                   VG_(get_error_string)(err));
+         break;
+
+      case Err_MemParam:
+         mc_pp_msg("SyscallParam", err,
+                   "Syscall param %s points to %s byte(s)",
+                   VG_(get_error_string)(err),
+                   ( extra->Err.MemParam.isAddrErr 
+                     ? "unaddressable" : "uninitialised" ));
+         mc_pp_AddrInfo(VG_(get_error_address)(err),
+                        &extra->Err.MemParam.ai, False);
+         break;
+
+      case Err_User:
+         mc_pp_msg("ClientCheck", err,
+                   "%s byte(s) found during client check request", 
+                   ( extra->Err.User.isAddrErr
+                     ? "Unaddressable" : "Uninitialised" ));
+         mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.User.ai,
+                        False);
+         break;
+
+      case Err_Free:
+         mc_pp_msg("InvalidFree", err,
+                   "Invalid free() / delete / delete[]");
+         mc_pp_AddrInfo(VG_(get_error_address)(err),
+                        &extra->Err.Free.ai, False);
+         break;
+
+      case Err_FreeMismatch:
+         mc_pp_msg("MismatchedFree", err,
+                   "Mismatched free() / delete / delete []");
+         mc_pp_AddrInfo(VG_(get_error_address)(err),
+                        &extra->Err.FreeMismatch.ai, False);
+         break;
+
+      case Err_Addr:
+         if (extra->Err.Addr.isWrite) {
+            mc_pp_msg("InvalidWrite", err,
+                      "Invalid write of size %d", 
+                      extra->Err.Addr.szB); 
          } else {
-            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);
+            mc_pp_msg("InvalidRead", err,
+                      "Invalid read of size %d", 
+                      extra->Err.Addr.szB); 
          }
-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+         mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Addr.ai,
+                        extra->Err.Addr.maybe_gcc);
          break;
 
-      case ParamErr: {
-         Bool isReg = ( Register == err_extra->addrinfo.akind );
-         Char* s1 = ( isReg ? "contains" : "points to" );
-         Char* s2 = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
-         if (isReg) tl_assert(!err_extra->isUnaddr);
-
-         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) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
-         break;
-      }
-      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%s byte(s) found during client check request%s", 
-            xpre, s, xpost);
-
-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
-         break;
-      }
-      case FreeErr:
-         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) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
+      case Err_Jump:
+         mc_pp_msg("InvalidJump", err,
+                   "Jump to the invalid address stated on the next line");
+         mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Jump.ai,
+                        False);
          break;
 
-      case FreeMismatchErr:
-         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) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
-         break;
-
-      case AddrErr:
-         switch (err_extra->axskind) {
-            case ReadAxs:
-               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:
-               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:
-               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)("mc_pp_Error(axskind)");
-         }
-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
-         break;
-
-      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,
-                         "%sSource and destination overlap in %s(%p, %p)%s",
-                         xpre,
-                         VG_(get_error_string)(err),
-                         ov_extra->dst, ov_extra->src,
-                         xpost);
+      case Err_Overlap:
+         if (extra->Err.Overlap.szB == 0)
+            mc_pp_msg("Overlap", err,
+                      "Source and destination overlap in %s(%p, %p)",
+                      VG_(get_error_string)(err),
+                      extra->Err.Overlap.dst, extra->Err.Overlap.src);
          else
-            VG_(message)(Vg_UserMsg,
-                         "%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,
-                         xpost);
-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+            mc_pp_msg("Overlap", err,
+                      "Source and destination overlap in %s(%p, %p, %d)",
+                      VG_(get_error_string)(err),
+                      extra->Err.Overlap.dst, extra->Err.Overlap.src,
+                      extra->Err.Overlap.szB);
          break;
-      }
-      case LeakErr: {
-         MC_(pp_LeakError)(err_extra);
-         break;
-      }
 
-      case IllegalMempoolErr:
-         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) );
-         mc_pp_AddrInfo(VG_(get_error_address)(err), &err_extra->addrinfo);
+      case Err_IllegalMempool:
+         mc_pp_msg("InvalidMemPool", err,
+                   "Illegal memory pool address");
+         mc_pp_AddrInfo(VG_(get_error_address)(err),
+                        &extra->Err.IllegalMempool.ai, False);
          break;
 
+      case Err_Leak: {
+         HChar*      xpre  = VG_(clo_xml) ? "  <what>" : "";
+         HChar*      xpost = VG_(clo_xml) ? "</what>"  : "";
+         UInt        n_this_record   = extra->Err.Leak.n_this_record;
+         UInt        n_total_records = extra->Err.Leak.n_total_records;
+         LossRecord* l               = extra->Err.Leak.lossRecord;
+
+         if (VG_(clo_xml)) {
+            VG_(message)(Vg_UserMsg, "  <kind>%t</kind>",
+                         xml_leak_kind(l->loss_mode));
+         } else {
+            VG_(message)(Vg_UserMsg, "");
+         }
+
+         if (l->indirect_bytes) {
+            VG_(message)(Vg_UserMsg, 
+               "%s%,lu (%,lu direct, %,lu indirect) bytes in %,u blocks"
+               " are %s in loss record %,u of %,u%s",
+               xpre,
+               l->total_bytes + l->indirect_bytes, 
+               l->total_bytes, l->indirect_bytes, l->num_blocks,
+               str_leak_lossmode(l->loss_mode), n_this_record, n_total_records,
+               xpost
+            );
+            if (VG_(clo_xml)) {
+               // Nb: don't put commas in these XML numbers 
+               VG_(message)(Vg_UserMsg, "  <leakedbytes>%lu</leakedbytes>", 
+                                        l->total_bytes + l->indirect_bytes);
+               VG_(message)(Vg_UserMsg, "  <leakedblocks>%u</leakedblocks>", 
+                                        l->num_blocks);
+            }
+         } else {
+            VG_(message)(
+               Vg_UserMsg, 
+               "%s%,lu bytes in %,u blocks are %s in loss record %,u of %,u%s",
+               xpre,
+               l->total_bytes, l->num_blocks,
+               str_leak_lossmode(l->loss_mode), n_this_record, 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);
+         break;
+      }
+
       default: 
          VG_(printf)("Error:\n  unknown Memcheck error code %d\n",
                      VG_(get_error_kind)(err));
@@ -2939,25 +3063,12 @@
       return False;
 }
 
-static void mc_clear_MC_Error ( MC_Error* err_extra )
-{
-   err_extra->axskind             = ReadAxs;
-   err_extra->size                = 0;
-   err_extra->isUnaddr            = True;
-   err_extra->addrinfo.akind      = Unknown;
-   err_extra->addrinfo.blksize    = 0;
-   err_extra->addrinfo.rwoffset   = 0;
-   err_extra->addrinfo.lastchange = NULL;
-   err_extra->addrinfo.stack_tid  = VG_INVALID_THREADID;
-   err_extra->addrinfo.maybe_gcc  = False;
-   err_extra->addrinfo.desc       = NULL;
-}
+/* --- Called from generated and non-generated code --- */
 
-/* This one called from generated code and non-generated code. */
-static void mc_record_address_error ( ThreadId tid, Addr a, Int size,
+static void mc_record_address_error ( ThreadId tid, Addr a, Int szB,
                                       Bool isWrite )
 {
-   MC_Error err_extra;
+   MC_Error extra;
    Bool     just_below_esp;
 
    if (in_ignored_range(a)) 
@@ -2970,14 +3081,14 @@
       best we can do is to 'act normal' and mark the A bits in the
       normal way as noaccess, but then hide any reads from that page
       that get reported here. */
-   if ((!isWrite) && a >= 0 && a < 4096 && a+size <= 4096) 
+   if ((!isWrite) && a >= 0 && a < 4096 && a+szB <= 4096) 
       return;
 
    /* Appalling AIX hack.  It suppresses reads done by glink
       fragments.  Getting rid of this would require figuring out
       somehow where the referenced data areas are (and their
       sizes). */
-   if ((!isWrite) && size == sizeof(Word)) { 
+   if ((!isWrite) && szB == sizeof(Word)) { 
       UInt i1, i2;
       UInt* pc = (UInt*)VG_(get_IP)(tid);
       if (sizeof(Word) == 4) {
@@ -2999,225 +3110,191 @@
    if (MC_(clo_workaround_gcc296_bugs) && just_below_esp)
       return;
 
-   mc_clear_MC_Error( &err_extra );
-   err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
-   err_extra.size    = size;
-   err_extra.addrinfo.akind     = Undescribed;
-   err_extra.addrinfo.maybe_gcc = just_below_esp;
-   VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
+   extra.Err.Addr.isWrite   = isWrite;
+   extra.Err.Addr.szB       = szB;
+   extra.Err.Addr.maybe_gcc = just_below_esp;
+   extra.Err.Addr.ai.tag    = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_Addr, a, /*s*/NULL, &extra );
 }
 
-/* These ones are called from non-generated code */
+static void mc_record_value_error ( ThreadId tid, Int szB )
+{
+   MC_Error extra;
+   tl_assert(MC_(clo_undef_value_errors));
+   extra.Err.Value.szB = szB;
+   VG_(maybe_record_error)( tid, Err_Value, /*addr*/0, /*s*/NULL, &extra );
+}
+
+static void mc_record_cond_error ( ThreadId tid )
+{
+   tl_assert(MC_(clo_undef_value_errors));
+   VG_(maybe_record_error)( tid, Err_Cond, /*addr*/0, /*s*/NULL, /*extra*/NULL);
+}
+
+/* --- Called from non-generated code --- */
 
 /* This is for memory errors in pthread functions, as opposed to pthread API
    errors which are found by the core. */
-static void mc_record_core_mem_error ( ThreadId tid, Bool isUnaddr, Char* msg )
+static void mc_record_core_mem_error ( ThreadId tid, Bool isAddrErr, Char* msg )
 {
-   MC_Error err_extra;
-
-   mc_clear_MC_Error( &err_extra );
-   err_extra.isUnaddr = isUnaddr;
-   VG_(maybe_record_error)( tid, CoreMemErr, /*addr*/0, msg, &err_extra );
+   VG_(maybe_record_error)( tid, Err_CoreMem, /*addr*/0, msg, /*extra*/NULL );
 }
 
-// Three kinds of param errors:
-// - register arg contains undefined bytes
-// - memory arg is unaddressable
-// - memory arg contains undefined bytes
-// 'isReg' and 'isUnaddr' dictate which of these it is.
-static void mc_record_param_error ( ThreadId tid, Addr a, Bool isReg,
-                                    Bool isUnaddr, Char* msg )
+static void mc_record_regparam_error ( ThreadId tid, Char* msg )
 {
-   MC_Error err_extra;
-
-   if (!isUnaddr) tl_assert(MC_(clo_undef_value_errors));
    tl_assert(VG_INVALID_THREADID != tid);
-   if (isUnaddr) tl_assert(!isReg);    // unaddressable register is impossible
-   mc_clear_MC_Error( &err_extra );
-   err_extra.addrinfo.akind = ( isReg ? Register : Undescribed );
-   err_extra.isUnaddr = isUnaddr;
-   VG_(maybe_record_error)( tid, ParamErr, a, msg, &err_extra );
+   VG_(maybe_record_error)( tid, Err_RegParam, /*addr*/0, msg, /*extra*/NULL );
+}
+
+static void mc_record_memparam_error ( ThreadId tid, Addr a, 
+                                       Bool isAddrErr, Char* msg )
+{
+   MC_Error extra;
+   tl_assert(VG_INVALID_THREADID != tid);
+   if (!isAddrErr) 
+      tl_assert(MC_(clo_undef_value_errors));
+   extra.Err.MemParam.isAddrErr = isAddrErr;
+   extra.Err.MemParam.ai.tag    = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_MemParam, a, msg, &extra );
 }
 
 static void mc_record_jump_error ( ThreadId tid, Addr a )
 {
-   MC_Error err_extra;
-
+   MC_Error extra;
    tl_assert(VG_INVALID_THREADID != tid);
-   mc_clear_MC_Error( &err_extra );
-   err_extra.axskind = ExecAxs;
-   err_extra.size    = 1;     // size only used for suppressions
-   err_extra.addrinfo.akind = Undescribed;
-   VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
+   extra.Err.Jump.ai.tag = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_Jump, a, /*s*/NULL, &extra );
 }
 
 void MC_(record_free_error) ( ThreadId tid, Addr a ) 
 {
-   MC_Error err_extra;
-
+   MC_Error extra;
    tl_assert(VG_INVALID_THREADID != tid);
-   mc_clear_MC_Error( &err_extra );
-   err_extra.addrinfo.akind = Undescribed;
-   VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra );
+   extra.Err.Free.ai.tag = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_Free, a, /*s*/NULL, &extra );
+}
+
+void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc )
+{
+   MC_Error extra;
+   AddrInfo* ai = &extra.Err.FreeMismatch.ai;
+   tl_assert(VG_INVALID_THREADID != tid);
+   ai->tag = Addr_Block;
+   ai->Addr.Block.block_kind = Block_Mallocd;  // Nb: Not 'Block_Freed'
+   ai->Addr.Block.block_desc = "block";
+   ai->Addr.Block.block_szB  = mc->szB;
+   ai->Addr.Block.rwoffset   = 0;
+   ai->Addr.Block.lastchange = mc->where;
+   VG_(maybe_record_error)( tid, Err_FreeMismatch, mc->data, /*s*/NULL,
+                            &extra );
 }
 
 void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) 
 {
-   MC_Error err_extra;
-
+   MC_Error extra;
    tl_assert(VG_INVALID_THREADID != tid);
-   mc_clear_MC_Error( &err_extra );
-   err_extra.addrinfo.akind = Undescribed;
-   VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra );
+   extra.Err.IllegalMempool.ai.tag = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_IllegalMempool, a, /*s*/NULL, &extra );
 }
 
-void MC_(record_freemismatch_error) ( ThreadId tid, Addr a, MC_Chunk* mc )
+static void mc_record_overlap_error ( ThreadId tid, Char* function,
+                                      Addr src, Addr dst, SizeT szB )
 {
-   MC_Error err_extra;
-   AddrInfo* ai;
-
+   MC_Error extra;
    tl_assert(VG_INVALID_THREADID != tid);
-   mc_clear_MC_Error( &err_extra );
-   ai = &err_extra.addrinfo;
-   ai->akind      = Mallocd;     // Nb: not 'Freed'
-   ai->blksize    = mc->size;
-   ai->rwoffset   = (Int)a - (Int)mc->data;
-   ai->lastchange = mc->where;
-   VG_(maybe_record_error)( tid, FreeMismatchErr, a, /*s*/NULL, &err_extra );
-}
-
-static void mc_record_overlap_error ( ThreadId tid, 
-                                      Char* function, OverlapExtra* ov_extra )
-{
+   extra.Err.Overlap.src = src;
+   extra.Err.Overlap.dst = dst;
+   extra.Err.Overlap.szB = szB;
    VG_(maybe_record_error)( 
-      tid, OverlapErr, /*addr*/0, /*s*/function, ov_extra );
+      tid, Err_Overlap, /*addr*/0, /*s*/function, &extra );
 }
 
-Bool MC_(record_leak_error) ( ThreadId tid, /*LeakExtra*/void* leak_extra,
-                              ExeContext* where, Bool print_record )
+Bool MC_(record_leak_error) ( ThreadId tid, UInt n_this_record,
+                              UInt n_total_records, LossRecord* lossRecord,
+                              Bool print_record )
 {
+   MC_Error extra;
+   extra.Err.Leak.n_this_record   = n_this_record;
+   extra.Err.Leak.n_total_records = n_total_records;
+   extra.Err.Leak.lossRecord      = lossRecord;
    return
-   VG_(unique_error) ( tid, LeakErr, /*Addr*/0, /*s*/NULL,
-                       /*extra*/leak_extra, where, print_record,
+   VG_(unique_error) ( tid, Err_Leak, /*Addr*/0, /*s*/NULL, &extra,
+                       lossRecord->allocated_at, print_record,
                        /*allow_GDB_attach*/False, /*count_error*/False );
 }
 
-
-/* Creates a copy of the 'extra' part, updates the copy with address info if
-   necessary, and returns the copy. */
-/* This one called from generated code and non-generated code. */
-static void mc_record_value_error ( ThreadId tid, Int size )
+static void mc_record_user_error ( ThreadId tid, Addr a, Bool isAddrErr )
 {
-   MC_Error err_extra;
-
-   tl_assert(MC_(clo_undef_value_errors));
-   mc_clear_MC_Error( &err_extra );
-   err_extra.size     = size;
-   err_extra.isUnaddr = False;
-   VG_(maybe_record_error)( tid, ValueErr, /*addr*/0, /*s*/NULL, &err_extra );
-}
-
-/* This called from non-generated code */
-
-static void mc_record_user_error ( ThreadId tid, Addr a, Bool isWrite,
-                                   Bool isUnaddr )
-{
-   MC_Error err_extra;
+   MC_Error extra;
 
    tl_assert(VG_INVALID_THREADID != tid);
-   mc_clear_MC_Error( &err_extra );
-   err_extra.addrinfo.akind = Undescribed;
-   err_extra.isUnaddr       = isUnaddr;
-   VG_(maybe_record_error)( tid, UserErr, a, /*s*/NULL, &err_extra );
+   extra.Err.User.isAddrErr = isAddrErr;
+   extra.Err.User.ai.tag    = Addr_Undescribed;
+   VG_(maybe_record_error)( tid, Err_User, a, /*s*/NULL, &extra );
 }
 
-__attribute__ ((unused))
-static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 )
-{
-   if (ai1->akind != Undescribed 
-       && ai2->akind != Undescribed
-       && ai1->akind != ai2->akind) 
-      return False;
-   if (ai1->akind == Freed || ai1->akind == Mallocd) {
-      if (ai1->blksize != ai2->blksize)
-         return False;
-      if (!VG_(eq_ExeContext)(res, ai1->lastchange, ai2->lastchange))
-         return False;
-   }
-   return True;
-}
+/*------------------------------------------------------------*/
+/*--- Other error operations                               ---*/
+/*------------------------------------------------------------*/
 
 /* Compare error contexts, to detect duplicates.  Note that if they
    are otherwise the same, the faulting addrs and associated rwoffsets
    are allowed to be different.  */
 static Bool mc_eq_Error ( VgRes res, Error* e1, Error* e2 )
 {
-   MC_Error* e1_extra = VG_(get_error_extra)(e1);
-   MC_Error* e2_extra = VG_(get_error_extra)(e2);
+   MC_Error* extra1 = VG_(get_error_extra)(e1);
+   MC_Error* extra2 = VG_(get_error_extra)(e2);
 
    /* Guaranteed by calling function */
    tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
    
    switch (VG_(get_error_kind)(e1)) {
-      case CoreMemErr: {
+      case Err_CoreMem: {
          Char *e1s, *e2s;
-         if (e1_extra->isUnaddr != e2_extra->isUnaddr) return False;
          e1s = VG_(get_error_string)(e1);
          e2s = VG_(get_error_string)(e2);
-         if (e1s == e2s)                               return True;
-         if (0 == VG_(strcmp)(e1s, e2s))               return True;
+         if (e1s == e2s)                   return True;
+         if (VG_STREQ(e1s, e2s))           return True;
          return False;
       }
 
+      case Err_RegParam:
+         return VG_STREQ(VG_(get_error_string)(e1), VG_(get_error_string)(e2));
+
       // Perhaps we should also check the addrinfo.akinds for equality.
       // That would result in more error reports, but only in cases where
       // a register contains uninitialised bytes and points to memory
       // containing uninitialised bytes.  Currently, the 2nd of those to be
       // detected won't be reported.  That is (nearly?) always the memory
       // error, which is good.
-      case ParamErr:
-         if (0 != VG_(strcmp)(VG_(get_error_string)(e1),
-                              VG_(get_error_string)(e2)))   return False;
+      case Err_MemParam:
+         if (!VG_STREQ(VG_(get_error_string)(e1),
+                       VG_(get_error_string)(e2))) return False;
          // fall through
-      case UserErr:
-         if (e1_extra->isUnaddr != e2_extra->isUnaddr)      return False;
+      case Err_User:
+         return ( extra1->Err.User.isAddrErr == extra2->Err.User.isAddrErr
+                ? True : False );
+
+      case Err_Free:
+      case Err_FreeMismatch:
+      case Err_Jump:
+      case Err_IllegalMempool:
+      case Err_Overlap:
+      case Err_Cond:
          return True;
 
-      case FreeErr:
-      case FreeMismatchErr:
-         /* JRS 2002-Aug-26: comparing addrs seems overkill and can
-            cause excessive duplication of errors.  Not even AddrErr
-            below does that.  So don't compare either the .addr field
-            or the .addrinfo fields. */
-         /* if (e1->addr != e2->addr) return False; */
-         /* if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) 
-               return False;
-         */
-         return True;
+      case Err_Addr:
+         return ( extra1->Err.Addr.szB == extra2->Err.Addr.szB
+                ? True : False );
 
-      case AddrErr:
-         /* if (e1_extra->axskind != e2_extra->axskind) return False; */
-         if (e1_extra->size != e2_extra->size) return False;
-         /*
-         if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) 
-            return False;
-         */
-         return True;
+      case Err_Value:
+         return ( extra1->Err.Value.szB == extra2->Err.Value.szB
+                ? True : False );
 
-      case ValueErr:
-         if (e1_extra->size != e2_extra->size) return False;
-         return True;
-
-      case OverlapErr:
-         return True;
-
-      case LeakErr:
-         VG_(tool_panic)("Shouldn't get LeakErr in mc_eq_Error,\n"
+      case Err_Leak:
+         VG_(tool_panic)("Shouldn't get Err_Leak in mc_eq_Error,\n"
                          "since it's handled with VG_(unique_error)()!");
 
-      case IllegalMempoolErr:
-         return True;
-
       default: 
          VG_(printf)("Error:\n  unknown error code %d\n",
                      VG_(get_error_kind)(e1));
@@ -3234,20 +3311,23 @@
    // in some cases this could result in an incorrect description (eg.
    // saying "12 bytes after block A" when really it's within block B.
    // Fixing would require adding redzone size to MC_Chunks, though.
-   return VG_(addr_is_in_block)( a, mc->data, mc->size,
+   return VG_(addr_is_in_block)( a, mc->data, mc->szB,
                                  MC_MALLOC_REDZONE_SZB );
 }
 
 // Forward declaration
 static Bool client_perm_maybe_describe( Addr a, AddrInfo* ai );
 
+
 /* Describe an address as best you can, for error messages,
    putting the result in ai. */
 static void describe_addr ( Addr a, AddrInfo* ai )
 {
    MC_Chunk* mc;
-   ThreadId   tid;
-   Addr       stack_min, stack_max;
+   ThreadId  tid;
+   Addr      stack_min, stack_max;
+
+   tl_assert(Addr_Undescribed == ai->tag);
 
    /* Perhaps it's a user-def'd block? */
    if (client_perm_maybe_describe( a, ai ))
@@ -3257,8 +3337,8 @@
    VG_(thread_stack_reset_iter)();
    while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
       if (stack_min <= a && a <= stack_max) {
-         ai->akind     = Stack;
-         ai->stack_tid = tid;
+         ai->tag            = Addr_Stack;
+         ai->Addr.Stack.tid = tid;
          return;
       }
    }
@@ -3266,10 +3346,12 @@
    mc = MC_(get_freed_list_head)();
    while (mc) {
       if (addr_is_in_MC_Chunk(mc, a)) {
-         ai->akind      = Freed;
-         ai->blksize    = mc->size;
-         ai->rwoffset   = (Int)a - (Int)mc->data;
-         ai->lastchange = mc->where;
+         ai->tag = Addr_Block;
+         ai->Addr.Block.block_kind = Block_Freed;
+         ai->Addr.Block.block_desc = "block";
+         ai->Addr.Block.block_szB  = mc->szB;
+         ai->Addr.Block.rwoffset   = (Int)a - (Int)mc->data;
+         ai->Addr.Block.lastchange = mc->where;
          return;
       }
       mc = mc->next; 
@@ -3278,71 +3360,72 @@
    VG_(HT_ResetIter)(MC_(malloc_list));
    while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) {
       if (addr_is_in_MC_Chunk(mc, a)) {
-         ai->akind      = Mallocd;
-         ai->blksize    = mc->size;
-         ai->rwoffset   = (Int)(a) - (Int)mc->data;
-         ai->lastchange = mc->where;
+         ai->tag = Addr_Block;
+         ai->Addr.Block.block_kind = Block_Mallocd;
+         ai->Addr.Block.block_desc = "block";
+         ai->Addr.Block.block_szB  = mc->szB;
+         ai->Addr.Block.rwoffset   = (Int)a - (Int)mc->data;
+         ai->Addr.Block.lastchange = mc->where;
          return;
       }
    }
    /* Clueless ... */
-   ai->akind = Unknown;
+   ai->tag = Addr_Unknown;
    return;
 }
 
 /* Updates the copy with address info if necessary (but not for all errors). */
 static UInt mc_update_extra( Error* err )
 {
+   MC_Error* extra = VG_(get_error_extra)(err);
+
    switch (VG_(get_error_kind)(err)) {
-   // These two don't have addresses associated with them, and so don't
+   // These ones don't have addresses associated with them, and so don't
    // need any updating.
-   case CoreMemErr:
-   case ValueErr: {
-      MC_Error* extra = VG_(get_error_extra)(err);
-      tl_assert(Unknown == extra->addrinfo.akind);
+   case Err_CoreMem:
+   case Err_Value:
+   case Err_Cond:
+   case Err_Overlap:
+   case Err_RegParam:
+   // For Err_Leaks the returned size does not matter -- they are always
+   // shown with VG_(unique_error)() so they 'extra' not copied.  But we make it
+   // consistent with the others.
+   case Err_Leak:
       return sizeof(MC_Error);
-   }
 
-   // ParamErrs sometimes involve a memory address; call describe_addr() in
-   // this case.
-   case ParamErr: {
-      MC_Error* extra = VG_(get_error_extra)(err);
-      tl_assert(Undescribed == extra->addrinfo.akind ||
-                Register    == extra->addrinfo.akind);
-      if (Undescribed == extra->addrinfo.akind)
-         describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
+   // These ones always involve a memory address.
+   case Err_Addr:
+      describe_addr ( VG_(get_error_address)(err), &extra->Err.Addr.ai );
       return sizeof(MC_Error);
-   }
-
-   // These four always involve a memory address.
-   case AddrErr: 
-   case UserErr:
-   case FreeErr:
-   case IllegalMempoolErr: {
-      MC_Error* extra = VG_(get_error_extra)(err);
-      tl_assert(Undescribed == extra->addrinfo.akind);
-      describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
+   case Err_MemParam:
+      describe_addr ( VG_(get_error_address)(err), &extra->Err.MemParam.ai );
       return sizeof(MC_Error);
-   }
+   case Err_Jump:
+      describe_addr ( VG_(get_error_address)(err), &extra->Err.Jump.ai );
+      return sizeof(MC_Error);
+   case Err_User:
+      describe_addr ( VG_(get_error_address)(err), &extra->Err.User.ai );
+      return sizeof(MC_Error);
+   case Err_Free:
+      describe_addr ( VG_(get_error_address)(err), &extra->Err.Free.ai );
+      return sizeof(MC_Error);
+   case Err_IllegalMempool:
+      describe_addr ( VG_(get_error_address)(err),
+                      &extra->Err.IllegalMempool.ai );
+      return sizeof(MC_Error);
 
-   // FreeMismatchErrs have already had their address described;  this is
+   // Err_FreeMismatches have already had their address described;  this is
    // possible because we have the MC_Chunk on hand when the error is
    // detected.  However, the address may be part of a user block, and if so
    // we override the pre-determined description with a user block one.
-   case FreeMismatchErr: {
-      MC_Error* extra = VG_(get_error_extra)(err);
-      tl_assert(extra && Mallocd == extra->addrinfo.akind);
+   case Err_FreeMismatch: {
+      tl_assert(extra && Block_Mallocd ==
+                extra->Err.FreeMismatch.ai.Addr.Block.block_kind);
       (void)client_perm_maybe_describe( VG_(get_error_address)(err), 
-                                        &(extra->addrinfo) );
+                                        &extra->Err.FreeMismatch.ai );
       return sizeof(MC_Error);
    }
 
-   // No memory address involved with these ones.  Nb:  for LeakErrs the
-   // returned size does not matter -- LeakErrs are always shown with
-   // VG_(unique_error)() so they're not copied.
-   case LeakErr:     return 0;
-   case OverlapErr:  return sizeof(OverlapExtra);
-
    default: VG_(tool_panic)("mc_update_extra: bad errkind");
    }
 }
@@ -3351,6 +3434,29 @@
 /*--- Suppressions                                         ---*/
 /*------------------------------------------------------------*/
 
+typedef 
+   enum { 
+      ParamSupp,     // Bad syscall params
+      UserSupp,      // Errors arising from client-request checks
+      CoreMemSupp,   // Memory errors in core (pthread ops, signal handling)
+
+      // Undefined value errors of given size
+      Value1Supp, Value2Supp, Value4Supp, Value8Supp, Value16Supp,
+
+      // Undefined value error in conditional.
+      CondSupp,
+
+      // Unaddressable read/write attempt at given size
+      Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp,
+
+      JumpSupp,      // Jump to unaddressable target
+      FreeSupp,      // Invalid or mismatching free
+      OverlapSupp,   // Overlapping blocks in memcpy(), strcpy(), etc
+      LeakSupp,      // Something to be suppressed in a leak check.
+      MempoolSupp,   // Memory pool suppression.
+   } 
+   MC_SuppKind;
+
 static Bool mc_recognised_suppression ( Char* name, Supp* su )
 {
    SuppKind skind;
@@ -3363,12 +3469,13 @@
    else if (VG_STREQ(name, "Addr4"))   skind = Addr4Supp;
    else if (VG_STREQ(name, "Addr8"))   skind = Addr8Supp;
    else if (VG_STREQ(name, "Addr16"))  skind = Addr16Supp;
+   else if (VG_STREQ(name, "Jump"))    skind = JumpSupp;
    else if (VG_STREQ(name, "Free"))    skind = FreeSupp;
    else if (VG_STREQ(name, "Leak"))    skind = LeakSupp;
    else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp;
    else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp;
-   else if (VG_STREQ(name, "Cond"))    skind = Value0Supp;
-   else if (VG_STREQ(name, "Value0"))  skind = Value0Supp;/* backwards compat */
+   else if (VG_STREQ(name, "Cond"))    skind = CondSupp;
+   else if (VG_STREQ(name, "Value0"))  skind = CondSupp; /* backwards compat */
    else if (VG_STREQ(name, "Value1"))  skind = Value1Supp;
    else if (VG_STREQ(name, "Value2"))  skind = Value2Supp;
    else if (VG_STREQ(name, "Value4"))  skind = Value4Supp;
@@ -3396,52 +3503,57 @@
 
 static Bool mc_error_matches_suppression(Error* err, Supp* su)
 {
-   Int        su_size;
-   MC_Error* err_extra = VG_(get_error_extra)(err);
-   ErrorKind  ekind     = VG_(get_error_kind )(err);
+   Int       su_szB;
+   MC_Error* extra = VG_(get_error_extra)(err);
+   ErrorKind ekind = VG_(get_error_kind )(err);
 
    switch (VG_(get_supp_kind)(su)) {
       case ParamSupp:
-         return (ekind == ParamErr 
+         return ((ekind == Err_RegParam || ekind == Err_MemParam)
               && VG_STREQ(VG_(get_error_string)(err), 
                           VG_(get_supp_string)(su)));
 
       case UserSupp:
-         return (ekind == UserErr);
+         return (ekind == Err_User);
 
       case CoreMemSupp:
-         return (ekind == CoreMemErr
+         return (ekind == Err_CoreMem
               && VG_STREQ(VG_(get_error_string)(err),
                           VG_(get_supp_string)(su)));
 
-      case Value0Supp: su_size = 0; goto value_case;
-      case Value1Supp: su_size = 1; goto value_case;
-      case Value2Supp: su_size = 2; goto value_case;
-      case Value4Supp: su_size = 4; goto value_case;
-      case Value8Supp: su_size = 8; goto value_case;
-      case Value16Supp:su_size =16; goto value_case;
+      case Value1Supp: su_szB = 1; goto value_case;
+      case Value2Supp: su_szB = 2; goto value_case;
+      case Value4Supp: su_szB = 4; goto value_case;
+      case Value8Supp: su_szB = 8; goto value_case;
+      case Value16Supp:su_szB =16; goto value_case;
       value_case:
-         return (ekind == ValueErr && err_extra->size == su_size);
+         return (ekind == Err_Value && extra->Err.Value.szB == su_szB);
 
-      case Addr1Supp: su_size = 1; goto addr_case;
-      case Addr2Supp: su_size = 2; goto addr_case;
-      case Addr4Supp: su_size = 4; goto addr_case;
-      case Addr8Supp: su_size = 8; goto addr_case;
-      case Addr16Supp:su_size =16; goto addr_case;
+      case CondSupp:
+         return (ekind == Err_Cond);
+
+      case Addr1Supp: su_szB = 1; goto addr_case;
+      case Addr2Supp: su_szB = 2; goto addr_case;
+      case Addr4Supp: su_szB = 4; goto addr_case;
+      case Addr8Supp: su_szB = 8; goto addr_case;
+      case Addr16Supp:su_szB =16; goto addr_case;
       addr_case:
-         return (ekind == AddrErr && err_extra->size == su_size);
+         return (ekind == Err_Addr && extra->Err.Addr.szB == su_szB);
+
+      case JumpSupp:
+         return (ekind == Err_Jump);
 
       case FreeSupp:
-         return (ekind == FreeErr || ekind == FreeMismatchErr);
+         return (ekind == Err_Free || ekind == Err_FreeMismatch);
 
       case OverlapSupp:
-         return (ekind == OverlapErr);
+         return (ekind == Err_Overlap);
 
       case LeakSupp:
-         return (ekind == LeakErr);
+         return (ekind == Err_Leak);
 
       case MempoolSupp:
-         return (ekind == IllegalMempoolErr);
+         return (ekind == Err_IllegalMempool);
 
       default:
          VG_(printf)("Error:\n"
@@ -3456,13 +3568,20 @@
 {
    Char* s;
    switch (VG_(get_error_kind)(err)) {
-   case ParamErr:           return "Param";
-   case UserErr:            return "User";
-   case FreeMismatchErr:    return "Free";
-   case IllegalMempoolErr:  return "Mempool";
-   case FreeErr:            return "Free";
-   case AddrErr:            
-      switch ( ((MC_Error*)VG_(get_error_extra)(err))->size ) {
+   case Err_RegParam:       return "Param";
+   case Err_MemParam:       return "Param";
+   case Err_User:           return "User";
+   case Err_FreeMismatch:   return "Free";
+   case Err_IllegalMempool: return "Mempool";
+   case Err_Free:           return "Free";
+   case Err_Jump:           return "Jump";
+   case Err_CoreMem:        return "CoreMem";
+   case Err_Overlap:        return "Overlap";
+   case Err_Leak:           return "Leak";
+   case Err_Cond:           return "Cond";
+   case Err_Addr: {
+      MC_Error* extra = VG_(get_error_extra)(err);
+      switch ( extra->Err.Addr.szB ) {
       case 1:               return "Addr1";
       case 2:               return "Addr2";
       case 4:               return "Addr4";
@@ -3470,10 +3589,10 @@
       case 16:              return "Addr16";
       default:              VG_(tool_panic)("unexpected size for Addr");
       }
-     
-   case ValueErr:
-      switch ( ((MC_Error*)VG_(get_error_extra)(err))->size ) {
-      case 0:               return "Cond";
+   }
+   case Err_Value: {
+      MC_Error* extra = VG_(get_error_extra)(err);
+      switch ( extra->Err.Value.szB ) {
       case 1:               return "Value1";
       case 2:               return "Value2";
       case 4:               return "Value4";
@@ -3481,9 +3600,7 @@
       case 16:              return "Value16";
       default:              VG_(tool_panic)("unexpected size for Value");
       }
-   case CoreMemErr:         return "CoreMem";
-   case OverlapErr:         return "Overlap";
-   case LeakErr:            return "Leak";
+   }
    default:                 VG_(tool_panic)("get_error_name: unexpected type");
    }
    VG_(printf)(s);
@@ -3491,7 +3608,8 @@
 
 static void mc_print_extra_suppression_info ( Error* err )
 {
-   if (ParamErr == VG_(get_error_kind)(err)) {
+   ErrorKind ekind = VG_(get_error_kind )(err);
+   if (Err_RegParam == ekind || Err_MemParam == ekind) {
       VG_(printf)("   %s\n", VG_(get_error_string)(err));
    }
 }
@@ -3785,7 +3903,6 @@
    // Handle common case quickly: a is suitably aligned, is mapped, and is
    // addressible.
    // Convert V bits from compact memory form to expanded register form
-   // XXX: set the high 16/48 bits of retval to 1 for 64-bit paranoia?
    if      (vabits8 == VA_BITS8_DEFINED  ) { return V_BITS16_DEFINED;   }
    else if (vabits8 == VA_BITS8_UNDEFINED) { return V_BITS16_UNDEFINED; }
    else {
@@ -3894,7 +4011,6 @@
    // Convert V bits from compact memory form to expanded register form
    // Handle common case quickly: a is mapped, and the entire
    // word32 it lives in is addressible.
-   // XXX: set the high 24/56 bits of retval to 1 for 64-bit paranoia?
    if      (vabits8 == VA_BITS8_DEFINED  ) { return V_BITS8_DEFINED;   }
    else if (vabits8 == VA_BITS8_UNDEFINED) { return V_BITS8_UNDEFINED; }
    else {
@@ -3971,7 +4087,7 @@
 
 void MC_(helperc_value_check0_fail) ( void )
 {
-   mc_record_value_error ( VG_(get_running_tid)(), 0 );
+   mc_record_cond_error ( VG_(get_running_tid)() );
 }
 
 void MC_(helperc_value_check1_fail) ( void )
@@ -4005,6 +4121,9 @@
 
 /* Copy Vbits from/to address 'a'. Returns: 1 == OK, 2 == alignment
    error [no longer used], 3 == addressing error. */
+/* Nb: We used to issue various definedness/addressability errors from here,
+   but we took them out because they ranged from not-very-helpful to
+   downright annoying, and they complicated the error data structures. */
 static Int mc_get_or_set_vbits_for_client ( 
    ThreadId tid,
    Addr a, 
@@ -4019,24 +4138,14 @@
 
    /* Check that arrays are addressible before doing any getting/setting. */
    for (i = 0; i < szB; i++) {
-      if (VA_BITS2_NOACCESS == get_vabits2(a + i)) {
-         mc_record_address_error( tid, a + i,     1, setting ? True : False );
-         return 3;
-      }
-      if (VA_BITS2_NOACCESS == get_vabits2(vbits + i)) {
-         mc_record_address_error( tid, vbits + i, 1, setting ? False : True );
+      if (VA_BITS2_NOACCESS == get_vabits2(a + i) ||
+          VA_BITS2_NOACCESS == get_vabits2(vbits + i)) {
          return 3;
       }
    }
 
    /* Do the copy */
    if (setting) {
-
-      // It's actually a tool ClientReq, but Vg_CoreClientReq is the closest
-      // thing we have.
-      check_mem_is_defined(Vg_CoreClientReq, tid, "SET_VBITS(vbits)",
-                       vbits, szB);
-      
       /* setting */
       for (i = 0; i < szB; i++) {
          ok = set_vbits8(a + i, ((UChar*)vbits)[i]);
@@ -4047,9 +4156,6 @@
       for (i = 0; i < szB; i++) {
          ok = get_vbits8(a + i, &vbits8);
          tl_assert(ok);
-// XXX: used to do this, but it's a pain
-//         if (V_BITS8_DEFINED != vbits8)
-//            mc_record_value_error(tid, 1);
          ((UChar*)vbits)[i] = vbits8;
       }
       // The bytes in vbits[] have now been set, so mark them as such.
@@ -4439,7 +4545,6 @@
 static Bool client_perm_maybe_describe( Addr a, AddrInfo* ai )
 {
    UInt i;
-   /* VG_(printf)("try to identify %d\n", a); */
 
    /* Perhaps it's a general block ? */
    for (i = 0; i < cgb_used; i++) {
@@ -4456,25 +4561,30 @@
                VG_(HT_ResetIter)(mp->chunks);
                while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
                   if (addr_is_in_MC_Chunk(mc, a)) {
-                     ai->akind      = UserG;
-                     ai->blksize    = mc->size;
-                     ai->rwoffset   = (Int)(a) - (Int)mc->data;
-                     ai->lastchange = mc->where;
+                     ai->tag = Addr_Block;
+                     ai->Addr.Block.block_kind = Block_MempoolChunk;
+                     ai->Addr.Block.block_desc = "block";
+                     ai->Addr.Block.block_szB  = mc->szB;
+                     ai->Addr.Block.rwoffset   = (Int)a - (Int)mc->data;
+                     ai->Addr.Block.lastchange = mc->where;
                      return True;
                   }
                }
             }
-            ai->akind      = Mempool;
-            ai->blksize    = cgbs[i].size;
-            ai->rwoffset   = (Int)(a) - (Int)(cgbs[i].start);
-            ai->lastchange = cgbs[i].where;
+            ai->tag = Addr_Block;
+            ai->Addr.Block.block_kind = Block_Mempool;
+            ai->Addr.Block.block_desc = "mempool";
+            ai->Addr.Block.block_szB  = cgbs[i].size;
+            ai->Addr.Block.rwoffset   = (Int)(a) - (Int)(cgbs[i].start);
+            ai->Addr.Block.lastchange = cgbs[i].where;
             return True;
          }
-         ai->akind      = UserG;
-         ai->blksize    = cgbs[i].size;
-         ai->rwoffset   = (Int)(a) - (Int)(cgbs[i].start);
-         ai->lastchange = cgbs[i].where;
-         ai->desc       = cgbs[i].desc;
+         ai->tag = Addr_Block;
+         ai->Addr.Block.block_kind = Block_UserG;
+         ai->Addr.Block.block_desc = cgbs[i].desc;
+         ai->Addr.Block.block_szB  = cgbs[i].size;
+         ai->Addr.Block.rwoffset   = (Int)(a) - (Int)(cgbs[i].start);
+         ai->Addr.Block.lastchange = cgbs[i].where;
          return True;
       }
    }
@@ -4504,8 +4614,7 @@
       case VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE:
          ok = is_mem_addressable ( arg[1], arg[2], &bad_addr );
          if (!ok)
-            mc_record_user_error ( tid, bad_addr, /*isWrite*/True,
-                                   /*isUnaddr*/True );
+            mc_record_user_error ( tid, bad_addr, /*isAddrErr*/True );
          *ret = ok ? (UWord)NULL : bad_addr;
          break;
 
@@ -4513,11 +4622,9 @@
          MC_ReadResult res;
          res = is_mem_defined ( arg[1], arg[2], &bad_addr );
          if (MC_AddrErr == res)
-            mc_record_user_error ( tid, bad_addr, /*isWrite*/False,
-                                   /*isUnaddr*/True );
+            mc_record_user_error ( tid, bad_addr, /*isAddrErr*/True );
          else if (MC_ValueErr == res)
-            mc_record_user_error ( tid, bad_addr, /*isWrite*/False,
-                                   /*isUnaddr*/False );
+            mc_record_user_error ( tid, bad_addr, /*isAddrErr*/False );
          *ret = ( res==MC_Ok ? (UWord)NULL : bad_addr );
          break;
       }
@@ -4576,17 +4683,11 @@
          break;
 
       case VG_USERREQ__GET_VBITS:
-         /* Returns: 1 == OK, 2 == alignment error, 3 == addressing
-            error. */
-         /* VG_(printf)("get_vbits %p %p %d\n", arg[1], arg[2], arg[3] ); */
          *ret = mc_get_or_set_vbits_for_client
                    ( tid, arg[1], arg[2], arg[3], False /* get them */ );
          break;
 
       case VG_USERREQ__SET_VBITS:
-         /* Returns: 1 == OK, 2 == alignment error, 3 == addressing
-            error. */
-         /* VG_(printf)("set_vbits %p %p %d\n", arg[1], arg[2], arg[3] ); */
          *ret = mc_get_or_set_vbits_for_client
                    ( tid, arg[1], arg[2], arg[3], True /* set them */ );
          break;
@@ -4624,9 +4725,11 @@
       }
 
       case _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR: {
-         Char*         s     = (Char*)        arg[1];
-         OverlapExtra* extra = (OverlapExtra*)arg[2];
-         mc_record_overlap_error(tid, s, extra);
+         Char* s   = (Char*)arg[1];
+         Addr  dst = (Addr) arg[2];
+         Addr  src = (Addr) arg[3];
+         SizeT len = (SizeT)arg[4];
+         mc_record_overlap_error(tid, s, src, dst, len);
          return True;
       }
 
diff --git a/memcheck/mc_malloc_wrappers.c b/memcheck/mc_malloc_wrappers.c
index 8e24d11..db407e9 100644
--- a/memcheck/mc_malloc_wrappers.c
+++ b/memcheck/mc_malloc_wrappers.c
@@ -82,12 +82,12 @@
    if (freed_list_end == NULL) {
       tl_assert(freed_list_start == NULL);
       freed_list_end    = freed_list_start = mc;
-      freed_list_volume = mc->size;
+      freed_list_volume = mc->szB;
    } else {
       tl_assert(freed_list_end->next == NULL);
       freed_list_end->next = mc;
       freed_list_end       = mc;
-      freed_list_volume += mc->size;
+      freed_list_volume += mc->szB;
    }
    mc->next = NULL;
 
@@ -101,7 +101,7 @@
       tl_assert(freed_list_end != NULL);
 
       mc1 = freed_list_start;
-      freed_list_volume -= mc1->size;
+      freed_list_volume -= mc1->szB;
       /* VG_(printf)("volume now %d\n", freed_list_volume); */
       tl_assert(freed_list_volume >= 0);
 
@@ -125,12 +125,12 @@
 
 /* Allocate its shadow chunk, put it on the appropriate list. */
 static
-MC_Chunk* create_MC_Chunk ( ThreadId tid, Addr p, SizeT size,
+MC_Chunk* create_MC_Chunk ( ThreadId tid, Addr p, SizeT szB,
                             MC_AllocKind kind)
 {
    MC_Chunk* mc  = VG_(malloc)(sizeof(MC_Chunk));
    mc->data      = p;
-   mc->size      = size;
+   mc->szB       = szB;
    mc->allockind = kind;
    mc->where     = VG_(record_ExeContext)(tid);
 
@@ -174,7 +174,7 @@
 /* Allocate memory and note change in memory available */
 __inline__
 void* MC_(new_block) ( ThreadId tid,
-                        Addr p, SizeT size, SizeT align, UInt rzB,
+                        Addr p, SizeT szB, SizeT alignB, UInt rzB,
                         Bool is_zeroed, MC_AllocKind kind, VgHashTable table)
 {
    cmalloc_n_mallocs ++;
@@ -184,22 +184,22 @@
       tl_assert(MC_AllocCustom == kind);
    } else {
       tl_assert(MC_AllocCustom != kind);
-      p = (Addr)VG_(cli_malloc)( align, size );
+      p = (Addr)VG_(cli_malloc)( alignB, szB );
       if (!p) {
          return NULL;
       }
-      if (is_zeroed) VG_(memset)((void*)p, 0, size);
+      if (is_zeroed) VG_(memset)((void*)p, 0, szB);
    }
 
    // Only update this stat if allocation succeeded.
-   cmalloc_bs_mallocd += size;
+   cmalloc_bs_mallocd += szB;
 
-   VG_(HT_add_node)( table, create_MC_Chunk(tid, p, size, kind) );
+   VG_(HT_add_node)( table, create_MC_Chunk(tid, p, szB, kind) );
 
    if (is_zeroed)
-      MC_(make_mem_defined)( p, size );
+      MC_(make_mem_defined)( p, szB );
    else
-      MC_(make_mem_undefined)( p, size );
+      MC_(make_mem_undefined)( p, szB );
 
    return (void*)p;
 }
@@ -237,12 +237,12 @@
    }
 }
 
-void* MC_(memalign) ( ThreadId tid, SizeT align, SizeT n )
+void* MC_(memalign) ( ThreadId tid, SizeT alignB, SizeT n )
 {
    if (complain_about_silly_args(n, "memalign")) {
       return NULL;
    } else {
-      return MC_(new_block) ( tid, 0, n, align, 
+      return MC_(new_block) ( tid, 0, n, alignB, 
          MC_MALLOC_REDZONE_SZB, /*is_zeroed*/False, MC_AllocMalloc,
          MC_(malloc_list));
    }
@@ -264,7 +264,7 @@
 {
    /* Note: make redzones noaccess again -- just in case user made them
       accessible with a client request... */
-   MC_(make_mem_noaccess)( mc->data-rzB, mc->size + 2*rzB );
+   MC_(make_mem_noaccess)( mc->data-rzB, mc->szB + 2*rzB );
 
    /* Put it out of harm's way for a while, if not from a client request */
    if (MC_AllocCustom != mc->allockind) {
@@ -289,7 +289,8 @@
    } else {
       /* check if it is a matching free() / delete / delete [] */
       if (kind != mc->allockind) {
-         MC_(record_freemismatch_error) ( tid, p, mc );
+         tl_assert(p == mc->data);
+         MC_(record_freemismatch_error) ( tid, mc );
       }
       die_and_free_mem ( tid, mc, rzB );
    }
@@ -313,17 +314,17 @@
       tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocNewVec);
 }
 
-void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_size )
+void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB )
 {
    MC_Chunk* mc;
    void*     p_new;
-   SizeT     old_size;
+   SizeT     old_szB;
 
    cmalloc_n_frees ++;
    cmalloc_n_mallocs ++;
-   cmalloc_bs_mallocd += new_size;
+   cmalloc_bs_mallocd += new_szB;
 
-   if (complain_about_silly_args(new_size, "realloc")) 
+   if (complain_about_silly_args(new_szB, "realloc")) 
       return NULL;
 
    /* Remove the old block */
@@ -337,38 +338,39 @@
    /* check if its a matching free() / delete / delete [] */
    if (MC_AllocMalloc != mc->allockind) {
       /* can not realloc a range that was allocated with new or new [] */
-      MC_(record_freemismatch_error) ( tid, (Addr)p_old, mc );
+      tl_assert((Addr)p_old == mc->data);
+      MC_(record_freemismatch_error) ( tid, mc );
       /* but keep going anyway */
    }
 
-   old_size = mc->size;
+   old_szB = mc->szB;
 
-   if (old_size == new_size) {
+   if (old_szB == new_szB) {
       /* size unchanged */
       mc->where = VG_(record_ExeContext)(tid);
       p_new = p_old;
       
-   } else if (old_size > new_size) {
+   } else if (old_szB > new_szB) {
       /* new size is smaller */
-      MC_(make_mem_noaccess)( mc->data+new_size, mc->size-new_size );
-      mc->size = new_size;
+      MC_(make_mem_noaccess)( mc->data+new_szB, mc->szB-new_szB );
+      mc->szB = new_szB;
       mc->where = VG_(record_ExeContext)(tid);
       p_new = p_old;
 
    } else {
       /* new size is bigger */
       /* Get new memory */
-      Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size);
+      Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
 
       if (a_new) {
          /* First half kept and copied, second half new, red zones as normal */
          MC_(make_mem_noaccess)( a_new-MC_MALLOC_REDZONE_SZB, MC_MALLOC_REDZONE_SZB );
-         MC_(copy_address_range_state)( (Addr)p_old, a_new, mc->size );
-         MC_(make_mem_undefined)( a_new+mc->size, new_size-mc->size );
-         MC_(make_mem_noaccess) ( a_new+new_size, MC_MALLOC_REDZONE_SZB );
+         MC_(copy_address_range_state)( (Addr)p_old, a_new, mc->szB );
+         MC_(make_mem_undefined)( a_new+mc->szB, new_szB-mc->szB );
+         MC_(make_mem_noaccess) ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
 
          /* Copy from old to new */
-         VG_(memcpy)((void*)a_new, p_old, mc->size);
+         VG_(memcpy)((void*)a_new, p_old, mc->szB);
 
          /* Free old memory */
          /* Nb: we have to allocate a new MC_Chunk for the new memory rather
@@ -377,7 +379,7 @@
          die_and_free_mem ( tid, mc, MC_MALLOC_REDZONE_SZB );
 
          // Allocate a new chunk.
-         mc = create_MC_Chunk( tid, a_new, new_size, MC_AllocMalloc );
+         mc = create_MC_Chunk( tid, a_new, new_szB, MC_AllocMalloc );
       }
 
       p_new = (void*)a_new;
@@ -453,7 +455,7 @@
    while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
       /* Note: make redzones noaccess again -- just in case user made them
          accessible with a client request... */
-      MC_(make_mem_noaccess)(mc->data-mp->rzB, mc->size + 2*mp->rzB );
+      MC_(make_mem_noaccess)(mc->data-mp->rzB, mc->szB + 2*mp->rzB );
    }
    // Destroy the chunk table
    VG_(HT_destruct)(mp->chunks);
@@ -517,7 +519,7 @@
    
    /* Sanity check -- make sure they don't overlap */
    for (i = 0; i < n_chunks-1; i++) {
-      if (chunks[i]->data + chunks[i]->size > chunks[i+1]->data ) {
+      if (chunks[i]->data + chunks[i]->szB > chunks[i+1]->data ) {
          VG_(message)(Vg_UserMsg, 
                       "Mempool chunk %d / %d overlaps with its successor", 
                       i+1, n_chunks);
@@ -534,9 +536,9 @@
                          "Mempool chunk %d / %d: %d bytes [%x,%x), allocated:",
                          i+1, 
                          n_chunks, 
-                         chunks[i]->size, 
+                         chunks[i]->szB, 
                          chunks[i]->data, 
-                         chunks[i]->data + chunks[i]->size);
+                         chunks[i]->data + chunks[i]->szB);
 
             VG_(pp_ExeContext)(chunks[i]->where);
          }
@@ -544,12 +546,12 @@
    VG_(free)(chunks);
 }
 
-void MC_(mempool_alloc)(ThreadId tid, Addr pool, Addr addr, SizeT size)
+void MC_(mempool_alloc)(ThreadId tid, Addr pool, Addr addr, SizeT szB)
 {
    MC_Mempool* mp;
 
    if (VG_(clo_verbosity) > 2) {     
-      VG_(message)(Vg_UserMsg, "mempool_alloc(%p, %p, %d)", pool, addr, size);
+      VG_(message)(Vg_UserMsg, "mempool_alloc(%p, %p, %d)", pool, addr, szB);
       VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
    }
 
@@ -558,7 +560,7 @@
       MC_(record_illegal_mempool_error) ( tid, pool );
    } else {
       check_mempool_sane(mp);
-      MC_(new_block)(tid, addr, size, /*ignored*/0, mp->rzB, mp->is_zeroed,
+      MC_(new_block)(tid, addr, szB, /*ignored*/0, mp->rzB, mp->is_zeroed,
                      MC_AllocCustom, mp->chunks);
       check_mempool_sane(mp);
    }
@@ -591,7 +593,7 @@
    if (VG_(clo_verbosity) > 2) {
       VG_(message)(Vg_UserMsg, 
 		   "mempool_free(%p, %p) freed chunk of %d bytes", 
-		   pool, addr, mc->size);
+		   pool, addr, mc->szB);
    }
 
    die_and_free_mem ( tid, mc, mp->rzB );
@@ -599,7 +601,7 @@
 }
 
 
-void MC_(mempool_trim)(Addr pool, Addr addr, SizeT size)
+void MC_(mempool_trim)(Addr pool, Addr addr, SizeT szB)
 {
    MC_Mempool*  mp;
    MC_Chunk*    mc;
@@ -608,7 +610,7 @@
    VgHashNode** chunks;
 
    if (VG_(clo_verbosity) > 2) {
-      VG_(message)(Vg_UserMsg, "mempool_trim(%p, %p, %d)", pool, addr, size);
+      VG_(message)(Vg_UserMsg, "mempool_trim(%p, %p, %d)", pool, addr, szB);
       VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
    }
 
@@ -633,9 +635,9 @@
       mc = (MC_Chunk*) chunks[i];
 
       lo = mc->data;
-      hi = mc->size == 0 ? mc->data : mc->data + mc->size - 1;
+      hi = mc->szB == 0 ? mc->data : mc->data + mc->szB - 1;
 
-#define EXTENT_CONTAINS(x) ((addr <= (x)) && ((x) < addr + size))
+#define EXTENT_CONTAINS(x) ((addr <= (x)) && ((x) < addr + szB))
 
       if (EXTENT_CONTAINS(lo) && EXTENT_CONTAINS(hi)) {
 
@@ -680,12 +682,12 @@
            lo = mc->data;
          }
 
-         if (mc->data + size > addr + size) {
-           max = mc->data + size;
-           hi = addr + size;
+         if (mc->data + szB > addr + szB) {
+           max = mc->data + szB;
+           hi = addr + szB;
          } else {
-           max = addr + size;
-           hi = mc->data + size;
+           max = addr + szB;
+           hi = mc->data + szB;
          }
 
          tl_assert(min <= lo);
@@ -701,7 +703,7 @@
          }
 
          mc->data = lo;
-         mc->size = (UInt) (hi - lo);
+         mc->szB = (UInt) (hi - lo);
          VG_(HT_add_node)( mp->chunks, mc );        
       }
 
@@ -734,7 +736,7 @@
    VG_(HT_add_node)( MC_(mempool_list), mp );
 }
 
-void MC_(mempool_change)(Addr pool, Addr addrA, Addr addrB, SizeT size)
+void MC_(mempool_change)(Addr pool, Addr addrA, Addr addrB, SizeT szB)
 {
    MC_Mempool*  mp;
    MC_Chunk*    mc;
@@ -742,7 +744,7 @@
 
    if (VG_(clo_verbosity) > 2) {
       VG_(message)(Vg_UserMsg, "mempool_change(%p, %p, %p, %d)", 
-                   pool, addrA, addrB, size);
+                   pool, addrA, addrB, szB);
       VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
    }
 
@@ -761,7 +763,7 @@
    }
 
    mc->data = addrB;
-   mc->size = size;
+   mc->szB  = szB;
    VG_(HT_add_node)( mp->chunks, mc );
 
    check_mempool_sane(mp);
@@ -798,7 +800,7 @@
    VG_(HT_ResetIter)(MC_(malloc_list));
    while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) {
       nblocks++;
-      nbytes += mc->size;
+      nbytes += mc->szB;
    }
 
    VG_(message)(Vg_UserMsg, 
diff --git a/memcheck/mc_replace_strmem.c b/memcheck/mc_replace_strmem.c
index b3d2997..a0eed00 100644
--- a/memcheck/mc_replace_strmem.c
+++ b/memcheck/mc_replace_strmem.c
@@ -89,31 +89,12 @@
 
 // This is a macro rather than a function because we don't want to have an
 // extra function in the stack trace.
-#define RECORD_OVERLAP_ERROR(s, p_extra) \
+#define RECORD_OVERLAP_ERROR(s, src, dst, len) \
 { \
    Word unused_res; \
    VALGRIND_DO_CLIENT_REQUEST(unused_res, 0, \
 			      _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR, \
-			      s, p_extra, 0, 0, 0); \
-}
-
-static __inline__
-void complain2 ( Char* s, char* dst, const char* src )
-{
-   OverlapExtra extra = {
-      .src = (Addr)src, .dst = (Addr)dst, .len = -1,
-   };
-   RECORD_OVERLAP_ERROR( s, &extra );
-}
-
-static __inline__
-void complain3 ( Char* s, void* dst, const void* src, int n )
-{
-   /* Must wrap it up here, because we cannot pass 4 args to core */
-   OverlapExtra extra = {
-      .src = (Addr)src, .dst = (Addr)dst, .len = n,
-   };
-   RECORD_OVERLAP_ERROR( s, &extra );
+			      s, src, dst, len, 0); \
 }
 
 /* --------- Some handy Z-encoded names. --------- */
@@ -197,7 +178,7 @@
                      src_orig,  \
                      (Addr)dst-(Addr)dst_orig+1,  \
                      (Addr)src-(Addr)src_orig+1)) \
-         complain2("strcat", dst_orig, src_orig); \
+         RECORD_OVERLAP_ERROR("strcat", dst_orig, src_orig, 0); \
  \
       return dst_orig; \
    }
@@ -225,7 +206,7 @@
                      src_orig,  \
                      (Addr)dst-(Addr)dst_orig+1,  \
                      (Addr)src-(Addr)src_orig+1)) \
-         complain3("strncat", dst_orig, src_orig, n); \
+         RECORD_OVERLAP_ERROR("strncat", dst_orig, src_orig, n); \
  \
       return dst_orig; \
    }
@@ -279,7 +260,7 @@
                      src_orig,  \
                      (Addr)dst-(Addr)dst_orig+1,  \
                      (Addr)src-(Addr)src_orig+1)) \
-         complain2("strcpy", dst_orig, src_orig); \
+         RECORD_OVERLAP_ERROR("strcpy", dst_orig, src_orig, 0); \
  \
       return dst_orig; \
    }
@@ -301,7 +282,7 @@
       /* Check for overlap after copying; all n bytes of dst are relevant, */ \
       /* but only m+1 bytes of src if terminator was found */ \
       if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n)) \
-         complain3("strncpy", dst, src, n); \
+         RECORD_OVERLAP_ERROR("strncpy", dst, src, n); \
       while (m++ < n) *dst++ = 0;         /* must pad remainder with nulls */ \
  \
       return dst_orig; \
@@ -386,7 +367,7 @@
          return dst; \
  \
       if (is_overlap(dst, src, len, len)) \
-         complain3("memcpy", dst, src, len); \
+         RECORD_OVERLAP_ERROR("memcpy", dst, src, len); \
  \
       if ( dst > src ) { \
          d = (char *)dst + len - 1; \
@@ -469,7 +450,7 @@
                      src_orig,  \
                      (Addr)dst-(Addr)dst_orig+1,  \
                      (Addr)src-(Addr)src_orig+1)) \
-         complain2("stpcpy", dst_orig, src_orig); \
+         RECORD_OVERLAP_ERROR("stpcpy", dst_orig, src_orig, 0); \
  \
       return dst; \
    }
diff --git a/memcheck/tests/supp_unknown.supp b/memcheck/tests/supp_unknown.supp
index d221e89..f702e90 100644
--- a/memcheck/tests/supp_unknown.supp
+++ b/memcheck/tests/supp_unknown.supp
@@ -1,7 +1,7 @@
 # This contains a match against a "???" entry
 {
    <insert a suppression name here>
-   Memcheck:Addr1
+   Memcheck:Jump
    obj:*
    fun:(below main)
 }
diff --git a/xfree-3.supp b/xfree-3.supp
index 56a0141..e967d0e 100644
--- a/xfree-3.supp
+++ b/xfree-3.supp
@@ -16,7 +16,7 @@
 #
 # For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #
diff --git a/xfree-4.supp b/xfree-4.supp
index 4e60e82..c09d455 100644
--- a/xfree-4.supp
+++ b/xfree-4.supp
@@ -14,9 +14,9 @@
 #     (optionally: caller3 name)
 #  }
 #
-# For memcheck, the supp_kinds are:
+# For Memcheck, the supp_kinds are:
 #
-#     Param Value1 Value2 Value4 Value8 Value16
+#     Param Value1 Value2 Value4 Value8 Value16 Jump
 #     Free Addr1 Addr2 Addr4 Addr8 Addr16
 #     Cond (previously known as Value0)
 #