This commit moves some skin-specific stuff out of core, and generally
neatens other things up.

Also, it adds the --gen-suppressions option for automatically generating
suppressions for each error.

Note that it changes the core/skin interface:
SK_(dup_extra_and_update)() is replaced by SK_(update_extra)(), and
SK_(get_error_name)() and SK_(print_extra_suppression_info)() are added.


-----------------------------------------------------------------------------
details
-----------------------------------------------------------------------------
Removed ac_common.c -- it just #included another .c file;  moved the
#include into ac_main.c.

Introduced "mac_" prefixes for files shared between Addrcheck and Memcheck,
to make it clearer which code is shared.  Also using a "MAC_" prefix for
functions and variables and types that are shared.  Addrcheck doesn't see
the "MC_" prefix at all.

Factored out almost-identical mc_describe_addr() and describe_addr()
(AddrCheck's version) into MAC_(describe_addr)().

Got rid of the "pp_ExeContext" closure passed to SK_(pp_SkinError)(), it
wasn't really necessary.

Introduced MAC_(pp_shared_SkinError)() for the error printing code shared by
Addrcheck and Memcheck.  Fixed some bogus stuff in Addrcheck error messages
about "uninitialised bytes" (there because of an imperfect conversion from
Memcheck).

Moved the leak checker out of core (vg_memory.c), into mac_leakcheck.c.
 - This meant the hacky way of recording Leak errors, which was different to
   normal errors, could be changed to something better:  introduced a
   function VG_(unique_error)(), which unlike VG_(maybe_record_error)() just
   prints the error (unless suppressed) but doesn't record it.  Used for
   leaks;  a much better solution all round as it allowed me to remove a lot
   of almost-identical code from leak handling (is_suppressible_leak(),
   leaksupp_matches_callers()).

 - As part of this, changed the horrible SK_(dup_extra_and_update) into the
   slightly less horrible SK_(update_extra), which returns the size of the
   `extra' part for the core to duplicate.

 - Also renamed it from VG_(generic_detect_memory_leaks)() to
   MAC_(do_detect_memory_leaks).  In making the code nicer w.r.t suppressions
   and error reporting, I tied it a bit more closely to Memcheck/Addrcheck,
   and got rid of some of the args.  It's not really "generic" any more, but
   then it never really was.  (This could be undone, but there doesn't seem
   to be much point.)

STREQ and STREQN were #defined in several places, and in two different ways.
Made global macros VG_STREQ, VG_CLO_STREQ and VG_CLO_STREQN in vg_skin.h.

Added the --gen-suppressions code.  This required adding the functions
SK_(get_error_name)() and SK_(print_extra_suppression_info)() for skins that
use the error handling need.

Added documentation for --gen-suppressions, and fixed some other minor document
problems.

Various other minor related changes too.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1517 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/mac_needs.c b/memcheck/mac_needs.c
new file mode 100644
index 0000000..ad6b2f6
--- /dev/null
+++ b/memcheck/mac_needs.c
@@ -0,0 +1,854 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Code that is shared between MemCheck and AddrCheck.          ---*/
+/*---                                                  mac_needs.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of MemCheck, a heavyweight Valgrind skin for
+   detecting memory errors, and AddrCheck, a lightweight Valgrind skin 
+   for detecting memory errors.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+
+#include "mac_shared.h"
+
+/*------------------------------------------------------------*/
+/*--- Defns                                                ---*/
+/*------------------------------------------------------------*/
+
+/* These many bytes below %ESP are considered addressible if we're
+   doing the --workaround-gcc296-bugs hack. */
+#define VG_GCC296_BUG_STACK_SLOP 1024
+
+/*------------------------------------------------------------*/
+/*--- Command line options                                 ---*/
+/*------------------------------------------------------------*/
+
+Bool  MAC_(clo_partial_loads_ok)       = True;
+Int   MAC_(clo_freelist_vol)           = 1000000;
+Bool  MAC_(clo_leak_check)             = False;
+VgRes MAC_(clo_leak_resolution)        = Vg_LowRes;
+Bool  MAC_(clo_show_reachable)         = False;
+Bool  MAC_(clo_workaround_gcc296_bugs) = False;
+
+Bool MAC_(process_common_cmd_line_option)(Char* arg)
+{
+   if      (VG_CLO_STREQ(arg, "--partial-loads-ok=yes"))
+      MAC_(clo_partial_loads_ok) = True;
+   else if (VG_CLO_STREQ(arg, "--partial-loads-ok=no"))
+      MAC_(clo_partial_loads_ok) = False;
+
+   else if (VG_CLO_STREQN(15, arg, "--freelist-vol=")) {
+      MAC_(clo_freelist_vol) = (Int)VG_(atoll)(&arg[15]);
+      if (MAC_(clo_freelist_vol) < 0) MAC_(clo_freelist_vol) = 0;
+   }
+
+   else if (VG_CLO_STREQ(arg, "--leak-check=yes"))
+      MAC_(clo_leak_check) = True;
+   else if (VG_CLO_STREQ(arg, "--leak-check=no"))
+      MAC_(clo_leak_check) = False;
+
+   else if (VG_CLO_STREQ(arg, "--leak-resolution=low"))
+      MAC_(clo_leak_resolution) = Vg_LowRes;
+   else if (VG_CLO_STREQ(arg, "--leak-resolution=med"))
+      MAC_(clo_leak_resolution) = Vg_MedRes;
+   else if (VG_CLO_STREQ(arg, "--leak-resolution=high"))
+      MAC_(clo_leak_resolution) = Vg_HighRes;
+   
+   else if (VG_CLO_STREQ(arg, "--show-reachable=yes"))
+      MAC_(clo_show_reachable) = True;
+   else if (VG_CLO_STREQ(arg, "--show-reachable=no"))
+      MAC_(clo_show_reachable) = False;
+
+   else if (VG_CLO_STREQ(arg, "--workaround-gcc296-bugs=yes"))
+      MAC_(clo_workaround_gcc296_bugs) = True;
+   else if (VG_CLO_STREQ(arg, "--workaround-gcc296-bugs=no"))
+      MAC_(clo_workaround_gcc296_bugs) = False;
+
+   else
+      return False;
+
+   return True;
+}
+
+/*------------------------------------------------------------*/
+/*--- Shadow chunks info                                   ---*/
+/*------------------------------------------------------------*/
+
+void MAC_(set_where)( ShadowChunk* sc, ExeContext* ec )
+{
+   VG_(set_sc_extra)( sc, 0, (UInt)ec );
+}
+
+ExeContext *MAC_(get_where)( ShadowChunk* sc )
+{
+   return (ExeContext*)VG_(get_sc_extra)(sc, 0);
+}
+
+void SK_(complete_shadow_chunk) ( ShadowChunk* sc, ThreadState* tst )
+{
+   VG_(set_sc_extra) ( sc, 0, (UInt)VG_(get_ExeContext)(tst) );
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Postponing free()ing                                 ---*/
+/*------------------------------------------------------------*/
+
+/* Holds blocks after freeing. */
+static ShadowChunk* freed_list_start  = NULL;
+static ShadowChunk* freed_list_end    = NULL;
+static Int          freed_list_volume = 0;
+
+__attribute__ ((unused))
+Int MAC_(count_freelist) ( void )
+{
+   ShadowChunk* sc;
+   Int n = 0;
+   for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+      n++;
+   return n;
+}
+
+__attribute__ ((unused))
+void MAC_(freelist_sanity) ( void )
+{
+   ShadowChunk* sc;
+   Int n = 0;
+   /* VG_(printf)("freelist sanity\n"); */
+   for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+      n += VG_(get_sc_size)(sc);
+   sk_assert(n == freed_list_volume);
+}
+
+/* Put a shadow chunk on the freed blocks queue, possibly freeing up
+   some of the oldest blocks in the queue at the same time. */
+static void add_to_freed_queue ( ShadowChunk* sc )
+{
+   ShadowChunk* sc1;
+
+   /* Put it at the end of the freed list */
+   if (freed_list_end == NULL) {
+      sk_assert(freed_list_start == NULL);
+      freed_list_end = freed_list_start = sc;
+      freed_list_volume = VG_(get_sc_size)(sc);
+   } else {    
+      sk_assert(VG_(get_sc_next)(freed_list_end) == NULL);
+      VG_(set_sc_next)(freed_list_end, sc);
+      freed_list_end = sc;
+      freed_list_volume += VG_(get_sc_size)(sc);
+   }
+   VG_(set_sc_next)(sc, NULL);
+
+   /* Release enough of the oldest blocks to bring the free queue
+      volume below vg_clo_freelist_vol. */
+   
+   while (freed_list_volume > MAC_(clo_freelist_vol)) {
+      /* freelist_sanity(); */
+      sk_assert(freed_list_start != NULL);
+      sk_assert(freed_list_end != NULL);
+
+      sc1 = freed_list_start;
+      freed_list_volume -= VG_(get_sc_size)(sc1);
+      /* VG_(printf)("volume now %d\n", freed_list_volume); */
+      sk_assert(freed_list_volume >= 0);
+
+      if (freed_list_start == freed_list_end) {
+         freed_list_start = freed_list_end = NULL;
+      } else {
+         freed_list_start = VG_(get_sc_next)(sc1);
+      }
+      VG_(set_sc_next)(sc1, NULL); /* just paranoia */
+      VG_(free_ShadowChunk) ( sc1 );
+   }
+}
+
+void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst )
+{
+   /* Record where freed */
+   MAC_(set_where)( sc, VG_(get_ExeContext) ( tst ) );
+
+   /* Put it out of harm's way for a while. */
+   add_to_freed_queue ( sc );
+}
+
+/*------------------------------------------------------------*/
+/*--- Comparing and printing errors                        ---*/
+/*------------------------------------------------------------*/
+
+static __inline__
+void clear_AddrInfo ( AddrInfo* ai )
+{
+   ai->akind      = Unknown;
+   ai->blksize    = 0;
+   ai->rwoffset   = 0;
+   ai->lastchange = NULL;
+   ai->stack_tid  = VG_INVALID_THREADID;
+   ai->maybe_gcc  = False;
+}
+
+void MAC_(clear_MAC_Error) ( MAC_Error* err_extra )
+{
+   err_extra->axskind   = ReadAxs;
+   err_extra->size      = 0;
+   clear_AddrInfo ( &err_extra->addrinfo );
+   err_extra->isWrite   = False;
+}
+
+__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;
+}
+
+/* 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.  */
+
+Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 )
+{
+   MAC_Error* e1_extra = VG_(get_error_extra)(e1);
+   MAC_Error* e2_extra = VG_(get_error_extra)(e2);
+
+   /* Guaranteed by calling function */
+   sk_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
+   
+   switch (VG_(get_error_kind)(e1)) {
+      case CoreMemErr: {
+         Char *e1s, *e2s;
+         if (e1_extra->isWrite != e2_extra->isWrite)   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;
+         return False;
+      }
+
+      case UserErr:
+      case ParamErr:
+         if (e1_extra->isWrite != e2_extra->isWrite)           return False;
+         if (VG_(get_error_kind)(e1) == ParamErr 
+             && 0 != VG_(strcmp)(VG_(get_error_string)(e1),
+                                 VG_(get_error_string)(e2)))   return False;
+         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 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 ValueErr:
+         if (e1_extra->size != e2_extra->size) return False;
+         return True;
+
+      case LeakErr:
+         VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\n"
+                         "since it's handled with VG_(unique_error)()!");
+
+      default: 
+         VG_(printf)("Error:\n  unknown error code %d\n",
+                     VG_(get_error_kind)(e1));
+         VG_(skin_panic)("unknown error code in SK_(eq_SkinError)");
+   }
+}
+
+void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
+{
+   switch (ai->akind) {
+      case Stack: 
+         VG_(message)(Vg_UserMsg, 
+                      "   Address 0x%x is on thread %d's stack", 
+                      a, ai->stack_tid);
+         break;
+      case Unknown:
+         if (ai->maybe_gcc) {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is just below %%esp.  Possibly a bug in GCC/G++",
+               a);
+            VG_(message)(Vg_UserMsg, 
+               "   v 2.96 or 3.0.X.  To suppress, use: --workaround-gcc296-bugs=yes");
+	 } else {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is not stack'd, malloc'd or free'd", a);
+         }
+         break;
+      case Freed: case Mallocd: case UserG: {
+         UInt delta;
+         UChar* relative;
+         if (ai->rwoffset < 0) {
+            delta    = (UInt)(- ai->rwoffset);
+            relative = "before";
+         } else if (ai->rwoffset >= ai->blksize) {
+            delta    = ai->rwoffset - ai->blksize;
+            relative = "after";
+         } else {
+            delta    = ai->rwoffset;
+            relative = "inside";
+         }
+         VG_(message)(Vg_UserMsg, 
+            "   Address 0x%x is %d bytes %s a block of size %d %s",
+            a, delta, relative, 
+            ai->blksize,
+            ai->akind==Mallocd ? "alloc'd" 
+               : ai->akind==Freed ? "free'd" 
+                                  : "client-defined");
+         VG_(pp_ExeContext)(ai->lastchange);
+         break;
+      }
+      default:
+         VG_(skin_panic)("MAC_(pp_AddrInfo)");
+   }
+}
+
+/* This prints out the message for the error types where Memcheck and
+   Addrcheck have identical messages */
+void MAC_(pp_shared_SkinError) ( Error* err )
+{
+   MAC_Error* err_extra = VG_(get_error_extra)(err);
+
+   switch (VG_(get_error_kind)(err)) {
+      case FreeErr:
+         VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
+         /* fall through */
+      case FreeMismatchErr:
+         if (VG_(get_error_kind)(err) == FreeMismatchErr)
+            VG_(message)(Vg_UserMsg, 
+                         "Mismatched free() / delete / delete []");
+         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+         MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+         break;
+
+      case LeakErr: {
+         /* Totally abusing the types of these spare fields... oh well. */
+         UInt n_this_record   = (UInt)VG_(get_error_address)(err);
+         UInt n_total_records = (UInt)VG_(get_error_string) (err);
+
+         MAC_(pp_LeakError)(err_extra, n_this_record, n_total_records);
+         break;
+      }
+
+      default: 
+         VG_(printf)("Error:\n  unknown Memcheck/Addrcheck error code %d\n",
+                     VG_(get_error_kind)(err));
+         VG_(skin_panic)("unknown error code in MAC_(pp_shared_SkinError)");
+   }
+}
+
+/*------------------------------------------------------------*/
+/*--- Recording errors                                     ---*/
+/*------------------------------------------------------------*/
+
+/* Additional description function for describe_addr();  used by
+   MemCheck for user blocks, which Addrcheck doesn't support. */
+Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai ) = NULL;
+   
+/* Return the first shadow chunk satisfying the predicate p. */
+static ShadowChunk* first_matching_freed_ShadowChunk ( Bool (*p)(ShadowChunk*) )
+{
+   ShadowChunk* sc;
+
+   /* No point looking through freed blocks if we're not keeping
+      them around for a while... */
+   for (sc = freed_list_start; sc != NULL; sc = VG_(get_sc_next)(sc))
+      if (p(sc))
+         return sc;
+
+   return NULL;
+}
+
+/* Describe an address as best you can, for error messages,
+   putting the result in ai. */
+static void describe_addr ( Addr a, AddrInfo* ai )
+{
+   ShadowChunk* sc;
+   ThreadId     tid;
+
+   /* Nested functions, yeah.  Need the lexical scoping of 'a'. */
+   
+   /* Closure for searching thread stacks */
+   Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
+   {
+      return (stack_min <= a && a <= stack_max);
+   }
+   /* Closure for searching malloc'd and free'd lists */
+   Bool addr_is_in_block(ShadowChunk *sh_ch)
+   {
+      return VG_(addr_is_in_block) ( a, VG_(get_sc_data)(sh_ch),
+                                        VG_(get_sc_size)(sh_ch) );
+   }
+
+   /* Perhaps it's a user-def'd block ?  (only check if requested, though) */
+   if (NULL != MAC_(describe_addr_supp)) {
+      if (MAC_(describe_addr_supp)( a, ai ))
+         return;
+   }
+   /* Perhaps it's on a thread's stack? */
+   tid = VG_(first_matching_thread_stack)(addr_is_in_bounds);
+   if (tid != VG_INVALID_THREADID) {
+      ai->akind     = Stack;
+      ai->stack_tid = tid;
+      return;
+   }
+   /* Search for a recently freed block which might bracket it. */
+   sc = first_matching_freed_ShadowChunk(addr_is_in_block);
+   if (NULL != sc) { 
+      ai->akind      = Freed;
+      ai->blksize    = VG_(get_sc_size)(sc);
+      ai->rwoffset   = (Int)a - (Int)VG_(get_sc_data)(sc);
+      ai->lastchange = MAC_(get_where)(sc);
+      return;
+   }
+   /* Search for a currently malloc'd block which might bracket it. */
+   sc = VG_(first_matching_mallocd_ShadowChunk)(addr_is_in_block);
+   if (NULL != sc) {
+      ai->akind      = Mallocd;
+      ai->blksize    = VG_(get_sc_size)(sc);
+      ai->rwoffset   = (Int)a - (Int)VG_(get_sc_data)(sc);
+      ai->lastchange = MAC_(get_where)(sc);
+      return;
+   }
+   /* Clueless ... */
+   ai->akind = Unknown;
+   return;
+}
+
+
+/* Is this address within some small distance below %ESP?  Used only
+   for the --workaround-gcc296-bugs kludge. */
+static Bool is_just_below_ESP( Addr esp, Addr aa )
+{
+   if ((UInt)esp > (UInt)aa
+       && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP)
+      return True;
+   else
+      return False;
+}
+
+/* This one called from generated code. */
+
+void MAC_(record_address_error) ( Addr a, Int size, Bool isWrite )
+{
+   MAC_Error err_extra;
+   Bool      just_below_esp;
+
+   just_below_esp = is_just_below_ESP( VG_(get_stack_pointer)(), a );
+
+   /* If this is caused by an access immediately below %ESP, and the
+      user asks nicely, we just ignore it. */
+   if (MAC_(clo_workaround_gcc296_bugs) && just_below_esp)
+      return;
+
+   MAC_(clear_MAC_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)( NULL, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+/* These ones are 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. */
+void MAC_(record_core_mem_error) ( ThreadState* tst, Bool isWrite, Char* msg )
+{
+   MAC_Error err_extra;
+
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.isWrite = isWrite;
+   VG_(maybe_record_error)( tst, CoreMemErr, /*addr*/0, msg, &err_extra );
+}
+
+void MAC_(record_param_error) ( ThreadState* tst, Addr a, Bool isWrite, 
+                               Char* msg )
+{
+   MAC_Error err_extra;
+
+   sk_assert(NULL != tst);
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   err_extra.isWrite = isWrite;
+   VG_(maybe_record_error)( tst, ParamErr, a, msg, &err_extra );
+}
+
+void MAC_(record_jump_error) ( ThreadState* tst, Addr a )
+{
+   MAC_Error err_extra;
+
+   sk_assert(NULL != tst);
+
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.axskind = ExecAxs;
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+void MAC_(record_free_error) ( ThreadState* tst, Addr a ) 
+{
+   MAC_Error err_extra;
+
+   sk_assert(NULL != tst);
+
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, FreeErr, a, /*s*/NULL, &err_extra );
+}
+
+void MAC_(record_freemismatch_error) ( ThreadState* tst, Addr a )
+{
+   MAC_Error err_extra;
+
+   sk_assert(NULL != tst);
+
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, FreeMismatchErr, a, /*s*/NULL, &err_extra );
+}
+
+/* Updates the copy with address info if necessary (but not for LeakErrs). */
+UInt SK_(update_extra)( Error* err )
+{
+   MAC_Error* extra;
+
+   /* Don't need to return the correct size -- LeakErrs are always shown with
+      VG_(unique_error)() so they're not copied anyway. */
+   if (LeakErr == VG_(get_error_kind)(err))
+      return 0;
+
+   extra = (MAC_Error*)VG_(get_error_extra)(err);
+
+   if (extra != NULL && Undescribed == extra->addrinfo.akind) {
+      describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
+   }
+
+   return sizeof(MAC_Error);
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Suppressions                                         ---*/
+/*------------------------------------------------------------*/
+
+Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su )
+{
+   SuppKind skind;
+
+   if      (VG_STREQ(name, "Param"))   skind = ParamSupp;
+   else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp;
+   else if (VG_STREQ(name, "Addr1"))   skind = Addr1Supp;
+   else if (VG_STREQ(name, "Addr2"))   skind = Addr2Supp;
+   else if (VG_STREQ(name, "Addr4"))   skind = Addr4Supp;
+   else if (VG_STREQ(name, "Addr8"))   skind = Addr8Supp;
+   else if (VG_STREQ(name, "Free"))    skind = FreeSupp;
+   else if (VG_STREQ(name, "Leak"))    skind = LeakSupp;
+   else
+      return False;
+
+   VG_(set_supp_kind)(su, skind);
+   return True;
+}
+
+Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su )
+{
+   Bool eof;
+
+   if (VG_(get_supp_kind)(su) == ParamSupp) {
+      eof = VG_(get_line) ( fd, buf, nBuf );
+      if (eof) return False;
+      VG_(set_supp_string)(su, VG_(strdup)(buf));
+   }
+   return True;
+}
+
+Bool SK_(error_matches_suppression)(Error* err, Supp* su)
+{
+   UInt       su_size;
+   MAC_Error* err_extra = VG_(get_error_extra)(err);
+   ErrorKind  ekind     = VG_(get_error_kind )(err);
+
+   switch (VG_(get_supp_kind)(su)) {
+      case ParamSupp:
+         return (ekind == ParamErr 
+              && VG_STREQ(VG_(get_error_string)(err), 
+                          VG_(get_supp_string)(su)));
+
+      case CoreMemSupp:
+         return (ekind == CoreMemErr
+              && 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;
+      value_case:
+         return (ekind == ValueErr && err_extra->size == su_size);
+
+      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;
+      addr_case:
+         return (ekind == AddrErr && err_extra->size == su_size);
+
+      case FreeSupp:
+         return (ekind == FreeErr || ekind == FreeMismatchErr);
+
+      case LeakSupp:
+         return (ekind == LeakErr);
+
+      default:
+         VG_(printf)("Error:\n"
+                     "  unknown suppression type %d\n",
+                     VG_(get_supp_kind)(su));
+         VG_(skin_panic)("unknown suppression type in "
+                         "SK_(error_matches_suppression)");
+   }
+}
+
+Char* SK_(get_error_name) ( Error* err )
+{
+   Char* s;
+   switch (VG_(get_error_kind)(err)) {
+   case ParamErr:           return "Param";
+   case UserErr:            return NULL;  /* Can't suppress User errors */
+   case FreeMismatchErr:    return "Free";
+   case FreeErr:            return "Free";
+   case AddrErr:            
+      switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
+      case 1:               return "Addr1";
+      case 2:               return "Addr2";
+      case 4:               return "Addr4";
+      case 8:               return "Addr8";
+      default:              VG_(skin_panic)("unexpected size for Addr");
+      }
+     
+   case ValueErr:
+      switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
+      case 0:               return "Cond";
+      case 1:               return "Value1";
+      case 2:               return "Value2";
+      case 4:               return "Value4";
+      case 8:               return "Value8";
+      default:              VG_(skin_panic)("unexpected size for Value");
+      }
+   case CoreMemErr:         return "CoreMem";
+   case LeakErr:            return "Leak";
+   default:                 VG_(skin_panic)("get_error_name: unexpected type");
+   }
+   VG_(printf)(s);
+}
+
+void SK_(print_extra_suppression_info) ( Error* err )
+{
+   if (ParamErr == VG_(get_error_kind)(err)) {
+      VG_(printf)("   %s\n", VG_(get_error_string)(err));
+   }
+}
+
+/*------------------------------------------------------------*/
+/*--- Crude profiling machinery.                           ---*/
+/*------------------------------------------------------------*/
+
+/* Event index.  If just the name of the fn is given, this means the
+   number of calls to the fn.  Otherwise it is the specified event.
+   Ones marked 'M' are MemCheck only.  Ones marked 'A' are AddrCheck only.
+   The rest are shared.
+
+   10   alloc_secondary_map
+
+   20   get_abit
+M  21   get_vbyte
+   22   set_abit
+M  23   set_vbyte
+   24   get_abits4_ALIGNED
+M  25   get_vbytes4_ALIGNED       
+
+   30   set_address_range_perms
+   31   set_address_range_perms(lower byte loop)
+   32   set_address_range_perms(quadword loop)
+   33   set_address_range_perms(upper byte loop)
+   
+   35   make_noaccess
+   36   make_writable
+   37   make_readable
+A  38   make_accessible
+
+   40   copy_address_range_state
+   41   copy_address_range_state(byte loop)
+   42   check_writable
+   43   check_writable(byte loop)
+   44   check_readable
+   45   check_readable(byte loop)
+   46   check_readable_asciiz
+   47   check_readable_asciiz(byte loop)
+A  48   check_accessible
+A  49   check_accessible(byte loop)
+
+   50   make_noaccess_aligned
+   51   make_writable_aligned
+
+M  60   helperc_LOADV4
+M  61   helperc_STOREV4
+M  62   helperc_LOADV2
+M  63   helperc_STOREV2
+M  64   helperc_LOADV1
+M  65   helperc_STOREV1
+
+A  66   helperc_ACCESS4
+A  67   helperc_ACCESS2
+A  68   helperc_ACCESS1
+
+M  70   rim_rd_V4_SLOWLY
+M  71   rim_wr_V4_SLOWLY
+M  72   rim_rd_V2_SLOWLY
+M  73   rim_wr_V2_SLOWLY
+M  74   rim_rd_V1_SLOWLY
+M  75   rim_wr_V1_SLOWLY
+
+A  76   ACCESS4_SLOWLY
+A  77   ACCESS2_SLOWLY
+A  78   ACCESS1_SLOWLY
+
+   80   fpu_read
+   81   fpu_read aligned 4
+   82   fpu_read aligned 8
+   83   fpu_read 2
+   84   fpu_read 10/28/108
+
+M  85   fpu_write
+M  86   fpu_write aligned 4
+M  87   fpu_write aligned 8
+M  88   fpu_write 2
+M  89   fpu_write 10/28/108
+
+   90   fpu_access
+   91   fpu_access aligned 4
+   92   fpu_access aligned 8
+   93   fpu_access 2
+   94   fpu_access 10/28/108
+
+   100  fpu_access_check_SLOWLY
+   101  fpu_access_check_SLOWLY(byte loop)
+
+   110  new_mem_stack_4
+   111  new_mem_stack_8
+   112  new_mem_stack_12
+   113  new_mem_stack_16
+   114  new_mem_stack_32
+   115  new_mem_stack
+
+   120  die_mem_stack_4
+   121  die_mem_stack_8
+   122  die_mem_stack_12
+   123  die_mem_stack_16
+   124  die_mem_stack_32
+   125  die_mem_stack
+*/
+
+#ifdef MAC_PROFILE_MEMORY
+
+UInt MAC_(event_ctr)[N_PROF_EVENTS];
+
+void MAC_(init_prof_mem) ( void )
+{
+   Int i;
+   for (i = 0; i < N_PROF_EVENTS; i++)
+      MAC_(event_ctr)[i] = 0;
+}
+
+void MAC_(done_prof_mem) ( void )
+{
+   Int i;
+   for (i = 0; i < N_PROF_EVENTS; i++) {
+      if ((i % 10) == 0) 
+         VG_(printf)("\n");
+      if (MAC_(event_ctr)[i] > 0)
+         VG_(printf)( "prof mem event %2d: %d\n", i, MAC_(event_ctr)[i] );
+   }
+   VG_(printf)("\n");
+}
+
+#else
+
+void MAC_(init_prof_mem) ( void ) { }
+void MAC_(done_prof_mem) ( void ) { }
+
+#endif
+
+/*------------------------------------------------------------*/
+/*--- Syscall wrappers                                     ---*/
+/*------------------------------------------------------------*/
+
+void* SK_(pre_syscall)  ( ThreadId tid, UInt syscallno, Bool isBlocking )
+{
+   Int sane = SK_(cheap_sanity_check)();
+   return (void*)sane;
+}
+
+void  SK_(post_syscall) ( ThreadId tid, UInt syscallno,
+                           void* pre_result, Int res, Bool isBlocking )
+{
+   Int  sane_before_call = (Int)pre_result;
+   Bool sane_after_call  = SK_(cheap_sanity_check)();
+
+   if ((Int)sane_before_call && (!sane_after_call)) {
+      VG_(message)(Vg_DebugMsg, "post-syscall: ");
+      VG_(message)(Vg_DebugMsg,
+                   "probable sanity check failure for syscall number %d\n",
+                   syscallno );
+      VG_(skin_panic)("aborting due to the above ... bye!");
+   }
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                              mac_needs.c ---*/
+/*--------------------------------------------------------------------*/