Memory pool support.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2428 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c
index aac8153..67f1ce8 100644
--- a/addrcheck/ac_main.c
+++ b/addrcheck/ac_main.c
@@ -1185,7 +1185,11 @@
    /* Overload memcheck client reqs */
    if (!VG_IS_SKIN_USERREQ('M','C',arg[0])
     && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0]
-    && VG_USERREQ__FREELIKE_BLOCK   != arg[0])
+    && VG_USERREQ__FREELIKE_BLOCK   != arg[0]
+    && VG_USERREQ__CREATE_MEMPOOL   != arg[0]
+    && VG_USERREQ__DESTROY_MEMPOOL  != arg[0]
+    && VG_USERREQ__MEMPOOL_ALLOC    != arg[0]
+    && VG_USERREQ__MEMPOOL_FREE     != arg[0])
       return False;
 
    switch (arg[0]) {
diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html
index 0a01661..c6d6722 100644
--- a/coregrind/docs/coregrind_core.html
+++ b/coregrind/docs/coregrind_core.html
@@ -984,6 +984,23 @@
     with <code>VALGRIND_MALLOCLIKE_BLOCK</code>.  Again, see
     <code>memcheck/memcheck.h</code> for information on how to use it.  
 <p>
+<li><code>VALGRIND_CREATE_MEMPOOL</code>: This is similar to
+    <code>VALGRIND_MALLOCLIKE_BLOCK</code>, but is tailored towards code
+    that uses memory pools.  See the comments in <code>valgrind.h</code>
+    for information on how to use it.
+<p>
+<li><code>VALGRIND_DESTROY_MEMPOOL</code>: This should be used in
+    conjunction with <code>VALGRIND_CREATE_MEMPOOL</code> Again, see the
+    comments in <code>valgrind.h</code> for information on how to use it.
+<p>
+<li><code>VALGRIND_MEMPOOL_ALLOC</code>: This should be used in
+    conjunction with <code>VALGRIND_CREATE_MEMPOOL</code> Again, see the
+    comments in <code>valgrind.h</code> for information on how to use it.
+<p>
+<li><code>VALGRIND_MEMPOOL_FREE</code>: This should be used in
+    conjunction with <code>VALGRIND_CREATE_MEMPOOL</code> Again, see the
+    comments in <code>valgrind.h</code> for information on how to use it.
+<p>
 <li><code>VALGRIND_NON_SIMD_CALL[0123]</code>: executes a function of 0, 1, 2
      or 3 args in the client program on the <i>real</i> CPU, not the virtual
      CPU that Valgrind normally runs code on.  These are used in various ways
diff --git a/include/valgrind.h b/include/valgrind.h
index 8cd07c9..33d7be3 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -163,6 +163,11 @@
              malloc() et al, by using vg_replace_malloc.c. */
           VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
           VG_USERREQ__FREELIKE_BLOCK   = 0x1302,
+          /* Memory pool support. */
+          VG_USERREQ__CREATE_MEMPOOL   = 0x1303,
+          VG_USERREQ__DESTROY_MEMPOOL  = 0x1304,
+          VG_USERREQ__MEMPOOL_ALLOC    = 0x1305,
+          VG_USERREQ__MEMPOOL_FREE     = 0x1306,
 
           /* Allow printfs to valgrind log. */
           VG_USERREQ__PRINTF = 0x1401,
@@ -322,4 +327,36 @@
                             addr, rzB, 0, 0);                      \
    }
 
+/* Create a memory pool. */
+#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed)              \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__CREATE_MEMPOOL,            \
+                            pool, rzB, is_zeroed, 0);              \
+   }
+
+/* Destroy a memory pool. */
+#define VALGRIND_DESTROY_MEMPOOL(pool)                             \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__DESTROY_MEMPOOL,           \
+                            pool, 0, 0, 0);                        \
+   }
+
+/* Associate a piece of memory with a memory pool. */
+#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size)                   \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__MEMPOOL_ALLOC,             \
+                            pool, addr, size, 0);                  \
+   }
+
+/* Disassociate a piece of memory from a memory pool. */
+#define VALGRIND_MEMPOOL_FREE(pool, addr)                          \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__MEMPOOL_FREE,              \
+                            pool, addr, 0, 0);                     \
+   }
+
 #endif   /* __VALGRIND_H */
diff --git a/memcheck/mac_malloc_wrappers.c b/memcheck/mac_malloc_wrappers.c
index b3c5353..45c0ec1 100644
--- a/memcheck/mac_malloc_wrappers.c
+++ b/memcheck/mac_malloc_wrappers.c
@@ -61,6 +61,10 @@
 /* Record malloc'd blocks.  Nb: Addrcheck and Memcheck construct this
    separately in their respective initialisation functions. */
 VgHashTable MAC_(malloc_list) = NULL;
+
+/* Memory pools.  Nb: Addrcheck and Memcheck construct this separately
+   in their respective initialisation functions. */
+VgHashTable MAC_(mempool_list) = NULL;
    
 /* Records blocks after freeing. */
 static MAC_Chunk* freed_list_start  = NULL;
@@ -127,7 +131,8 @@
 
 /* Allocate its shadow chunk, put it on the appropriate list. */
 static
-void add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind )
+MAC_Chunk* add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind,
+                           VgHashTable table)
 {
    MAC_Chunk* mc;
 
@@ -146,7 +151,9 @@
       VG_(skin_panic)("add_MAC_chunk: shadow area is accessible");
    } 
 
-   VG_(HT_add_node)( MAC_(malloc_list), (VgHashNode*)mc );
+   VG_(HT_add_node)( table, (VgHashNode*)mc );
+
+   return mc;
 }
 
 /*------------------------------------------------------------*/
@@ -155,21 +162,26 @@
 
 /* Allocate memory and note change in memory available */
 __inline__
-void MAC_(new_block) ( Addr p, UInt size,
-                       UInt rzB, Bool is_zeroed, MAC_AllocKind kind )
+MAC_Chunk* MAC_(new_block) ( Addr p, UInt size,
+                             UInt rzB, Bool is_zeroed, MAC_AllocKind kind,
+                             VgHashTable table)
 {
+   MAC_Chunk *mc;
+
    VGP_PUSHCC(VgpCliMalloc);
 
    cmalloc_n_mallocs ++;
    cmalloc_bs_mallocd += size;
 
-   add_MAC_Chunk( p, size, kind );
+   mc = add_MAC_Chunk( p, size, kind, table );
 
    MAC_(ban_mem_heap)( p-rzB, rzB );
    MAC_(new_mem_heap)( p, size, is_zeroed );
    MAC_(ban_mem_heap)( p+size, rzB );
 
    VGP_POPCC(VgpCliMalloc);
+
+   return mc;
 }
 
 void* SK_(malloc) ( Int n )
@@ -180,7 +192,8 @@
    } else {
       Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n );
       MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB),
-                        /*is_zeroed*/False, MAC_AllocMalloc );
+                        /*is_zeroed*/False, MAC_AllocMalloc,
+                        MAC_(malloc_list));
       return (void*)p;
    }
 }
@@ -193,7 +206,8 @@
    } else {
       Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n );
       MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB),
-                        /*is_zeroed*/False, MAC_AllocNew );
+                        /*is_zeroed*/False, MAC_AllocNew,
+                        MAC_(malloc_list));
       return (void*)p;
    }
 }
@@ -207,7 +221,8 @@
    } else {
       Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n );
       MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB),
-                        /*is_zeroed*/False, MAC_AllocNewVec );
+                        /*is_zeroed*/False, MAC_AllocNewVec,
+                        MAC_(malloc_list));
       return (void*)p;
    }
 }
@@ -220,7 +235,8 @@
    } else {
       Addr p = (Addr)VG_(cli_malloc)( align, n );
       MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB),
-                        /*is_zeroed*/False, MAC_AllocMalloc );
+                        /*is_zeroed*/False, MAC_AllocMalloc,
+                        MAC_(malloc_list));
       return (void*)p;
    }
 }
@@ -238,7 +254,8 @@
    } else {
       Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n );
       MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB),
-                        /*is_zeroed*/True, MAC_AllocMalloc );
+                        /*is_zeroed*/True, MAC_AllocMalloc,
+                        MAC_(malloc_list));
       for (i = 0; i < n; i++) 
          ((UChar*)p)[i] = 0;
       return (void*)p;
@@ -261,17 +278,15 @@
       describe_addr() which looks for it in malloclist. */
    *prev_chunks_next_ptr = mc->next;
 
-   /* Record where freed */
-   mc->where = VG_(get_ExeContext) ( VG_(get_current_or_recent_tid)() );
-
    /* Put it out of harm's way for a while, if not from a client request */
-   if (MAC_AllocCustom != mc->allockind)
+   if (MAC_AllocCustom != mc->allockind) {
+      /* Record where freed */
+      mc->where = VG_(get_ExeContext) ( VG_(get_current_or_recent_tid)() );
       add_to_freed_queue ( mc );
-   else
+   } else
       VG_(free) ( mc );
 }
 
-
 __inline__
 void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind )
 {
@@ -392,13 +407,119 @@
       /* this has to be after die_and_free_mem, otherwise the
          former succeeds in shorting out the new block, not the
          old, in the case when both are on the same list.  */
-      add_MAC_Chunk ( p_new, new_size, MAC_AllocMalloc );
+      add_MAC_Chunk ( p_new, new_size, MAC_AllocMalloc,  MAC_(malloc_list) );
 
       VGP_POPCC(VgpCliMalloc);
       return (void*)p_new;
    }  
 }
 
+/* Memory pool stuff. */
+
+void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed)
+{
+   MAC_Mempool* mp;
+
+   mp            = VG_(malloc)(sizeof(MAC_Mempool));
+   mp->pool      = pool;
+   mp->rzB       = rzB;
+   mp->is_zeroed = is_zeroed;
+   mp->chunks    = VG_(HT_construct)();
+
+   /* Paranoia ... ensure this area is off-limits to the client, so
+      the mp->data field isn't visible to the leak checker.  If memory
+      management is working correctly, anything pointer returned by
+      VG_(malloc) should be noaccess as far as the client is
+      concerned. */
+   if (!MAC_(check_noaccess)( (Addr)mp, sizeof(MAC_Mempool), NULL )) {
+      VG_(skin_panic)("MAC_(create_mempool): shadow area is accessible");
+   } 
+
+   VG_(HT_add_node)( MAC_(mempool_list), (VgHashNode*)mp );
+   
+}
+
+void MAC_(destroy_mempool)(Addr pool)
+{
+   MAC_Mempool*  mp;
+   MAC_Mempool** prev_next;
+
+   void nuke_chunk(VgHashNode *node)
+   {
+      MAC_Chunk *mc = (MAC_Chunk *)node;
+
+      /* Note: ban redzones again -- just in case user de-banned them
+         with a client request... */
+      MAC_(ban_mem_heap)(mc->data-mp->rzB, mp->rzB );
+      MAC_(die_mem_heap)(mc->data, mc->size );
+      MAC_(ban_mem_heap)(mc->data+mc->size, mp->rzB );
+   }
+
+   mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), (UInt)pool,
+                                        (VgHashNode***)&prev_next );
+
+   if (mp == NULL) {
+      ThreadId      tid = VG_(get_current_or_recent_tid)();
+
+      MAC_(record_illegal_mempool_error) ( tid, pool );
+      return;
+   }
+
+   *prev_next = mp->next;
+   VG_(HT_apply_to_all_nodes)(mp->chunks, nuke_chunk);
+   VG_(HT_destruct)(mp->chunks);
+
+   VG_(free)(mp);
+}
+
+void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size)
+{
+   MAC_Mempool*  mp;
+   MAC_Mempool** prev_next;
+   MAC_Chunk*    mc;
+
+   mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), (UInt)pool,
+                                        (VgHashNode***)&prev_next );
+
+   if (mp == NULL) {
+      ThreadId      tid = VG_(get_current_or_recent_tid)();
+
+      MAC_(record_illegal_mempool_error) ( tid, pool );
+      return;
+   }
+
+   mc = MAC_(new_block)(addr, size, mp->rzB, mp->is_zeroed, MAC_AllocCustom,
+                        mp->chunks);
+}
+
+void MAC_(mempool_free)(Addr pool, Addr addr)
+{
+   MAC_Mempool*  mp;
+   MAC_Mempool** prev_pool;
+   MAC_Chunk*    mc;
+   MAC_Chunk**   prev_chunk;
+   ThreadId      tid = VG_(get_current_or_recent_tid)();
+
+
+   mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list), (UInt)pool,
+                                       (VgHashNode***)&prev_pool);
+
+   if (mp == NULL) {
+      MAC_(record_illegal_mempool_error)(tid, pool);
+      return;
+   }
+
+   mc = (MAC_Chunk*)VG_(HT_get_node)(mp->chunks, (UInt)addr,
+                                     (VgHashNode***)&prev_chunk);
+
+   if (mc == NULL) {
+      MAC_(record_free_error)(tid, (Addr)addr);
+      return;
+   }
+
+   die_and_free_mem(mc, prev_chunk, mp->rzB);
+}
+
 void MAC_(print_malloc_stats) ( void )
 {
    UInt nblocks = 0, nbytes = 0;
diff --git a/memcheck/mac_needs.c b/memcheck/mac_needs.c
index 29772a2..8e1a7b8 100644
--- a/memcheck/mac_needs.c
+++ b/memcheck/mac_needs.c
@@ -196,6 +196,9 @@
          VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\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));
@@ -223,9 +226,15 @@
                " Address 0x%x is not stack'd, malloc'd or (recently) free'd",a);
          }
          break;
-      case Freed: case Mallocd: case UserG: {
+      case Freed: case Mallocd: case UserG: case Mempool: {
          UInt delta;
          UChar* relative;
+         UChar* kind;
+         if (ai->akind == Mempool) {
+            kind = "mempool";
+         } else {
+            kind = "block";
+         }
          if (ai->rwoffset < 0) {
             delta    = (UInt)(- ai->rwoffset);
             relative = "before";
@@ -237,8 +246,8 @@
             relative = "inside";
          }
          VG_(message)(Vg_UserMsg, 
-            " Address 0x%x is %d bytes %s a block of size %d %s",
-            a, delta, relative, 
+            " Address 0x%x is %d bytes %s a %s of size %d %s",
+            a, delta, relative, kind,
             ai->blksize,
             ai->akind==Mallocd ? "alloc'd" 
                : ai->akind==Freed ? "free'd" 
@@ -314,6 +323,12 @@
          break;
       }
 
+      case IllegalMempoolErr:
+         VG_(message)(Vg_UserMsg, "Illegal memory pool address");
+         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+         MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+         break;
+
       default: 
          VG_(printf)("Error:\n  unknown Memcheck/Addrcheck error code %d\n",
                      VG_(get_error_kind)(err));
@@ -470,6 +485,16 @@
    VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra );
 }
 
+void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) 
+{
+   MAC_Error err_extra;
+
+   sk_assert(VG_INVALID_THREADID != tid);
+   MAC_(clear_MAC_Error)( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra );
+}
+
 void MAC_(record_freemismatch_error) ( ThreadId tid, Addr a )
 {
    MAC_Error err_extra;
@@ -499,6 +524,7 @@
    case ParamErr:
    case UserErr:
    case FreeErr:
+   case IllegalMempoolErr:
    case FreeMismatchErr: {
       MAC_Error* extra = (MAC_Error*)VG_(get_error_extra)(err);
       if (extra != NULL && Undescribed == extra->addrinfo.akind) {
@@ -533,6 +559,7 @@
    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
       return False;
 
@@ -595,6 +622,9 @@
       case LeakSupp:
          return (ekind == LeakErr);
 
+      case MempoolSupp:
+         return (ekind == IllegalMempoolErr);
+
       default:
          VG_(printf)("Error:\n"
                      "  unknown suppression type %d\n",
@@ -611,6 +641,7 @@
    case ParamErr:           return "Param";
    case UserErr:            return NULL;  /* Can't suppress User errors */
    case FreeMismatchErr:    return "Free";
+   case IllegalMempoolErr:  return "Mempool";
    case FreeErr:            return "Free";
    case AddrErr:            
       switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
@@ -784,6 +815,7 @@
 void MAC_(common_pre_clo_init)(void)
 {
    MAC_(malloc_list) = VG_(HT_construct)();
+   MAC_(mempool_list) = VG_(HT_construct)();
    init_prof_mem();
 }
 
@@ -844,7 +876,8 @@
       UInt rzB       =       arg[3];
       Bool is_zeroed = (Bool)arg[4];
 
-      MAC_(new_block) ( p, sizeB, rzB, is_zeroed, MAC_AllocCustom );
+      MAC_(new_block) ( p, sizeB, rzB, is_zeroed, MAC_AllocCustom,
+                        MAC_(malloc_list) );
       return True;
    }
    case VG_USERREQ__FREELIKE_BLOCK: {
@@ -859,6 +892,39 @@
       *ret = (Addr)MAC_(record_overlap_error);
       return True;
 
+   case VG_USERREQ__CREATE_MEMPOOL: {
+      Addr pool      = (Addr)arg[1];
+      UInt rzB       =       arg[2];
+      Bool is_zeroed = (Bool)arg[3];
+
+      MAC_(create_mempool) ( pool, rzB, is_zeroed );
+      return True;
+   }
+
+   case VG_USERREQ__DESTROY_MEMPOOL: {
+      Addr pool      = (Addr)arg[1];
+
+      MAC_(destroy_mempool) ( pool );
+      return True;
+   }
+
+   case VG_USERREQ__MEMPOOL_ALLOC: {
+      Addr pool      = (Addr)arg[1];
+      Addr addr      = (Addr)arg[2];
+      UInt size      =       arg[3];
+
+      MAC_(mempool_alloc) ( pool, addr, size );
+      return True;
+   }
+
+   case VG_USERREQ__MEMPOOL_FREE: {
+      Addr pool      = (Addr)arg[1];
+      Addr addr      = (Addr)arg[2];
+
+      MAC_(mempool_free) ( pool, addr );
+      return True;
+   }
+
    default:
       return False;
    }
diff --git a/memcheck/mac_shared.h b/memcheck/mac_shared.h
index c968a08..1ae789f 100644
--- a/memcheck/mac_shared.h
+++ b/memcheck/mac_shared.h
@@ -51,7 +51,8 @@
       Stack, 
       Unknown,      /* classification yielded nothing useful */
       Freed, Mallocd, 
-      UserG  /* in a user-defined block; Addrcheck & Memcheck only */
+      UserG,        /* in a user-defined block; Addrcheck & Memcheck only */
+      Mempool,      /* in a mempool; Addrcheck & Memcheck only */
    }
    AddrKind;
 
@@ -88,7 +89,9 @@
       /* Overlapping blocks in memcpy(), strcpy(), etc */
       OverlapSupp,
       /* Something to be suppressed in a leak check. */
-      LeakSupp
+      LeakSupp,
+      /* Memory pool suppression. */
+      MempoolSupp,
    } 
    MAC_SuppKind;
 
@@ -100,7 +103,8 @@
           ParamErr, UserErr,  /* behaves like an anonymous ParamErr */
           FreeErr, FreeMismatchErr,
           OverlapErr,
-          LeakErr
+          LeakErr,
+          IllegalMempoolErr,
    }
    MAC_ErrorKind;
 
@@ -153,6 +157,18 @@
    }
    MAC_Chunk;
 
+/* Memory pool.  Nb: first two fields must match core's VgHashNode. */
+typedef
+   struct _MAC_Mempool {
+      struct _MAC_Mempool* next;
+      Addr          pool;           /* pool identifier                       */
+      UInt          rzB;            /* pool red-zone size                    */
+      Bool          is_zeroed;      /* allocations from this pool are zeroed */
+      VgHashTable   chunks;         /* chunks associated with this pool      */
+   }
+   MAC_Mempool;
+
+
 /*------------------------------------------------------------*/
 /*--- Profiling of tools and memory events                 ---*/
 /*------------------------------------------------------------*/
@@ -270,6 +286,9 @@
 /* For tracking malloc'd blocks */
 extern VgHashTable MAC_(malloc_list);
 
+/* For tracking memory pools. */
+extern VgHashTable MAC_(mempool_list);
+
 /* Function pointers for the two tools to track interesting events. */
 extern void (*MAC_(new_mem_heap)) ( Addr a, UInt len, Bool is_inited );
 extern void (*MAC_(ban_mem_heap)) ( Addr a, UInt len );
@@ -298,10 +317,16 @@
 
 extern Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su );
 
-extern void MAC_(new_block) ( Addr p, UInt size, UInt rzB,
-                              Bool is_zeroed, MAC_AllocKind kind );
+extern MAC_Chunk* MAC_(new_block) ( Addr p, UInt size, UInt rzB,
+                                    Bool is_zeroed, MAC_AllocKind kind,
+                                    VgHashTable table);
 extern void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind );
 
+extern void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed);
+extern void MAC_(destroy_mempool)(Addr pool);
+extern void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size);
+extern void MAC_(mempool_free)(Addr pool, Addr addr);
+
 extern void MAC_(record_address_error)     ( ThreadId tid, Addr a,
                                              Int size, Bool isWrite );
 extern void MAC_(record_core_mem_error)    ( ThreadId tid, Bool isWrite,
@@ -312,6 +337,7 @@
 extern void MAC_(record_free_error)        ( ThreadId tid, Addr a );
 extern void MAC_(record_freemismatch_error)( ThreadId tid, Addr a );
 extern void MAC_(record_overlap_error)     ( Char* function, OverlapExtra* oe );
+extern void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr pool );
 
 extern void MAC_(pp_shared_SkinError)      ( Error* err);
 
diff --git a/memcheck/mc_clientreqs.c b/memcheck/mc_clientreqs.c
index 9f383f6..9592237 100644
--- a/memcheck/mc_clientreqs.c
+++ b/memcheck/mc_clientreqs.c
@@ -134,6 +134,37 @@
       if (vg_cgbs[i].kind == CG_NotInUse) 
          continue;
       if (VG_(addr_is_in_block)(a, vg_cgbs[i].start, vg_cgbs[i].size)) {
+         MAC_Mempool **d, *mp;
+
+         /* OK - maybe it's a mempool, too? */
+         mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list),
+                                             (UInt)vg_cgbs[i].start,
+                                             (VgHashNode***)&d);
+         if(mp != NULL) {
+            if(mp->chunks != NULL) {
+               MAC_Chunk *mc;
+
+               Bool find_addr(VgHashNode* sh_ch)
+               {
+                  MAC_Chunk *m = (MAC_Chunk*)sh_ch;
+                  return VG_(addr_is_in_block)(a, m->data, m->size);
+               }
+
+               mc = (MAC_Chunk*)VG_(HT_first_match)(mp->chunks, find_addr);
+               if(mc != NULL) {
+                  ai->akind = UserG;
+                  ai->blksize = mc->size;
+                  ai->rwoffset = (Int)(a) - (Int)mc->data;
+                  ai->lastchange = mc->where;
+                  return True;
+               }
+            }
+            ai->akind = Mempool;
+            ai->blksize = vg_cgbs[i].size;
+            ai->rwoffset  = (Int)(a) - (Int)(vg_cgbs[i].start);
+            ai->lastchange = vg_cgbs[i].where;
+            return True;
+         }
          ai->akind = UserG;
          ai->blksize = vg_cgbs[i].size;
          ai->rwoffset  = (Int)(a) - (Int)(vg_cgbs[i].start);
@@ -152,7 +183,11 @@
 
    if (!VG_IS_SKIN_USERREQ('M','C',arg[0])
     && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0]
-    && VG_USERREQ__FREELIKE_BLOCK   != arg[0])
+    && VG_USERREQ__FREELIKE_BLOCK   != arg[0]
+    && VG_USERREQ__CREATE_MEMPOOL   != arg[0]
+    && VG_USERREQ__DESTROY_MEMPOOL  != arg[0]
+    && VG_USERREQ__MEMPOOL_ALLOC    != arg[0]
+    && VG_USERREQ__MEMPOOL_FREE     != arg[0])
       return False;
 
    switch (arg[0]) {
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index 37a3dba..3d38c06 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -43,6 +43,7 @@
 	manuel3.stderr.exp manuel3.vgtest \
 	memalign_test.stderr.exp memalign_test.vgtest \
 	memcmptest.stderr.exp memcmptest.stdout.exp memcmptest.vgtest \
+	mempool.stderr.exp mempool.vgtest \
 	mismatches.stderr.exp mismatches.vgtest \
 	mmaptest.stderr.exp mmaptest.vgtest \
 	nanoleak.stderr.exp nanoleak.vgtest \
@@ -78,8 +79,8 @@
 	doublefree error_counts errs1 exitprog execve \
 	fpeflags fprw fwrite inits inline \
 	malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \
-	memalign_test memcmptest mmaptest nanoleak new_nothrow null_socket \
-	overlap pushfpopf \
+	memalign_test memcmptest mempool mmaptest nanoleak new_nothrow \
+	null_socket overlap pushfpopf \
 	realloc1 realloc2 realloc3 sigaltstack signal2 supp1 supp2 suppfree \
 	trivialleak tronical weirdioctl	\
 	mismatches new_override metadata threadederrno writev zeropage
@@ -117,6 +118,7 @@
 mmaptest_SOURCES 	= mmaptest.c
 memalign_test_SOURCES 	= memalign_test.c
 memcmptest_SOURCES 	= memcmptest.c
+mempool_SOURCES 	= mempool.c
 nanoleak_SOURCES 	= nanoleak.c
 null_socket_SOURCES 	= null_socket.c
 overlap_SOURCES 	= overlap.c
diff --git a/memcheck/tests/mempool.c b/memcheck/tests/mempool.c
new file mode 100644
index 0000000..f40c1b8
--- /dev/null
+++ b/memcheck/tests/mempool.c
@@ -0,0 +1,150 @@
+#include <unistd.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "../memcheck.h"
+
+#define SUPERBLOCK_SIZE 100000
+#define REDZONE_SIZE 8
+
+static const int USE_MMAP = 0;
+
+typedef struct _level_list
+{
+   struct _level_list *next;
+   char *where;
+} level_list;
+
+typedef struct _pool {
+   char *mem;
+   char *where; 
+   int size, left;
+   level_list *levels;
+} pool;
+
+pool *make_pool()
+{
+   pool *p;
+
+   if(USE_MMAP) {
+      p = (pool *)mmap(0, sizeof(pool), PROT_READ|PROT_WRITE|PROT_EXEC,
+                       MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+      p->where = p->mem = (char *)mmap(NULL, SUPERBLOCK_SIZE,
+                                       PROT_READ|PROT_WRITE|PROT_EXEC,
+                                       MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+   } else {
+      p = (pool *)malloc(sizeof(pool));
+      p->where = p->mem = (char *)malloc(SUPERBLOCK_SIZE);
+   }
+
+   p->size = p->left = SUPERBLOCK_SIZE;
+   p->levels = NULL;
+   VALGRIND_MAKE_NOACCESS(p->where, SUPERBLOCK_SIZE);
+   return p;
+}
+
+void push(pool *p)
+{
+   level_list *l;
+
+   if(USE_MMAP)
+      l = (level_list *)mmap(0, sizeof(level_list),
+                             PROT_READ|PROT_WRITE|PROT_EXEC,
+                             MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+   else
+      l = (level_list *)malloc(sizeof(level_list));
+
+   l->next = p->levels;
+   l->where = p->where;
+   VALGRIND_CREATE_MEMPOOL(l->where, REDZONE_SIZE, 0);
+   p->levels = l;
+}
+
+void pop(pool *p)
+{
+   level_list *l = p->levels;
+   p->levels = l->next;
+   VALGRIND_DESTROY_MEMPOOL(l->where);
+   VALGRIND_MAKE_NOACCESS(l->where, p->where-l->where);
+   p->where = l->where;
+   if(USE_MMAP)
+      munmap(l, sizeof(level_list));
+   else
+      free(l);
+}
+
+void destroy_pool(pool *p)
+{
+   level_list *l = p->levels;
+
+   while(l) {
+      pop(p);
+   }
+   if(USE_MMAP) {
+      munmap(p->mem, SUPERBLOCK_SIZE);
+      munmap(p, sizeof(pool));
+   } else {
+      free(p->mem);
+      free(p);
+   }
+}
+
+char *allocate(pool *p, int size)
+{
+   char *where;
+   p->left -= size + (REDZONE_SIZE*2);
+   where = p->where + REDZONE_SIZE;
+   p->where += size + (REDZONE_SIZE*2);
+   VALGRIND_MEMPOOL_ALLOC(p->levels->where, where, size);
+   return where;
+}
+
+//-------------------------------------------------------------------------
+// Rest
+//-------------------------------------------------------------------------
+
+void test(void)
+{
+   char *x1, *x2, *x3, *x4, *x5;
+
+   pool *p = make_pool();
+
+   push(p);
+
+   x1 = allocate(p, 10);
+   x2 = allocate(p, 20);
+   push(p);
+   x3 = allocate(p, 10);
+   x4 = allocate(p, 20);
+
+   *x1 = 'a';  // valid
+   *x2 = 'b';  // valid
+
+   x1[-1] = 'h'; // invalid
+   x1[10] = 'i'; // invalid
+
+   pop(p);
+
+   *x3 = 'c';  // invalid
+   *x4 = 'd';  // invalid
+
+   *x1 = 'e';  // valid
+   *x2 = 'f';  // valid
+
+   x5 = allocate(p, 10);
+
+   *x5 = 'g';  // valid
+
+   // pop(p);
+
+   // *x5 = 'g';  // invalid
+
+   // destroy_pool(p);
+}
+
+int main(void)
+{
+   test();
+   return 0;
+}
diff --git a/memcheck/tests/mempool.stderr.exp b/memcheck/tests/mempool.stderr.exp
new file mode 100644
index 0000000..26de5ac
--- /dev/null
+++ b/memcheck/tests/mempool.stderr.exp
@@ -0,0 +1,38 @@
+Invalid write of size 1
+   at 0x........: test (mempool.c:124)
+   by 0x........: main (mempool.c:148)
+ Address 0x........ is 1 bytes before a block of size 10 client-defined
+   at 0x........: allocate (mempool.c:99)
+   by 0x........: test (mempool.c:115)
+   by 0x........: main (mempool.c:148)
+
+Invalid write of size 1
+   at 0x........: test (mempool.c:125)
+   by 0x........: main (mempool.c:148)
+ Address 0x........ is 0 bytes after a block of size 10 client-defined
+   at 0x........: allocate (mempool.c:99)
+   by 0x........: test (mempool.c:115)
+   by 0x........: main (mempool.c:148)
+
+Invalid write of size 1
+   at 0x........: test (mempool.c:129)
+   by 0x........: main (mempool.c:148)
+ Address 0x........ is 70 bytes inside a mempool of size 100000 client-defined
+   at 0x........: make_pool (mempool.c:43)
+   by 0x........: test (mempool.c:111)
+   by 0x........: main (mempool.c:148)
+
+Invalid write of size 1
+   at 0x........: test (mempool.c:130)
+   by 0x........: main (mempool.c:148)
+ Address 0x........ is 96 bytes inside a mempool of size 100000 client-defined
+   at 0x........: make_pool (mempool.c:43)
+   by 0x........: test (mempool.c:111)
+   by 0x........: main (mempool.c:148)
+
+
+20 bytes in 1 blocks are definitely lost in loss record 2 of 3
+   at 0x........: malloc (vg_replace_malloc.c:...)
+   by 0x........: make_pool (mempool.c:37)
+   by 0x........: test (mempool.c:111)
+   by 0x........: main (mempool.c:148)
diff --git a/memcheck/tests/mempool.vgtest b/memcheck/tests/mempool.vgtest
new file mode 100644
index 0000000..bcf4ede
--- /dev/null
+++ b/memcheck/tests/mempool.vgtest
@@ -0,0 +1,2 @@
+prog: mempool
+vgopts: -q --leak-check=yes