1
2/*--------------------------------------------------------------------*/
3/*--- malloc/free wrappers for detecting errors and updating bits. ---*/
4/*---                                         mc_malloc_wrappers.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8   This file is part of MemCheck, a heavyweight Valgrind tool for
9   detecting memory errors.
10
11   Copyright (C) 2000-2011 Julian Seward
12      jseward@acm.org
13
14   This program is free software; you can redistribute it and/or
15   modify it under the terms of the GNU General Public License as
16   published by the Free Software Foundation; either version 2 of the
17   License, or (at your option) any later version.
18
19   This program is distributed in the hope that it will be useful, but
20   WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22   General Public License for more details.
23
24   You should have received a copy of the GNU General Public License
25   along with this program; if not, write to the Free Software
26   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27   02111-1307, USA.
28
29   The GNU General Public License is contained in the file COPYING.
30*/
31
32#include "pub_tool_basics.h"
33#include "pub_tool_execontext.h"
34#include "pub_tool_hashtable.h"
35#include "pub_tool_libcbase.h"
36#include "pub_tool_libcassert.h"
37#include "pub_tool_libcprint.h"
38#include "pub_tool_mallocfree.h"
39#include "pub_tool_options.h"
40#include "pub_tool_replacemalloc.h"
41#include "pub_tool_threadstate.h"
42#include "pub_tool_tooliface.h"     // Needed for mc_include.h
43#include "pub_tool_stacktrace.h"    // For VG_(get_and_pp_StackTrace)
44
45#include "mc_include.h"
46
47/*------------------------------------------------------------*/
48/*--- Defns                                                ---*/
49/*------------------------------------------------------------*/
50
51/* Stats ... */
52static SizeT cmalloc_n_mallocs  = 0;
53static SizeT cmalloc_n_frees    = 0;
54static ULong cmalloc_bs_mallocd = 0;
55
56/* For debug printing to do with mempools: what stack trace
57   depth to show. */
58#define MEMPOOL_DEBUG_STACKTRACE_DEPTH 16
59
60
61/*------------------------------------------------------------*/
62/*--- Tracking malloc'd and free'd blocks                  ---*/
63/*------------------------------------------------------------*/
64
65/* Record malloc'd blocks. */
66VgHashTable MC_(malloc_list) = NULL;
67
68/* Memory pools: a hash table of MC_Mempools.  Search key is
69   MC_Mempool::pool. */
70VgHashTable MC_(mempool_list) = NULL;
71
72/* Records blocks after freeing. */
73/* Blocks freed by the client are queued in one of two lists of
74   freed blocks not yet physically freed:
75   "big blocks" freed list.
76   "small blocks" freed list
77   The blocks with a size >= MC_(clo_freelist_big_blocks)
78   are linked in the big blocks freed list.
79   This allows a client to allocate and free big blocks
80   (e.g. bigger than VG_(clo_freelist_vol)) without losing
81   immediately all protection against dangling pointers.
82   position [0] is for big blocks, [1] is for small blocks. */
83static MC_Chunk* freed_list_start[2]  = {NULL, NULL};
84static MC_Chunk* freed_list_end[2]    = {NULL, NULL};
85
86/* Put a shadow chunk on the freed blocks queue, possibly freeing up
87   some of the oldest blocks in the queue at the same time. */
88static void add_to_freed_queue ( MC_Chunk* mc )
89{
90   const Bool show = False;
91   const int l = (mc->szB >= MC_(clo_freelist_big_blocks) ? 0 : 1);
92
93   /* Put it at the end of the freed list, unless the block
94      would be directly released any way : in this case, we
95      put it at the head of the freed list. */
96   if (freed_list_end[l] == NULL) {
97      tl_assert(freed_list_start[l] == NULL);
98      mc->next = NULL;
99      freed_list_end[l]    = freed_list_start[l] = mc;
100   } else {
101      tl_assert(freed_list_end[l]->next == NULL);
102      if (mc->szB >= MC_(clo_freelist_vol)) {
103         mc->next = freed_list_start[l];
104         freed_list_start[l] = mc;
105      } else {
106         mc->next = NULL;
107         freed_list_end[l]->next = mc;
108         freed_list_end[l]       = mc;
109      }
110   }
111   VG_(free_queue_volume) += (Long)mc->szB;
112   if (show)
113      VG_(printf)("mc_freelist: acquire: volume now %lld\n",
114                  VG_(free_queue_volume));
115   VG_(free_queue_length)++;
116}
117
118/* Release enough of the oldest blocks to bring the free queue
119   volume below vg_clo_freelist_vol.
120   Start with big block list first.
121   On entry, VG_(free_queue_volume) must be > MC_(clo_freelist_vol).
122   On exit, VG_(free_queue_volume) will be <= MC_(clo_freelist_vol). */
123static void release_oldest_block(void)
124{
125   const Bool show = False;
126   int i;
127   tl_assert (VG_(free_queue_volume) > MC_(clo_freelist_vol));
128   tl_assert (freed_list_start[0] != NULL || freed_list_start[1] != NULL);
129
130   for (i = 0; i < 2; i++) {
131      while (VG_(free_queue_volume) > MC_(clo_freelist_vol)
132             && freed_list_start[i] != NULL) {
133         MC_Chunk* mc1;
134         tl_assert(freed_list_end[i] != NULL);
135
136         mc1 = freed_list_start[i];
137         VG_(free_queue_volume) -= (Long)mc1->szB;
138         VG_(free_queue_length)--;
139         if (show)
140            VG_(printf)("mc_freelist: discard: volume now %lld\n",
141                        VG_(free_queue_volume));
142         tl_assert(VG_(free_queue_volume) >= 0);
143
144         if (freed_list_start[i] == freed_list_end[i]) {
145            freed_list_start[i] = freed_list_end[i] = NULL;
146         } else {
147            freed_list_start[i] = mc1->next;
148         }
149         mc1->next = NULL; /* just paranoia */
150         /* free MC_Chunk */
151         if (MC_AllocCustom != mc1->allockind)
152            VG_(cli_free) ( (void*)(mc1->data) );
153         VG_(free) ( mc1 );
154       }
155   }
156}
157
158MC_Chunk* MC_(get_freed_block_bracketting) (Addr a)
159{
160   int i;
161   for (i = 0; i < 2; i++) {
162      MC_Chunk*  mc;
163      mc = freed_list_start[i];
164      while (mc) {
165         if (VG_(addr_is_in_block)( a, mc->data, mc->szB,
166                                    MC_MALLOC_REDZONE_SZB ))
167            return mc;
168         mc = mc->next;
169      }
170   }
171   return NULL;
172}
173
174/* Allocate a shadow chunk, put it on the appropriate list.
175   If needed, release oldest blocks from freed list. */
176static
177MC_Chunk* create_MC_Chunk ( ExeContext* ec, Addr p, SizeT szB,
178                            MC_AllocKind kind)
179{
180   MC_Chunk* mc  = VG_(malloc)("mc.cMC.1 (a MC_Chunk)", sizeof(MC_Chunk));
181   mc->data      = p;
182   mc->szB       = szB;
183   mc->allockind = kind;
184   mc->where     = ec;
185
186   /* Each time a new MC_Chunk is created, release oldest blocks
187      if the free list volume is exceeded. */
188   if (VG_(free_queue_volume) > MC_(clo_freelist_vol))
189      release_oldest_block();
190
191   /* Paranoia ... ensure the MC_Chunk is off-limits to the client, so
192      the mc->data field isn't visible to the leak checker.  If memory
193      management is working correctly, any pointer returned by VG_(malloc)
194      should be noaccess as far as the client is concerned. */
195   if (!MC_(check_mem_is_noaccess)( (Addr)mc, sizeof(MC_Chunk), NULL )) {
196      VG_(tool_panic)("create_MC_Chunk: shadow area is accessible");
197   }
198   return mc;
199}
200
201/*------------------------------------------------------------*/
202/*--- client_malloc(), etc                                 ---*/
203/*------------------------------------------------------------*/
204
205// XXX: should make this a proper error (bug #79311).
206static Bool complain_about_silly_args(SizeT sizeB, Char* fn)
207{
208   // Cast to a signed type to catch any unexpectedly negative args.  We're
209   // assuming here that the size asked for is not greater than 2^31 bytes
210   // (for 32-bit platforms) or 2^63 bytes (for 64-bit platforms).
211   if ((SSizeT)sizeB < 0) {
212      if (!VG_(clo_xml))
213         VG_(message)(Vg_UserMsg, "Warning: silly arg (%ld) to %s()\n",
214                      (SSizeT)sizeB, fn );
215      return True;
216   }
217   return False;
218}
219
220static Bool complain_about_silly_args2(SizeT n, SizeT sizeB)
221{
222   if ((SSizeT)n < 0 || (SSizeT)sizeB < 0) {
223      if (!VG_(clo_xml))
224         VG_(message)(Vg_UserMsg,
225                      "Warning: silly args (%ld,%ld) to calloc()\n",
226                      (SSizeT)n, (SSizeT)sizeB);
227      return True;
228   }
229   return False;
230}
231
232/* Allocate memory and note change in memory available */
233void* MC_(new_block) ( ThreadId tid,
234                       Addr p, SizeT szB, SizeT alignB,
235                       Bool is_zeroed, MC_AllocKind kind, VgHashTable table)
236{
237   ExeContext* ec;
238
239   cmalloc_n_mallocs ++;
240
241   // Allocate and zero if necessary
242   if (p) {
243      tl_assert(MC_AllocCustom == kind);
244   } else {
245      tl_assert(MC_AllocCustom != kind);
246      p = (Addr)VG_(cli_malloc)( alignB, szB );
247      if (!p) {
248         return NULL;
249      }
250      if (is_zeroed) {
251         VG_(memset)((void*)p, 0, szB);
252      } else
253      if (MC_(clo_malloc_fill) != -1) {
254         tl_assert(MC_(clo_malloc_fill) >= 0x00 && MC_(clo_malloc_fill) <= 0xFF);
255         VG_(memset)((void*)p, MC_(clo_malloc_fill), szB);
256      }
257   }
258
259   // Only update this stat if allocation succeeded.
260   cmalloc_bs_mallocd += (ULong)szB;
261
262   ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
263   tl_assert(ec);
264
265   VG_(HT_add_node)( table, create_MC_Chunk(ec, p, szB, kind) );
266
267   if (is_zeroed)
268      MC_(make_mem_defined)( p, szB );
269   else {
270      UInt ecu = VG_(get_ECU_from_ExeContext)(ec);
271      tl_assert(VG_(is_plausible_ECU)(ecu));
272      MC_(make_mem_undefined_w_otag)( p, szB, ecu | MC_OKIND_HEAP );
273   }
274
275   return (void*)p;
276}
277
278void* MC_(malloc) ( ThreadId tid, SizeT n )
279{
280   if (complain_about_silly_args(n, "malloc")) {
281      return NULL;
282   } else {
283      return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
284         /*is_zeroed*/False, MC_AllocMalloc, MC_(malloc_list));
285   }
286}
287
288void* MC_(__builtin_new) ( ThreadId tid, SizeT n )
289{
290   if (complain_about_silly_args(n, "__builtin_new")) {
291      return NULL;
292   } else {
293      return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
294         /*is_zeroed*/False, MC_AllocNew, MC_(malloc_list));
295   }
296}
297
298void* MC_(__builtin_vec_new) ( ThreadId tid, SizeT n )
299{
300   if (complain_about_silly_args(n, "__builtin_vec_new")) {
301      return NULL;
302   } else {
303      return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
304         /*is_zeroed*/False, MC_AllocNewVec, MC_(malloc_list));
305   }
306}
307
308void* MC_(memalign) ( ThreadId tid, SizeT alignB, SizeT n )
309{
310   if (complain_about_silly_args(n, "memalign")) {
311      return NULL;
312   } else {
313      return MC_(new_block) ( tid, 0, n, alignB,
314         /*is_zeroed*/False, MC_AllocMalloc, MC_(malloc_list));
315   }
316}
317
318void* MC_(calloc) ( ThreadId tid, SizeT nmemb, SizeT size1 )
319{
320   if (complain_about_silly_args2(nmemb, size1)) {
321      return NULL;
322   } else {
323      return MC_(new_block) ( tid, 0, nmemb*size1, VG_(clo_alignment),
324         /*is_zeroed*/True, MC_AllocMalloc, MC_(malloc_list));
325   }
326}
327
328static
329void die_and_free_mem ( ThreadId tid, MC_Chunk* mc, SizeT rzB )
330{
331   if (MC_(clo_free_fill) != -1) {
332      tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
333      VG_(memset)((void*)mc->data, MC_(clo_free_fill), mc->szB);
334   }
335
336   /* Note: make redzones noaccess again -- just in case user made them
337      accessible with a client request... */
338   MC_(make_mem_noaccess)( mc->data-rzB, mc->szB + 2*rzB );
339
340   /* Record where freed */
341   mc->where = VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ );
342   /* Put it out of harm's way for a while */
343   add_to_freed_queue ( mc );
344   /* If the free list volume is bigger than MC_(clo_freelist_vol),
345      we wait till the next block allocation to release blocks.
346      This increase the chance to discover dangling pointer usage,
347      even for big blocks being freed by the client. */
348}
349
350void MC_(handle_free) ( ThreadId tid, Addr p, UInt rzB, MC_AllocKind kind )
351{
352   MC_Chunk* mc;
353
354   cmalloc_n_frees++;
355
356   mc = VG_(HT_remove) ( MC_(malloc_list), (UWord)p );
357   if (mc == NULL) {
358      MC_(record_free_error) ( tid, p );
359   } else {
360      /* check if it is a matching free() / delete / delete [] */
361      if (kind != mc->allockind) {
362         tl_assert(p == mc->data);
363         MC_(record_freemismatch_error) ( tid, mc );
364      }
365      die_and_free_mem ( tid, mc, rzB );
366   }
367}
368
369void MC_(free) ( ThreadId tid, void* p )
370{
371   MC_(handle_free)(
372      tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocMalloc );
373}
374
375void MC_(__builtin_delete) ( ThreadId tid, void* p )
376{
377   MC_(handle_free)(
378      tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocNew);
379}
380
381void MC_(__builtin_vec_delete) ( ThreadId tid, void* p )
382{
383   MC_(handle_free)(
384      tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocNewVec);
385}
386
387void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB )
388{
389   MC_Chunk* mc;
390   void*     p_new;
391   SizeT     old_szB;
392
393   cmalloc_n_frees ++;
394   cmalloc_n_mallocs ++;
395   cmalloc_bs_mallocd += (ULong)new_szB;
396
397   if (complain_about_silly_args(new_szB, "realloc"))
398      return NULL;
399
400   /* Remove the old block */
401   mc = VG_(HT_remove) ( MC_(malloc_list), (UWord)p_old );
402   if (mc == NULL) {
403      MC_(record_free_error) ( tid, (Addr)p_old );
404      /* We return to the program regardless. */
405      return NULL;
406   }
407
408   /* check if its a matching free() / delete / delete [] */
409   if (MC_AllocMalloc != mc->allockind) {
410      /* can not realloc a range that was allocated with new or new [] */
411      tl_assert((Addr)p_old == mc->data);
412      MC_(record_freemismatch_error) ( tid, mc );
413      /* but keep going anyway */
414   }
415
416   old_szB = mc->szB;
417
418   /* In all cases, even when the new size is smaller or unchanged, we
419      reallocate and copy the contents, and make the old block
420      inaccessible.  This is so as to guarantee to catch all cases of
421      accesses via the old address after reallocation, regardless of
422      the change in size.  (Of course the ability to detect accesses
423      to the old block also depends on the size of the freed blocks
424      queue). */
425
426   if (new_szB <= old_szB) {
427      /* new size is smaller or the same */
428      Addr a_new;
429      /* Get new memory */
430      a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
431
432      if (a_new) {
433         ExeContext* ec;
434
435         ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
436         tl_assert(ec);
437
438         /* Retained part is copied, red zones set as normal */
439         MC_(make_mem_noaccess)( a_new-MC_MALLOC_REDZONE_SZB,
440                                 MC_MALLOC_REDZONE_SZB );
441         MC_(copy_address_range_state) ( (Addr)p_old, a_new, new_szB );
442         MC_(make_mem_noaccess)        ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
443
444         /* Copy from old to new */
445         VG_(memcpy)((void*)a_new, p_old, new_szB);
446
447         /* Possibly fill freed area with specified junk. */
448         if (MC_(clo_free_fill) != -1) {
449            tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
450            VG_(memset)((void*)p_old, MC_(clo_free_fill), old_szB);
451         }
452
453         /* Free old memory */
454         /* Nb: we have to allocate a new MC_Chunk for the new memory rather
455            than recycling the old one, so that any erroneous accesses to the
456            old memory are reported. */
457         die_and_free_mem ( tid, mc, MC_MALLOC_REDZONE_SZB );
458
459         // Allocate a new chunk.
460         mc = create_MC_Chunk( ec, a_new, new_szB, MC_AllocMalloc );
461      }
462
463      p_new = (void*)a_new;
464
465   } else {
466      /* new size is bigger */
467      Addr a_new;
468      tl_assert(old_szB < new_szB);
469      /* Get new memory */
470      a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
471
472      if (a_new) {
473         UInt        ecu;
474         ExeContext* ec;
475
476         ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
477         tl_assert(ec);
478         ecu = VG_(get_ECU_from_ExeContext)(ec);
479         tl_assert(VG_(is_plausible_ECU)(ecu));
480
481         /* First half kept and copied, second half new, red zones as normal */
482         MC_(make_mem_noaccess)( a_new-MC_MALLOC_REDZONE_SZB,
483                                 MC_MALLOC_REDZONE_SZB );
484         MC_(copy_address_range_state) ( (Addr)p_old, a_new, mc->szB );
485         MC_(make_mem_undefined_w_otag)( a_new+mc->szB, new_szB-mc->szB,
486                                                        ecu | MC_OKIND_HEAP );
487         MC_(make_mem_noaccess)        ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
488
489         /* Possibly fill new area with specified junk */
490         if (MC_(clo_malloc_fill) != -1) {
491            tl_assert(MC_(clo_malloc_fill) >= 0x00
492                      && MC_(clo_malloc_fill) <= 0xFF);
493            VG_(memset)((void*)(a_new+old_szB), MC_(clo_malloc_fill),
494                                                new_szB-old_szB);
495         }
496
497         /* Copy from old to new */
498         VG_(memcpy)((void*)a_new, p_old, mc->szB);
499
500         /* Possibly fill freed area with specified junk. */
501         if (MC_(clo_free_fill) != -1) {
502            tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
503            VG_(memset)((void*)p_old, MC_(clo_free_fill), old_szB);
504         }
505
506         /* Free old memory */
507         /* Nb: we have to allocate a new MC_Chunk for the new memory rather
508            than recycling the old one, so that any erroneous accesses to the
509            old memory are reported. */
510         die_and_free_mem ( tid, mc, MC_MALLOC_REDZONE_SZB );
511
512         // Allocate a new chunk.
513         mc = create_MC_Chunk( ec, a_new, new_szB, MC_AllocMalloc );
514      }
515
516      p_new = (void*)a_new;
517   }
518
519   // Now insert the new mc (with a possibly new 'data' field) into
520   // malloc_list.  If this realloc() did not increase the memory size, we
521   // will have removed and then re-added mc unnecessarily.  But that's ok
522   // because shrinking a block with realloc() is (presumably) much rarer
523   // than growing it, and this way simplifies the growing case.
524   VG_(HT_add_node)( MC_(malloc_list), mc );
525
526   return p_new;
527}
528
529SizeT MC_(malloc_usable_size) ( ThreadId tid, void* p )
530{
531   MC_Chunk* mc = VG_(HT_lookup) ( MC_(malloc_list), (UWord)p );
532
533   // There may be slop, but pretend there isn't because only the asked-for
534   // area will be marked as addressable.
535   return ( mc ? mc->szB : 0 );
536}
537
538/* This handles the in place resize of a block, as performed by the
539   VALGRIND_RESIZEINPLACE_BLOCK client request.  It is unrelated to,
540   and not used for, handling of the normal libc realloc()
541   function. */
542void MC_(handle_resizeInPlace)(ThreadId tid, Addr p,
543                               SizeT oldSizeB, SizeT newSizeB, SizeT rzB)
544{
545   MC_Chunk* mc = VG_(HT_lookup) ( MC_(malloc_list), (UWord)p );
546   if (!mc || mc->szB != oldSizeB || newSizeB == 0) {
547      /* Reject if: p is not found, or oldSizeB is wrong,
548         or new block would be empty. */
549      MC_(record_free_error) ( tid, p );
550      return;
551   }
552
553   if (oldSizeB == newSizeB)
554      return;
555
556   mc->szB = newSizeB;
557   if (newSizeB < oldSizeB) {
558      MC_(make_mem_noaccess)( p + newSizeB, oldSizeB - newSizeB + rzB );
559   } else {
560      ExeContext* ec  = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
561      UInt        ecu = VG_(get_ECU_from_ExeContext)(ec);
562      MC_(make_mem_undefined_w_otag)( p + oldSizeB, newSizeB - oldSizeB,
563                                      ecu | MC_OKIND_HEAP );
564      if (rzB > 0)
565         MC_(make_mem_noaccess)( p + newSizeB, rzB );
566   }
567}
568
569
570/*------------------------------------------------------------*/
571/*--- Memory pool stuff.                                   ---*/
572/*------------------------------------------------------------*/
573
574/* Set to 1 for intensive sanity checking.  Is very expensive though
575   and should not be used in production scenarios.  See #255966. */
576#define MP_DETAILED_SANITY_CHECKS 0
577
578static void check_mempool_sane(MC_Mempool* mp); /*forward*/
579
580
581void MC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed)
582{
583   MC_Mempool* mp;
584
585   if (VG_(clo_verbosity) > 2) {
586      VG_(message)(Vg_UserMsg, "create_mempool(0x%lx, %d, %d)\n",
587                               pool, rzB, is_zeroed);
588      VG_(get_and_pp_StackTrace)
589         (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
590   }
591
592   mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
593   if (mp != NULL) {
594     VG_(tool_panic)("MC_(create_mempool): duplicate pool creation");
595   }
596
597   mp = VG_(malloc)("mc.cm.1", sizeof(MC_Mempool));
598   mp->pool       = pool;
599   mp->rzB        = rzB;
600   mp->is_zeroed  = is_zeroed;
601   mp->chunks     = VG_(HT_construct)( "MC_(create_mempool)" );
602   check_mempool_sane(mp);
603
604   /* Paranoia ... ensure this area is off-limits to the client, so
605      the mp->data field isn't visible to the leak checker.  If memory
606      management is working correctly, anything pointer returned by
607      VG_(malloc) should be noaccess as far as the client is
608      concerned. */
609   if (!MC_(check_mem_is_noaccess)( (Addr)mp, sizeof(MC_Mempool), NULL )) {
610      VG_(tool_panic)("MC_(create_mempool): shadow area is accessible");
611   }
612
613   VG_(HT_add_node)( MC_(mempool_list), mp );
614}
615
616void MC_(destroy_mempool)(Addr pool)
617{
618   MC_Chunk*   mc;
619   MC_Mempool* mp;
620
621   if (VG_(clo_verbosity) > 2) {
622      VG_(message)(Vg_UserMsg, "destroy_mempool(0x%lx)\n", pool);
623      VG_(get_and_pp_StackTrace)
624         (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
625   }
626
627   mp = VG_(HT_remove) ( MC_(mempool_list), (UWord)pool );
628
629   if (mp == NULL) {
630      ThreadId tid = VG_(get_running_tid)();
631      MC_(record_illegal_mempool_error) ( tid, pool );
632      return;
633   }
634   check_mempool_sane(mp);
635
636   // Clean up the chunks, one by one
637   VG_(HT_ResetIter)(mp->chunks);
638   while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
639      /* Note: make redzones noaccess again -- just in case user made them
640         accessible with a client request... */
641      MC_(make_mem_noaccess)(mc->data-mp->rzB, mc->szB + 2*mp->rzB );
642   }
643   // Destroy the chunk table
644   VG_(HT_destruct)(mp->chunks);
645
646   VG_(free)(mp);
647}
648
649static Int
650mp_compar(void* n1, void* n2)
651{
652   MC_Chunk* mc1 = *(MC_Chunk**)n1;
653   MC_Chunk* mc2 = *(MC_Chunk**)n2;
654   if (mc1->data < mc2->data) return -1;
655   if (mc1->data > mc2->data) return  1;
656   return 0;
657}
658
659static void
660check_mempool_sane(MC_Mempool* mp)
661{
662   UInt n_chunks, i, bad = 0;
663   static UInt tick = 0;
664
665   MC_Chunk **chunks = (MC_Chunk**) VG_(HT_to_array)( mp->chunks, &n_chunks );
666   if (!chunks)
667      return;
668
669   if (VG_(clo_verbosity) > 1) {
670     if (tick++ >= 10000)
671       {
672	 UInt total_pools = 0, total_chunks = 0;
673	 MC_Mempool* mp2;
674
675	 VG_(HT_ResetIter)(MC_(mempool_list));
676	 while ( (mp2 = VG_(HT_Next)(MC_(mempool_list))) ) {
677	   total_pools++;
678	   VG_(HT_ResetIter)(mp2->chunks);
679	   while (VG_(HT_Next)(mp2->chunks)) {
680	     total_chunks++;
681	   }
682	 }
683
684         VG_(message)(Vg_UserMsg,
685                      "Total mempools active: %d pools, %d chunks\n",
686		      total_pools, total_chunks);
687	 tick = 0;
688       }
689   }
690
691
692   VG_(ssort)((void*)chunks, n_chunks, sizeof(VgHashNode*), mp_compar);
693
694   /* Sanity check; assert that the blocks are now in order */
695   for (i = 0; i < n_chunks-1; i++) {
696      if (chunks[i]->data > chunks[i+1]->data) {
697         VG_(message)(Vg_UserMsg,
698                      "Mempool chunk %d / %d is out of order "
699                      "wrt. its successor\n",
700                      i+1, n_chunks);
701         bad = 1;
702      }
703   }
704
705   /* Sanity check -- make sure they don't overlap */
706   for (i = 0; i < n_chunks-1; i++) {
707      if (chunks[i]->data + chunks[i]->szB > chunks[i+1]->data ) {
708         VG_(message)(Vg_UserMsg,
709                      "Mempool chunk %d / %d overlaps with its successor\n",
710                      i+1, n_chunks);
711         bad = 1;
712      }
713   }
714
715   if (bad) {
716         VG_(message)(Vg_UserMsg,
717                "Bad mempool (%d chunks), dumping chunks for inspection:\n",
718                n_chunks);
719         for (i = 0; i < n_chunks; ++i) {
720            VG_(message)(Vg_UserMsg,
721                         "Mempool chunk %d / %d: %ld bytes "
722                         "[%lx,%lx), allocated:\n",
723                         i+1,
724                         n_chunks,
725                         chunks[i]->szB + 0UL,
726                         chunks[i]->data,
727                         chunks[i]->data + chunks[i]->szB);
728
729            VG_(pp_ExeContext)(chunks[i]->where);
730         }
731   }
732   VG_(free)(chunks);
733}
734
735void MC_(mempool_alloc)(ThreadId tid, Addr pool, Addr addr, SizeT szB)
736{
737   MC_Mempool* mp;
738
739   if (VG_(clo_verbosity) > 2) {
740      VG_(message)(Vg_UserMsg, "mempool_alloc(0x%lx, 0x%lx, %ld)\n",
741                               pool, addr, szB);
742      VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
743   }
744
745   mp = VG_(HT_lookup) ( MC_(mempool_list), (UWord)pool );
746   if (mp == NULL) {
747      MC_(record_illegal_mempool_error) ( tid, pool );
748   } else {
749      if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
750      MC_(new_block)(tid, addr, szB, /*ignored*/0, mp->is_zeroed,
751                     MC_AllocCustom, mp->chunks);
752      if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
753   }
754}
755
756void MC_(mempool_free)(Addr pool, Addr addr)
757{
758   MC_Mempool*  mp;
759   MC_Chunk*    mc;
760   ThreadId     tid = VG_(get_running_tid)();
761
762   mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
763   if (mp == NULL) {
764      MC_(record_illegal_mempool_error)(tid, pool);
765      return;
766   }
767
768   if (VG_(clo_verbosity) > 2) {
769      VG_(message)(Vg_UserMsg, "mempool_free(0x%lx, 0x%lx)\n", pool, addr);
770      VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
771   }
772
773   if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
774   mc = VG_(HT_remove)(mp->chunks, (UWord)addr);
775   if (mc == NULL) {
776      MC_(record_free_error)(tid, (Addr)addr);
777      return;
778   }
779
780   if (VG_(clo_verbosity) > 2) {
781      VG_(message)(Vg_UserMsg,
782		   "mempool_free(0x%lx, 0x%lx) freed chunk of %ld bytes\n",
783		   pool, addr, mc->szB + 0UL);
784   }
785
786   die_and_free_mem ( tid, mc, mp->rzB );
787   if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
788}
789
790
791void MC_(mempool_trim)(Addr pool, Addr addr, SizeT szB)
792{
793   MC_Mempool*  mp;
794   MC_Chunk*    mc;
795   ThreadId     tid = VG_(get_running_tid)();
796   UInt         n_shadows, i;
797   VgHashNode** chunks;
798
799   if (VG_(clo_verbosity) > 2) {
800      VG_(message)(Vg_UserMsg, "mempool_trim(0x%lx, 0x%lx, %ld)\n",
801                               pool, addr, szB);
802      VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
803   }
804
805   mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
806   if (mp == NULL) {
807      MC_(record_illegal_mempool_error)(tid, pool);
808      return;
809   }
810
811   check_mempool_sane(mp);
812   chunks = VG_(HT_to_array) ( mp->chunks, &n_shadows );
813   if (n_shadows == 0) {
814     tl_assert(chunks == NULL);
815     return;
816   }
817
818   tl_assert(chunks != NULL);
819   for (i = 0; i < n_shadows; ++i) {
820
821      Addr lo, hi, min, max;
822
823      mc = (MC_Chunk*) chunks[i];
824
825      lo = mc->data;
826      hi = mc->szB == 0 ? mc->data : mc->data + mc->szB - 1;
827
828#define EXTENT_CONTAINS(x) ((addr <= (x)) && ((x) < addr + szB))
829
830      if (EXTENT_CONTAINS(lo) && EXTENT_CONTAINS(hi)) {
831
832         /* The current chunk is entirely within the trim extent: keep
833            it. */
834
835         continue;
836
837      } else if ( (! EXTENT_CONTAINS(lo)) &&
838                  (! EXTENT_CONTAINS(hi)) ) {
839
840         /* The current chunk is entirely outside the trim extent:
841            delete it. */
842
843         if (VG_(HT_remove)(mp->chunks, (UWord)mc->data) == NULL) {
844            MC_(record_free_error)(tid, (Addr)mc->data);
845            VG_(free)(chunks);
846            if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
847            return;
848         }
849         die_and_free_mem ( tid, mc, mp->rzB );
850
851      } else {
852
853         /* The current chunk intersects the trim extent: remove,
854            trim, and reinsert it. */
855
856         tl_assert(EXTENT_CONTAINS(lo) ||
857                   EXTENT_CONTAINS(hi));
858         if (VG_(HT_remove)(mp->chunks, (UWord)mc->data) == NULL) {
859            MC_(record_free_error)(tid, (Addr)mc->data);
860            VG_(free)(chunks);
861            if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
862            return;
863         }
864
865         if (mc->data < addr) {
866           min = mc->data;
867           lo = addr;
868         } else {
869           min = addr;
870           lo = mc->data;
871         }
872
873         if (mc->data + szB > addr + szB) {
874           max = mc->data + szB;
875           hi = addr + szB;
876         } else {
877           max = addr + szB;
878           hi = mc->data + szB;
879         }
880
881         tl_assert(min <= lo);
882         tl_assert(lo < hi);
883         tl_assert(hi <= max);
884
885         if (min < lo && !EXTENT_CONTAINS(min)) {
886           MC_(make_mem_noaccess)( min, lo - min);
887         }
888
889         if (hi < max && !EXTENT_CONTAINS(max)) {
890           MC_(make_mem_noaccess)( hi, max - hi );
891         }
892
893         mc->data = lo;
894         mc->szB = (UInt) (hi - lo);
895         VG_(HT_add_node)( mp->chunks, mc );
896      }
897
898#undef EXTENT_CONTAINS
899
900   }
901   check_mempool_sane(mp);
902   VG_(free)(chunks);
903}
904
905void MC_(move_mempool)(Addr poolA, Addr poolB)
906{
907   MC_Mempool* mp;
908
909   if (VG_(clo_verbosity) > 2) {
910      VG_(message)(Vg_UserMsg, "move_mempool(0x%lx, 0x%lx)\n", poolA, poolB);
911      VG_(get_and_pp_StackTrace)
912         (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
913   }
914
915   mp = VG_(HT_remove) ( MC_(mempool_list), (UWord)poolA );
916
917   if (mp == NULL) {
918      ThreadId tid = VG_(get_running_tid)();
919      MC_(record_illegal_mempool_error) ( tid, poolA );
920      return;
921   }
922
923   mp->pool = poolB;
924   VG_(HT_add_node)( MC_(mempool_list), mp );
925}
926
927void MC_(mempool_change)(Addr pool, Addr addrA, Addr addrB, SizeT szB)
928{
929   MC_Mempool*  mp;
930   MC_Chunk*    mc;
931   ThreadId     tid = VG_(get_running_tid)();
932
933   if (VG_(clo_verbosity) > 2) {
934      VG_(message)(Vg_UserMsg, "mempool_change(0x%lx, 0x%lx, 0x%lx, %ld)\n",
935                   pool, addrA, addrB, szB);
936      VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
937   }
938
939   mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
940   if (mp == NULL) {
941      MC_(record_illegal_mempool_error)(tid, pool);
942      return;
943   }
944
945   check_mempool_sane(mp);
946
947   mc = VG_(HT_remove)(mp->chunks, (UWord)addrA);
948   if (mc == NULL) {
949      MC_(record_free_error)(tid, (Addr)addrA);
950      return;
951   }
952
953   mc->data = addrB;
954   mc->szB  = szB;
955   VG_(HT_add_node)( mp->chunks, mc );
956
957   check_mempool_sane(mp);
958}
959
960Bool MC_(mempool_exists)(Addr pool)
961{
962   MC_Mempool*  mp;
963
964   mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
965   if (mp == NULL) {
966       return False;
967   }
968   return True;
969}
970
971
972/*------------------------------------------------------------*/
973/*--- Statistics printing                                  ---*/
974/*------------------------------------------------------------*/
975
976void MC_(print_malloc_stats) ( void )
977{
978   MC_Chunk* mc;
979   SizeT     nblocks = 0;
980   ULong     nbytes  = 0;
981
982   if (VG_(clo_verbosity) == 0)
983      return;
984   if (VG_(clo_xml))
985      return;
986
987   /* Count memory still in use. */
988   VG_(HT_ResetIter)(MC_(malloc_list));
989   while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) {
990      nblocks++;
991      nbytes += (ULong)mc->szB;
992   }
993
994   VG_(umsg)(
995      "HEAP SUMMARY:\n"
996      "    in use at exit: %'llu bytes in %'lu blocks\n"
997      "  total heap usage: %'lu allocs, %'lu frees, %'llu bytes allocated\n"
998      "\n",
999      nbytes, nblocks,
1000      cmalloc_n_mallocs,
1001      cmalloc_n_frees, cmalloc_bs_mallocd
1002   );
1003}
1004
1005/*--------------------------------------------------------------------*/
1006/*--- end                                                          ---*/
1007/*--------------------------------------------------------------------*/
1008