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