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