blob: 607c633b989d00e97862962f99a4dbf3392007b7 [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
sewardjde4a1d02002-03-22 01:27:54 +000013
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 LICENSE.
30*/
31
32#include "vg_include.h"
33
34
35/*------------------------------------------------------------*/
36/*--- Defns ---*/
37/*------------------------------------------------------------*/
38
39/* #define DEBUG_CLIENTMALLOC */
40
41/* Holds malloc'd but not freed blocks. */
42#define VG_MALLOCLIST_NO(aa) (((UInt)(aa)) % VG_N_MALLOCLISTS)
43static ShadowChunk* vg_malloclist[VG_N_MALLOCLISTS];
44static Bool vg_client_malloc_init_done = False;
45
46/* Holds blocks after freeing. */
47static ShadowChunk* vg_freed_list_start = NULL;
48static ShadowChunk* vg_freed_list_end = NULL;
49static Int vg_freed_list_volume = 0;
50
51/* Stats ... */
52static UInt vg_cmalloc_n_mallocs = 0;
53static UInt vg_cmalloc_n_frees = 0;
54static UInt vg_cmalloc_bs_mallocd = 0;
55
56static UInt vg_mlist_frees = 0;
57static UInt vg_mlist_tries = 0;
58
59
60/*------------------------------------------------------------*/
61/*--- Fns ---*/
62/*------------------------------------------------------------*/
63
64/* Allocate a suitably-sized array, copy all the malloc-d block
65 shadows into it, and return both the array and the size of it.
66 This is used by the memory-leak detector.
67*/
68ShadowChunk** VG_(get_malloc_shadows) ( /*OUT*/ UInt* n_shadows )
69{
70 UInt i, scn;
71 ShadowChunk** arr;
72 ShadowChunk* sc;
73 *n_shadows = 0;
74 for (scn = 0; scn < VG_N_MALLOCLISTS; scn++) {
75 for (sc = vg_malloclist[scn]; sc != NULL; sc = sc->next) {
76 (*n_shadows)++;
77 }
78 }
79 if (*n_shadows == 0) return NULL;
80
81 arr = VG_(malloc)( VG_AR_PRIVATE,
82 *n_shadows * sizeof(ShadowChunk*) );
83
84 i = 0;
85 for (scn = 0; scn < VG_N_MALLOCLISTS; scn++) {
86 for (sc = vg_malloclist[scn]; sc != NULL; sc = sc->next) {
87 arr[i++] = sc;
88 }
89 }
90 vg_assert(i == *n_shadows);
91 return arr;
92}
93
94static void client_malloc_init ( void )
95{
96 UInt ml_no;
97 if (vg_client_malloc_init_done) return;
98 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++)
99 vg_malloclist[ml_no] = NULL;
100 vg_client_malloc_init_done = True;
101}
102
103
104static __attribute__ ((unused))
105 Int count_freelist ( void )
106{
107 ShadowChunk* sc;
108 Int n = 0;
109 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
110 n++;
111 return n;
112}
113
114static __attribute__ ((unused))
115 Int count_malloclists ( void )
116{
117 ShadowChunk* sc;
118 UInt ml_no;
119 Int n = 0;
120 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++)
121 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next)
122 n++;
123 return n;
124}
125
126static __attribute__ ((unused))
127 void freelist_sanity ( void )
128{
129 ShadowChunk* sc;
130 Int n = 0;
131 /* VG_(printf)("freelist sanity\n"); */
132 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
133 n += sc->size;
134 vg_assert(n == vg_freed_list_volume);
135}
136
137/* Remove sc from malloc list # sc. It is an unchecked error for
138 sc not to be present in the list.
139*/
140static void remove_from_malloclist ( UInt ml_no, ShadowChunk* sc )
141{
142 ShadowChunk *sc1, *sc2;
143 if (sc == vg_malloclist[ml_no]) {
144 vg_malloclist[ml_no] = vg_malloclist[ml_no]->next;
145 } else {
146 sc1 = vg_malloclist[ml_no];
147 vg_assert(sc1 != NULL);
148 sc2 = sc1->next;
149 while (sc2 != sc) {
150 vg_assert(sc2 != NULL);
151 sc1 = sc2;
152 sc2 = sc2->next;
153 }
154 vg_assert(sc1->next == sc);
155 vg_assert(sc2 == sc);
156 sc1->next = sc2->next;
157 }
158}
159
160
161/* Put a shadow chunk on the freed blocks queue, possibly freeing up
162 some of the oldest blocks in the queue at the same time. */
163
164static void add_to_freed_queue ( ShadowChunk* sc )
165{
166 ShadowChunk* sc1;
167
168 /* Put it at the end of the freed list */
169 if (vg_freed_list_end == NULL) {
170 vg_assert(vg_freed_list_start == NULL);
171 vg_freed_list_end = vg_freed_list_start = sc;
172 vg_freed_list_volume = sc->size;
173 } else {
174 vg_assert(vg_freed_list_end->next == NULL);
175 vg_freed_list_end->next = sc;
176 vg_freed_list_end = sc;
177 vg_freed_list_volume += sc->size;
178 }
179 sc->next = NULL;
180
181 /* Release enough of the oldest blocks to bring the free queue
182 volume below vg_clo_freelist_vol. */
183
184 while (vg_freed_list_volume > VG_(clo_freelist_vol)) {
185 /* freelist_sanity(); */
186 vg_assert(vg_freed_list_start != NULL);
187 vg_assert(vg_freed_list_end != NULL);
188
189 sc1 = vg_freed_list_start;
190 vg_freed_list_volume -= sc1->size;
191 /* VG_(printf)("volume now %d\n", vg_freed_list_volume); */
192 vg_assert(vg_freed_list_volume >= 0);
193
194 if (vg_freed_list_start == vg_freed_list_end) {
195 vg_freed_list_start = vg_freed_list_end = NULL;
196 } else {
197 vg_freed_list_start = sc1->next;
198 }
199 sc1->next = NULL; /* just paranoia */
200 VG_(free)(VG_AR_CLIENT, (void*)(sc1->data));
201 VG_(free)(VG_AR_PRIVATE, sc1);
202 }
203}
204
205
206/* Allocate a user-chunk of size bytes. Also allocate its shadow
207 block, make the shadow block point at the user block. Put the
208 shadow chunk on the appropriate list, and set all memory
209 protections correctly. */
210
sewardj8c824512002-04-14 04:16:48 +0000211static ShadowChunk* client_malloc_shadow ( ThreadState* tst,
212 UInt align, UInt size,
sewardjde4a1d02002-03-22 01:27:54 +0000213 VgAllocKind kind )
214{
215 ShadowChunk* sc;
216 Addr p;
217 UInt ml_no;
218
219# ifdef DEBUG_CLIENTMALLOC
220 VG_(printf)("[m %d, f %d (%d)] client_malloc_shadow ( al %d, sz %d )\n",
221 count_malloclists(),
222 count_freelist(), vg_freed_list_volume,
223 align, size );
224# endif
225
226 if (align == 0)
227 p = (Addr)VG_(malloc)(VG_AR_CLIENT, size);
228 else
229 p = (Addr)VG_(malloc_aligned)(VG_AR_CLIENT, align, size);
230
231 sc = VG_(malloc)(VG_AR_PRIVATE, sizeof(ShadowChunk));
sewardjeb379472002-04-16 01:10:18 +0000232 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000233 sc->size = size;
234 sc->allockind = kind;
235 sc->data = p;
236 ml_no = VG_MALLOCLIST_NO(p);
237 sc->next = vg_malloclist[ml_no];
238 vg_malloclist[ml_no] = sc;
239
240 VGM_(make_writable)(p, size);
241 VGM_(make_noaccess)(p + size,
242 VG_AR_CLIENT_REDZONE_SZB);
243 VGM_(make_noaccess)(p - VG_AR_CLIENT_REDZONE_SZB,
244 VG_AR_CLIENT_REDZONE_SZB);
245
246 return sc;
247}
248
249
250/* Allocate memory, noticing whether or not we are doing the full
251 instrumentation thing. */
252
sewardj8c824512002-04-14 04:16:48 +0000253void* VG_(client_malloc) ( ThreadState* tst, UInt size, VgAllocKind kind )
sewardjde4a1d02002-03-22 01:27:54 +0000254{
255 ShadowChunk* sc;
sewardjde4a1d02002-03-22 01:27:54 +0000256
257 VGP_PUSHCC(VgpCliMalloc);
258 client_malloc_init();
259# ifdef DEBUG_CLIENTMALLOC
260 VG_(printf)("[m %d, f %d (%d)] client_malloc ( %d, %x )\n",
261 count_malloclists(),
262 count_freelist(), vg_freed_list_volume,
263 size, raw_alloc_kind );
264# endif
sewardj2e93c502002-04-12 11:12:52 +0000265
266 vg_cmalloc_n_mallocs ++;
267 vg_cmalloc_bs_mallocd += size;
268
sewardjde4a1d02002-03-22 01:27:54 +0000269 if (!VG_(clo_instrument)) {
270 VGP_POPCC;
271 return VG_(malloc) ( VG_AR_CLIENT, size );
272 }
sewardj2e93c502002-04-12 11:12:52 +0000273
sewardj8c824512002-04-14 04:16:48 +0000274 sc = client_malloc_shadow ( tst, 0, size, kind );
sewardjde4a1d02002-03-22 01:27:54 +0000275 VGP_POPCC;
276 return (void*)(sc->data);
277}
278
279
sewardj8c824512002-04-14 04:16:48 +0000280void* VG_(client_memalign) ( ThreadState* tst, UInt align, UInt size )
sewardjde4a1d02002-03-22 01:27:54 +0000281{
282 ShadowChunk* sc;
283 VGP_PUSHCC(VgpCliMalloc);
284 client_malloc_init();
285# ifdef DEBUG_CLIENTMALLOC
286 VG_(printf)("[m %d, f %d (%d)] client_memalign ( al %d, sz %d )\n",
287 count_malloclists(),
288 count_freelist(), vg_freed_list_volume,
289 align, size );
290# endif
sewardj2e93c502002-04-12 11:12:52 +0000291
292 vg_cmalloc_n_mallocs ++;
293 vg_cmalloc_bs_mallocd += size;
294
sewardjde4a1d02002-03-22 01:27:54 +0000295 if (!VG_(clo_instrument)) {
296 VGP_POPCC;
297 return VG_(malloc_aligned) ( VG_AR_CLIENT, align, size );
298 }
sewardj8c824512002-04-14 04:16:48 +0000299 sc = client_malloc_shadow ( tst, align, size, Vg_AllocMalloc );
sewardjde4a1d02002-03-22 01:27:54 +0000300 VGP_POPCC;
301 return (void*)(sc->data);
302}
303
304
sewardj8c824512002-04-14 04:16:48 +0000305void VG_(client_free) ( ThreadState* tst, void* ptrV, VgAllocKind kind )
sewardjde4a1d02002-03-22 01:27:54 +0000306{
307 ShadowChunk* sc;
308 UInt ml_no;
sewardjde4a1d02002-03-22 01:27:54 +0000309
310 VGP_PUSHCC(VgpCliMalloc);
311 client_malloc_init();
312# ifdef DEBUG_CLIENTMALLOC
313 VG_(printf)("[m %d, f %d (%d)] client_free ( %p, %x )\n",
314 count_malloclists(),
315 count_freelist(), vg_freed_list_volume,
316 ptrV, raw_alloc_kind );
317# endif
sewardj2e93c502002-04-12 11:12:52 +0000318
319 vg_cmalloc_n_frees ++;
320
sewardjde4a1d02002-03-22 01:27:54 +0000321 if (!VG_(clo_instrument)) {
322 VGP_POPCC;
323 VG_(free) ( VG_AR_CLIENT, ptrV );
324 return;
325 }
326
327 /* first, see if ptrV is one vg_client_malloc gave out. */
328 ml_no = VG_MALLOCLIST_NO(ptrV);
329 vg_mlist_frees++;
330 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
331 vg_mlist_tries++;
332 if ((Addr)ptrV == sc->data)
333 break;
334 }
335
336 if (sc == NULL) {
sewardjaabd5ad2002-04-19 15:43:37 +0000337 VG_(record_free_error) ( tst, (Addr)ptrV );
sewardjde4a1d02002-03-22 01:27:54 +0000338 VGP_POPCC;
339 return;
340 }
341
sewardjde4a1d02002-03-22 01:27:54 +0000342 /* check if its a matching free() / delete / delete [] */
343 if (kind != sc->allockind)
sewardjaabd5ad2002-04-19 15:43:37 +0000344 VG_(record_freemismatch_error) ( tst, (Addr) ptrV );
sewardjde4a1d02002-03-22 01:27:54 +0000345
346 /* Remove the shadow chunk from the mallocd list. */
347 remove_from_malloclist ( ml_no, sc );
348
349 /* Declare it inaccessible. */
350 VGM_(make_noaccess) ( sc->data - VG_AR_CLIENT_REDZONE_SZB,
351 sc->size + 2*VG_AR_CLIENT_REDZONE_SZB );
352 VGM_(make_noaccess) ( (Addr)sc, sizeof(ShadowChunk) );
sewardjeb379472002-04-16 01:10:18 +0000353 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000354
355 /* Put it out of harm's way for a while. */
356 add_to_freed_queue ( sc );
357 VGP_POPCC;
358}
359
360
361
sewardj8c824512002-04-14 04:16:48 +0000362void* VG_(client_calloc) ( ThreadState* tst, UInt nmemb, UInt size1 )
sewardjde4a1d02002-03-22 01:27:54 +0000363{
364 ShadowChunk* sc;
365 Addr p;
366 UInt size, i, ml_no;
367
368 VGP_PUSHCC(VgpCliMalloc);
369 client_malloc_init();
370
371# ifdef DEBUG_CLIENTMALLOC
372 VG_(printf)("[m %d, f %d (%d)] client_calloc ( %d, %d )\n",
373 count_malloclists(),
374 count_freelist(), vg_freed_list_volume,
375 nmemb, size1 );
376# endif
377
sewardj2e93c502002-04-12 11:12:52 +0000378 vg_cmalloc_n_mallocs ++;
379 vg_cmalloc_bs_mallocd += nmemb * size1;
380
sewardjde4a1d02002-03-22 01:27:54 +0000381 if (!VG_(clo_instrument)) {
382 VGP_POPCC;
383 return VG_(calloc) ( VG_AR_CLIENT, nmemb, size1 );
384 }
385
386 size = nmemb * size1;
387 p = (Addr)VG_(malloc)(VG_AR_CLIENT, size);
388 sc = VG_(malloc)(VG_AR_PRIVATE, sizeof(ShadowChunk));
sewardjeb379472002-04-16 01:10:18 +0000389 sc->where = VG_(get_ExeContext)(False, tst->m_eip, tst->m_ebp);
sewardjde4a1d02002-03-22 01:27:54 +0000390 sc->size = size;
391 sc->allockind = Vg_AllocMalloc; /* its a lie - but true. eat this :) */
392 sc->data = p;
393 ml_no = VG_MALLOCLIST_NO(p);
394 sc->next = vg_malloclist[ml_no];
395 vg_malloclist[ml_no] = sc;
396
397 VGM_(make_readable)(p, size);
398 VGM_(make_noaccess)(p + size,
399 VG_AR_CLIENT_REDZONE_SZB);
400 VGM_(make_noaccess)(p - VG_AR_CLIENT_REDZONE_SZB,
401 VG_AR_CLIENT_REDZONE_SZB);
402
403 for (i = 0; i < size; i++) ((UChar*)p)[i] = 0;
404
405 VGP_POPCC;
406 return (void*)p;
407}
408
409
sewardj8c824512002-04-14 04:16:48 +0000410void* VG_(client_realloc) ( ThreadState* tst, void* ptrV, UInt size_new )
sewardjde4a1d02002-03-22 01:27:54 +0000411{
412 ShadowChunk *sc, *sc_new;
413 UInt i, ml_no;
414
415 VGP_PUSHCC(VgpCliMalloc);
416 client_malloc_init();
417
418# ifdef DEBUG_CLIENTMALLOC
419 VG_(printf)("[m %d, f %d (%d)] client_realloc ( %p, %d )\n",
420 count_malloclists(),
421 count_freelist(), vg_freed_list_volume,
422 ptrV, size_new );
423# endif
424
sewardj2e93c502002-04-12 11:12:52 +0000425 vg_cmalloc_n_frees ++;
426 vg_cmalloc_n_mallocs ++;
427 vg_cmalloc_bs_mallocd += size_new;
428
sewardjde4a1d02002-03-22 01:27:54 +0000429 if (!VG_(clo_instrument)) {
430 vg_assert(ptrV != NULL && size_new != 0);
431 VGP_POPCC;
432 return VG_(realloc) ( VG_AR_CLIENT, ptrV, size_new );
433 }
434
435 /* First try and find the block. */
436 ml_no = VG_MALLOCLIST_NO(ptrV);
437 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
438 if ((Addr)ptrV == sc->data)
439 break;
440 }
441
442 if (sc == NULL) {
sewardjaabd5ad2002-04-19 15:43:37 +0000443 VG_(record_free_error) ( tst, (Addr)ptrV );
sewardjde4a1d02002-03-22 01:27:54 +0000444 /* Perhaps we should keep going regardless. */
445 VGP_POPCC;
446 return NULL;
447 }
448
449 if (sc->allockind != Vg_AllocMalloc) {
450 /* can not realloc a range that was allocated with new or new [] */
sewardjaabd5ad2002-04-19 15:43:37 +0000451 VG_(record_freemismatch_error) ( tst, (Addr)ptrV );
sewardjde4a1d02002-03-22 01:27:54 +0000452 /* but keep going anyway */
453 }
454
455 if (sc->size == size_new) {
456 /* size unchanged */
457 VGP_POPCC;
458 return ptrV;
459 }
460 if (sc->size > size_new) {
461 /* new size is smaller */
462 VGM_(make_noaccess)( sc->data + size_new,
463 sc->size - size_new );
464 sc->size = size_new;
465 VGP_POPCC;
466 return ptrV;
467 } else {
468 /* new size is bigger */
sewardj8c824512002-04-14 04:16:48 +0000469 sc_new = client_malloc_shadow ( tst, 0, size_new, Vg_AllocMalloc );
sewardjde4a1d02002-03-22 01:27:54 +0000470 for (i = 0; i < sc->size; i++)
471 ((UChar*)(sc_new->data))[i] = ((UChar*)(sc->data))[i];
472 VGM_(copy_address_range_perms) (
473 sc->data, sc_new->data, sc->size );
474 remove_from_malloclist ( VG_MALLOCLIST_NO(sc->data), sc );
475 VGM_(make_noaccess) ( sc->data - VG_AR_CLIENT_REDZONE_SZB,
476 sc->size + 2*VG_AR_CLIENT_REDZONE_SZB );
477 VGM_(make_noaccess) ( (Addr)sc, sizeof(ShadowChunk) );
478 add_to_freed_queue ( sc );
479 VGP_POPCC;
480 return (void*)sc_new->data;
481 }
482}
483
484
485void VG_(clientmalloc_done) ( void )
486{
487 UInt nblocks, nbytes, ml_no;
488 ShadowChunk* sc;
489
490 client_malloc_init();
491
492 nblocks = nbytes = 0;
493
494 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++) {
495 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
496 nblocks ++;
497 nbytes += sc->size;
498 }
499 }
500
501 if (VG_(clo_verbosity) == 0)
502 return;
503
504 VG_(message)(Vg_UserMsg,
505 "malloc/free: in use at exit: %d bytes in %d blocks.",
506 nbytes, nblocks);
507 VG_(message)(Vg_UserMsg,
508 "malloc/free: %d allocs, %d frees, %d bytes allocated.",
509 vg_cmalloc_n_mallocs,
510 vg_cmalloc_n_frees, vg_cmalloc_bs_mallocd);
511 if (!VG_(clo_leak_check))
512 VG_(message)(Vg_UserMsg,
513 "For a detailed leak analysis, rerun with: --leak-check=yes");
514 if (0)
515 VG_(message)(Vg_DebugMsg,
516 "free search: %d tries, %d frees",
517 vg_mlist_tries,
518 vg_mlist_frees );
519 if (VG_(clo_verbosity) > 1)
520 VG_(message)(Vg_UserMsg, "");
521}
522
523
524/* Describe an address as best you can, for error messages,
525 putting the result in ai. */
526
527void VG_(describe_addr) ( Addr a, AddrInfo* ai )
528{
529 ShadowChunk* sc;
530 UInt ml_no;
531 Bool ok;
sewardj1e8cdc92002-04-18 11:37:52 +0000532 ThreadId tid;
sewardjde4a1d02002-03-22 01:27:54 +0000533
534 /* Perhaps it's a user-def'd block ? */
535 ok = VG_(client_perm_maybe_describe)( a, ai );
536 if (ok)
537 return;
sewardj1e8cdc92002-04-18 11:37:52 +0000538 /* Perhaps it's on a thread's stack? */
539 tid = VG_(identify_stack_addr)(a);
540 if (tid != VG_INVALID_THREADID) {
541 ai->akind = Stack;
542 ai->stack_tid = tid;
sewardjde4a1d02002-03-22 01:27:54 +0000543 return;
544 }
545 /* Search for a freed block which might bracket it. */
546 for (sc = vg_freed_list_start; sc != NULL; sc = sc->next) {
547 if (sc->data - VG_AR_CLIENT_REDZONE_SZB <= a
548 && a < sc->data + sc->size + VG_AR_CLIENT_REDZONE_SZB) {
549 ai->akind = Freed;
550 ai->blksize = sc->size;
551 ai->rwoffset = (Int)(a) - (Int)(sc->data);
552 ai->lastchange = sc->where;
553 return;
554 }
555 }
556 /* Search for a mallocd block which might bracket it. */
557 for (ml_no = 0; ml_no < VG_N_MALLOCLISTS; ml_no++) {
558 for (sc = vg_malloclist[ml_no]; sc != NULL; sc = sc->next) {
559 if (sc->data - VG_AR_CLIENT_REDZONE_SZB <= a
560 && a < sc->data + sc->size + VG_AR_CLIENT_REDZONE_SZB) {
561 ai->akind = Mallocd;
562 ai->blksize = sc->size;
563 ai->rwoffset = (Int)(a) - (Int)(sc->data);
564 ai->lastchange = sc->where;
565 return;
566 }
567 }
568 }
569 /* Clueless ... */
570 ai->akind = Unknown;
571 return;
572}
573
sewardjde4a1d02002-03-22 01:27:54 +0000574
575/*--------------------------------------------------------------------*/
576/*--- end vg_clientmalloc.c ---*/
577/*--------------------------------------------------------------------*/