blob: 132fb50adaa944ab7530bae980c56478605d09b9 [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- An implementation of malloc/free for the client. ---*/
4/*--- vg_clientmalloc.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8 This file is part of Valgrind, an x86 protected-mode emulator
9 designed for debugging and profiling binaries on x86-Unixes.
10
11 Copyright (C) 2000-2002 Julian Seward
12 jseward@acm.org
13 Julian_Seward@muraroa.demon.co.uk
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of the
18 License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 02111-1307, USA.
29
30 The GNU General Public License is contained in the file LICENSE.
31*/
32
33#include "vg_include.h"
34
35
36/*------------------------------------------------------------*/
37/*--- Defns ---*/
38/*------------------------------------------------------------*/
39
40/* #define DEBUG_CLIENTMALLOC */
41
42/* Holds malloc'd but not freed blocks. */
43#define VG_MALLOCLIST_NO(aa) (((UInt)(aa)) % VG_N_MALLOCLISTS)
44static ShadowChunk* vg_malloclist[VG_N_MALLOCLISTS];
45static Bool vg_client_malloc_init_done = False;
46
47/* Holds blocks after freeing. */
48static ShadowChunk* vg_freed_list_start = NULL;
49static ShadowChunk* vg_freed_list_end = NULL;
50static Int vg_freed_list_volume = 0;
51
52/* Stats ... */
53static UInt vg_cmalloc_n_mallocs = 0;
54static UInt vg_cmalloc_n_frees = 0;
55static UInt vg_cmalloc_bs_mallocd = 0;
56
57static UInt vg_mlist_frees = 0;
58static UInt vg_mlist_tries = 0;
59
60
61/*------------------------------------------------------------*/
62/*--- Fns ---*/
63/*------------------------------------------------------------*/
64
65/* Allocate a suitably-sized array, copy all the malloc-d block
66 shadows into it, and return both the array and the size of it.
67 This is used by the memory-leak detector.
68*/
69ShadowChunk** VG_(get_malloc_shadows) ( /*OUT*/ UInt* n_shadows )
70{
71 UInt i, scn;
72 ShadowChunk** arr;
73 ShadowChunk* sc;
74 *n_shadows = 0;
75 for (scn = 0; scn < VG_N_MALLOCLISTS; scn++) {
76 for (sc = vg_malloclist[scn]; sc != NULL; sc = sc->next) {
77 (*n_shadows)++;
78 }
79 }
80 if (*n_shadows == 0) return NULL;
81
82 arr = VG_(malloc)( VG_AR_PRIVATE,
83 *n_shadows * sizeof(ShadowChunk*) );
84
85 i = 0;
86 for (scn = 0; scn < VG_N_MALLOCLISTS; scn++) {
87 for (sc = vg_malloclist[scn]; sc != NULL; sc = sc->next) {
88 arr[i++] = sc;
89 }
90 }
91 vg_assert(i == *n_shadows);
92 return arr;
93}
94
95static void client_malloc_init ( void )
96{
97 UInt ml_no;
98 if (vg_client_malloc_init_done) return;
99 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++)
100 vg_malloclist[ml_no] = NULL;
101 vg_client_malloc_init_done = True;
102}
103
104
105static __attribute__ ((unused))
106 Int count_freelist ( void )
107{
108 ShadowChunk* sc;
109 Int n = 0;
110 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
111 n++;
112 return n;
113}
114
115static __attribute__ ((unused))
116 Int count_malloclists ( void )
117{
118 ShadowChunk* sc;
119 UInt ml_no;
120 Int n = 0;
121 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++)
122 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next)
123 n++;
124 return n;
125}
126
127static __attribute__ ((unused))
128 void freelist_sanity ( void )
129{
130 ShadowChunk* sc;
131 Int n = 0;
132 /* VG_(printf)("freelist sanity\n"); */
133 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
134 n += sc->size;
135 vg_assert(n == vg_freed_list_volume);
136}
137
138/* Remove sc from malloc list # sc. It is an unchecked error for
139 sc not to be present in the list.
140*/
141static void remove_from_malloclist ( UInt ml_no, ShadowChunk* sc )
142{
143 ShadowChunk *sc1, *sc2;
144 if (sc == vg_malloclist[ml_no]) {
145 vg_malloclist[ml_no] = vg_malloclist[ml_no]->next;
146 } else {
147 sc1 = vg_malloclist[ml_no];
148 vg_assert(sc1 != NULL);
149 sc2 = sc1->next;
150 while (sc2 != sc) {
151 vg_assert(sc2 != NULL);
152 sc1 = sc2;
153 sc2 = sc2->next;
154 }
155 vg_assert(sc1->next == sc);
156 vg_assert(sc2 == sc);
157 sc1->next = sc2->next;
158 }
159}
160
161
162/* Put a shadow chunk on the freed blocks queue, possibly freeing up
163 some of the oldest blocks in the queue at the same time. */
164
165static void add_to_freed_queue ( ShadowChunk* sc )
166{
167 ShadowChunk* sc1;
168
169 /* Put it at the end of the freed list */
170 if (vg_freed_list_end == NULL) {
171 vg_assert(vg_freed_list_start == NULL);
172 vg_freed_list_end = vg_freed_list_start = sc;
173 vg_freed_list_volume = sc->size;
174 } else {
175 vg_assert(vg_freed_list_end->next == NULL);
176 vg_freed_list_end->next = sc;
177 vg_freed_list_end = sc;
178 vg_freed_list_volume += sc->size;
179 }
180 sc->next = NULL;
181
182 /* Release enough of the oldest blocks to bring the free queue
183 volume below vg_clo_freelist_vol. */
184
185 while (vg_freed_list_volume > VG_(clo_freelist_vol)) {
186 /* freelist_sanity(); */
187 vg_assert(vg_freed_list_start != NULL);
188 vg_assert(vg_freed_list_end != NULL);
189
190 sc1 = vg_freed_list_start;
191 vg_freed_list_volume -= sc1->size;
192 /* VG_(printf)("volume now %d\n", vg_freed_list_volume); */
193 vg_assert(vg_freed_list_volume >= 0);
194
195 if (vg_freed_list_start == vg_freed_list_end) {
196 vg_freed_list_start = vg_freed_list_end = NULL;
197 } else {
198 vg_freed_list_start = sc1->next;
199 }
200 sc1->next = NULL; /* just paranoia */
201 VG_(free)(VG_AR_CLIENT, (void*)(sc1->data));
202 VG_(free)(VG_AR_PRIVATE, sc1);
203 }
204}
205
206
207/* Allocate a user-chunk of size bytes. Also allocate its shadow
208 block, make the shadow block point at the user block. Put the
209 shadow chunk on the appropriate list, and set all memory
210 protections correctly. */
211
sewardj8c824512002-04-14 04:16:48 +0000212static ShadowChunk* client_malloc_shadow ( ThreadState* tst,
213 UInt align, UInt size,
sewardjde4a1d02002-03-22 01:27:54 +0000214 VgAllocKind kind )
215{
216 ShadowChunk* sc;
217 Addr p;
218 UInt ml_no;
219
220# ifdef DEBUG_CLIENTMALLOC
221 VG_(printf)("[m %d, f %d (%d)] client_malloc_shadow ( al %d, sz %d )\n",
222 count_malloclists(),
223 count_freelist(), vg_freed_list_volume,
224 align, size );
225# endif
226
227 if (align == 0)
228 p = (Addr)VG_(malloc)(VG_AR_CLIENT, size);
229 else
230 p = (Addr)VG_(malloc_aligned)(VG_AR_CLIENT, align, size);
231
232 sc = VG_(malloc)(VG_AR_PRIVATE, sizeof(ShadowChunk));
sewardjeb379472002-04-16 01:10:18 +0000233 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000234 sc->size = size;
235 sc->allockind = kind;
236 sc->data = p;
237 ml_no = VG_MALLOCLIST_NO(p);
238 sc->next = vg_malloclist[ml_no];
239 vg_malloclist[ml_no] = sc;
240
241 VGM_(make_writable)(p, size);
242 VGM_(make_noaccess)(p + size,
243 VG_AR_CLIENT_REDZONE_SZB);
244 VGM_(make_noaccess)(p - VG_AR_CLIENT_REDZONE_SZB,
245 VG_AR_CLIENT_REDZONE_SZB);
246
247 return sc;
248}
249
250
251/* Allocate memory, noticing whether or not we are doing the full
252 instrumentation thing. */
253
sewardj8c824512002-04-14 04:16:48 +0000254void* VG_(client_malloc) ( ThreadState* tst, UInt size, VgAllocKind kind )
sewardjde4a1d02002-03-22 01:27:54 +0000255{
256 ShadowChunk* sc;
sewardjde4a1d02002-03-22 01:27:54 +0000257
258 VGP_PUSHCC(VgpCliMalloc);
259 client_malloc_init();
260# ifdef DEBUG_CLIENTMALLOC
261 VG_(printf)("[m %d, f %d (%d)] client_malloc ( %d, %x )\n",
262 count_malloclists(),
263 count_freelist(), vg_freed_list_volume,
264 size, raw_alloc_kind );
265# endif
sewardj2e93c502002-04-12 11:12:52 +0000266
267 vg_cmalloc_n_mallocs ++;
268 vg_cmalloc_bs_mallocd += size;
269
sewardjde4a1d02002-03-22 01:27:54 +0000270 if (!VG_(clo_instrument)) {
271 VGP_POPCC;
272 return VG_(malloc) ( VG_AR_CLIENT, size );
273 }
sewardj2e93c502002-04-12 11:12:52 +0000274
sewardj8c824512002-04-14 04:16:48 +0000275 sc = client_malloc_shadow ( tst, 0, size, kind );
sewardjde4a1d02002-03-22 01:27:54 +0000276 VGP_POPCC;
277 return (void*)(sc->data);
278}
279
280
sewardj8c824512002-04-14 04:16:48 +0000281void* VG_(client_memalign) ( ThreadState* tst, UInt align, UInt size )
sewardjde4a1d02002-03-22 01:27:54 +0000282{
283 ShadowChunk* sc;
284 VGP_PUSHCC(VgpCliMalloc);
285 client_malloc_init();
286# ifdef DEBUG_CLIENTMALLOC
287 VG_(printf)("[m %d, f %d (%d)] client_memalign ( al %d, sz %d )\n",
288 count_malloclists(),
289 count_freelist(), vg_freed_list_volume,
290 align, size );
291# endif
sewardj2e93c502002-04-12 11:12:52 +0000292
293 vg_cmalloc_n_mallocs ++;
294 vg_cmalloc_bs_mallocd += size;
295
sewardjde4a1d02002-03-22 01:27:54 +0000296 if (!VG_(clo_instrument)) {
297 VGP_POPCC;
298 return VG_(malloc_aligned) ( VG_AR_CLIENT, align, size );
299 }
sewardj8c824512002-04-14 04:16:48 +0000300 sc = client_malloc_shadow ( tst, align, size, Vg_AllocMalloc );
sewardjde4a1d02002-03-22 01:27:54 +0000301 VGP_POPCC;
302 return (void*)(sc->data);
303}
304
305
sewardj8c824512002-04-14 04:16:48 +0000306void VG_(client_free) ( ThreadState* tst, void* ptrV, VgAllocKind kind )
sewardjde4a1d02002-03-22 01:27:54 +0000307{
308 ShadowChunk* sc;
309 UInt ml_no;
sewardjde4a1d02002-03-22 01:27:54 +0000310
311 VGP_PUSHCC(VgpCliMalloc);
312 client_malloc_init();
313# ifdef DEBUG_CLIENTMALLOC
314 VG_(printf)("[m %d, f %d (%d)] client_free ( %p, %x )\n",
315 count_malloclists(),
316 count_freelist(), vg_freed_list_volume,
317 ptrV, raw_alloc_kind );
318# endif
sewardj2e93c502002-04-12 11:12:52 +0000319
320 vg_cmalloc_n_frees ++;
321
sewardjde4a1d02002-03-22 01:27:54 +0000322 if (!VG_(clo_instrument)) {
323 VGP_POPCC;
324 VG_(free) ( VG_AR_CLIENT, ptrV );
325 return;
326 }
327
328 /* first, see if ptrV is one vg_client_malloc gave out. */
329 ml_no = VG_MALLOCLIST_NO(ptrV);
330 vg_mlist_frees++;
331 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
332 vg_mlist_tries++;
333 if ((Addr)ptrV == sc->data)
334 break;
335 }
336
337 if (sc == NULL) {
338 VG_(record_free_error) ( (Addr)ptrV );
339 VGP_POPCC;
340 return;
341 }
342
sewardjde4a1d02002-03-22 01:27:54 +0000343 /* check if its a matching free() / delete / delete [] */
344 if (kind != sc->allockind)
345 VG_(record_freemismatch_error) ( (Addr) ptrV );
346
347 /* Remove the shadow chunk from the mallocd list. */
348 remove_from_malloclist ( ml_no, sc );
349
350 /* Declare it inaccessible. */
351 VGM_(make_noaccess) ( sc->data - VG_AR_CLIENT_REDZONE_SZB,
352 sc->size + 2*VG_AR_CLIENT_REDZONE_SZB );
353 VGM_(make_noaccess) ( (Addr)sc, sizeof(ShadowChunk) );
sewardjeb379472002-04-16 01:10:18 +0000354 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000355
356 /* Put it out of harm's way for a while. */
357 add_to_freed_queue ( sc );
358 VGP_POPCC;
359}
360
361
362
sewardj8c824512002-04-14 04:16:48 +0000363void* VG_(client_calloc) ( ThreadState* tst, UInt nmemb, UInt size1 )
sewardjde4a1d02002-03-22 01:27:54 +0000364{
365 ShadowChunk* sc;
366 Addr p;
367 UInt size, i, ml_no;
368
369 VGP_PUSHCC(VgpCliMalloc);
370 client_malloc_init();
371
372# ifdef DEBUG_CLIENTMALLOC
373 VG_(printf)("[m %d, f %d (%d)] client_calloc ( %d, %d )\n",
374 count_malloclists(),
375 count_freelist(), vg_freed_list_volume,
376 nmemb, size1 );
377# endif
378
sewardj2e93c502002-04-12 11:12:52 +0000379 vg_cmalloc_n_mallocs ++;
380 vg_cmalloc_bs_mallocd += nmemb * size1;
381
sewardjde4a1d02002-03-22 01:27:54 +0000382 if (!VG_(clo_instrument)) {
383 VGP_POPCC;
384 return VG_(calloc) ( VG_AR_CLIENT, nmemb, size1 );
385 }
386
387 size = nmemb * size1;
388 p = (Addr)VG_(malloc)(VG_AR_CLIENT, size);
389 sc = VG_(malloc)(VG_AR_PRIVATE, sizeof(ShadowChunk));
sewardjeb379472002-04-16 01:10:18 +0000390 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000391 sc->size = size;
392 sc->allockind = Vg_AllocMalloc; /* its a lie - but true. eat this :) */
393 sc->data = p;
394 ml_no = VG_MALLOCLIST_NO(p);
395 sc->next = vg_malloclist[ml_no];
396 vg_malloclist[ml_no] = sc;
397
398 VGM_(make_readable)(p, size);
399 VGM_(make_noaccess)(p + size,
400 VG_AR_CLIENT_REDZONE_SZB);
401 VGM_(make_noaccess)(p - VG_AR_CLIENT_REDZONE_SZB,
402 VG_AR_CLIENT_REDZONE_SZB);
403
404 for (i = 0; i < size; i++) ((UChar*)p)[i] = 0;
405
406 VGP_POPCC;
407 return (void*)p;
408}
409
410
sewardj8c824512002-04-14 04:16:48 +0000411void* VG_(client_realloc) ( ThreadState* tst, void* ptrV, UInt size_new )
sewardjde4a1d02002-03-22 01:27:54 +0000412{
413 ShadowChunk *sc, *sc_new;
414 UInt i, ml_no;
415
416 VGP_PUSHCC(VgpCliMalloc);
417 client_malloc_init();
418
419# ifdef DEBUG_CLIENTMALLOC
420 VG_(printf)("[m %d, f %d (%d)] client_realloc ( %p, %d )\n",
421 count_malloclists(),
422 count_freelist(), vg_freed_list_volume,
423 ptrV, size_new );
424# endif
425
sewardj2e93c502002-04-12 11:12:52 +0000426 vg_cmalloc_n_frees ++;
427 vg_cmalloc_n_mallocs ++;
428 vg_cmalloc_bs_mallocd += size_new;
429
sewardjde4a1d02002-03-22 01:27:54 +0000430 if (!VG_(clo_instrument)) {
431 vg_assert(ptrV != NULL && size_new != 0);
432 VGP_POPCC;
433 return VG_(realloc) ( VG_AR_CLIENT, ptrV, size_new );
434 }
435
436 /* First try and find the block. */
437 ml_no = VG_MALLOCLIST_NO(ptrV);
438 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
439 if ((Addr)ptrV == sc->data)
440 break;
441 }
442
443 if (sc == NULL) {
444 VG_(record_free_error) ( (Addr)ptrV );
445 /* Perhaps we should keep going regardless. */
446 VGP_POPCC;
447 return NULL;
448 }
449
450 if (sc->allockind != Vg_AllocMalloc) {
451 /* can not realloc a range that was allocated with new or new [] */
452 VG_(record_freemismatch_error) ( (Addr)ptrV );
453 /* but keep going anyway */
454 }
455
456 if (sc->size == size_new) {
457 /* size unchanged */
458 VGP_POPCC;
459 return ptrV;
460 }
461 if (sc->size > size_new) {
462 /* new size is smaller */
463 VGM_(make_noaccess)( sc->data + size_new,
464 sc->size - size_new );
465 sc->size = size_new;
466 VGP_POPCC;
467 return ptrV;
468 } else {
469 /* new size is bigger */
sewardj8c824512002-04-14 04:16:48 +0000470 sc_new = client_malloc_shadow ( tst, 0, size_new, Vg_AllocMalloc );
sewardjde4a1d02002-03-22 01:27:54 +0000471 for (i = 0; i < sc->size; i++)
472 ((UChar*)(sc_new->data))[i] = ((UChar*)(sc->data))[i];
473 VGM_(copy_address_range_perms) (
474 sc->data, sc_new->data, sc->size );
475 remove_from_malloclist ( VG_MALLOCLIST_NO(sc->data), sc );
476 VGM_(make_noaccess) ( sc->data - VG_AR_CLIENT_REDZONE_SZB,
477 sc->size + 2*VG_AR_CLIENT_REDZONE_SZB );
478 VGM_(make_noaccess) ( (Addr)sc, sizeof(ShadowChunk) );
479 add_to_freed_queue ( sc );
480 VGP_POPCC;
481 return (void*)sc_new->data;
482 }
483}
484
485
486void VG_(clientmalloc_done) ( void )
487{
488 UInt nblocks, nbytes, ml_no;
489 ShadowChunk* sc;
490
491 client_malloc_init();
492
493 nblocks = nbytes = 0;
494
495 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++) {
496 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
497 nblocks ++;
498 nbytes += sc->size;
499 }
500 }
501
502 if (VG_(clo_verbosity) == 0)
503 return;
504
505 VG_(message)(Vg_UserMsg,
506 "malloc/free: in use at exit: %d bytes in %d blocks.",
507 nbytes, nblocks);
508 VG_(message)(Vg_UserMsg,
509 "malloc/free: %d allocs, %d frees, %d bytes allocated.",
510 vg_cmalloc_n_mallocs,
511 vg_cmalloc_n_frees, vg_cmalloc_bs_mallocd);
512 if (!VG_(clo_leak_check))
513 VG_(message)(Vg_UserMsg,
514 "For a detailed leak analysis, rerun with: --leak-check=yes");
515 if (0)
516 VG_(message)(Vg_DebugMsg,
517 "free search: %d tries, %d frees",
518 vg_mlist_tries,
519 vg_mlist_frees );
520 if (VG_(clo_verbosity) > 1)
521 VG_(message)(Vg_UserMsg, "");
522}
523
524
525/* Describe an address as best you can, for error messages,
526 putting the result in ai. */
527
528void VG_(describe_addr) ( Addr a, AddrInfo* ai )
529{
530 ShadowChunk* sc;
531 UInt ml_no;
532 Bool ok;
533
534 /* Perhaps it's a user-def'd block ? */
535 ok = VG_(client_perm_maybe_describe)( a, ai );
536 if (ok)
537 return;
538 /* Perhaps it's on the stack? */
539 if (VG_(is_plausible_stack_addr)(a)
540 && a >= (Addr)VG_(baseBlock)[VGOFF_(m_esp)]) {
541 ai->akind = Stack;
542 return;
543 }
544 /* Search for a freed block which might bracket it. */
545 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next) {
546 if (sc->data - VG_AR_CLIENT_REDZONE_SZB <= a
547 && a < sc->data + sc->size + VG_AR_CLIENT_REDZONE_SZB) {
548 ai->akind = Freed;
549 ai->blksize = sc->size;
550 ai->rwoffset = (Int)(a) - (Int)(sc->data);
551 ai->lastchange = sc->where;
552 return;
553 }
554 }
555 /* Search for a mallocd block which might bracket it. */
556 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++) {
557 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
558 if (sc->data - VG_AR_CLIENT_REDZONE_SZB <= a
559 && a < sc->data + sc->size + VG_AR_CLIENT_REDZONE_SZB) {
560 ai->akind = Mallocd;
561 ai->blksize = sc->size;
562 ai->rwoffset = (Int)(a) - (Int)(sc->data);
563 ai->lastchange = sc->where;
564 return;
565 }
566 }
567 }
568 /* Clueless ... */
569 ai->akind = Unknown;
570 return;
571}
572
sewardjde4a1d02002-03-22 01:27:54 +0000573
574/*--------------------------------------------------------------------*/
575/*--- end vg_clientmalloc.c ---*/
576/*--------------------------------------------------------------------*/