blob: ad1d2b4203e7d64bac00bd61a03e21bd0b4d1183 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28#include <errno.h>
29#include <pthread.h>
30#include <stdio.h>
31#include <arpa/inet.h>
32#include <sys/socket.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <errno.h>
37#include <stddef.h>
38#include <stdarg.h>
39#include <fcntl.h>
40#include <unwind.h>
41
42#include <sys/socket.h>
43#include <sys/un.h>
44#include <sys/select.h>
45#include <sys/types.h>
46#include <sys/system_properties.h>
47
48#include "dlmalloc.h"
49#include "logd.h"
50
51// =============================================================================
52// Utilities directly used by Dalvik
53// =============================================================================
54
55#define HASHTABLE_SIZE 1543
56#define BACKTRACE_SIZE 32
57/* flag definitions, currently sharing storage with "size" */
58#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
59#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
60
61#define MAX_SIZE_T (~(size_t)0)
62
63/*
64 * In a VM process, this is set to 1 after fork()ing out of zygote.
65 */
66int gMallocLeakZygoteChild = 0;
67
68// =============================================================================
69// Structures
70// =============================================================================
71
72typedef struct HashEntry HashEntry;
73struct HashEntry {
74 size_t slot;
75 HashEntry* prev;
76 HashEntry* next;
77 size_t numEntries;
78 // fields above "size" are NOT sent to the host
79 size_t size;
80 size_t allocations;
81 intptr_t backtrace[0];
82};
83
84typedef struct HashTable HashTable;
85struct HashTable {
86 size_t count;
87 HashEntry* slots[HASHTABLE_SIZE];
88};
89
90static pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER;
91static HashTable gHashTable;
92
93// =============================================================================
Andy McFadden39f37452009-07-21 15:25:23 -070094// log functions
95// =============================================================================
96
97#define debug_log(format, ...) \
98 __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak", (format), ##__VA_ARGS__ )
99
100// =============================================================================
101// output functions
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800102// =============================================================================
103
104static int hash_entry_compare(const void* arg1, const void* arg2)
105{
106 HashEntry* e1 = *(HashEntry**)arg1;
107 HashEntry* e2 = *(HashEntry**)arg2;
108
109 size_t nbAlloc1 = e1->allocations;
110 size_t nbAlloc2 = e2->allocations;
111 size_t size1 = e1->size & ~SIZE_FLAG_MASK;
112 size_t size2 = e2->size & ~SIZE_FLAG_MASK;
113 size_t alloc1 = nbAlloc1 * size1;
114 size_t alloc2 = nbAlloc2 * size2;
115
116 // sort in descending order by:
117 // 1) total size
118 // 2) number of allocations
119 //
120 // This is used for sorting, not determination of equality, so we don't
121 // need to compare the bit flags.
122 int result;
123 if (alloc1 > alloc2) {
124 result = -1;
125 } else if (alloc1 < alloc2) {
126 result = 1;
127 } else {
128 if (nbAlloc1 > nbAlloc2) {
129 result = -1;
130 } else if (nbAlloc1 < nbAlloc2) {
131 result = 1;
132 } else {
133 result = 0;
134 }
135 }
136 return result;
137}
138
139/*
140 * Retrieve native heap information.
141 *
142 * "*info" is set to a buffer we allocate
143 * "*overallSize" is set to the size of the "info" buffer
144 * "*infoSize" is set to the size of a single entry
145 * "*totalMemory" is set to the sum of all allocations we're tracking; does
146 * not include heap overhead
147 * "*backtraceSize" is set to the maximum number of entries in the back trace
148 */
149void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
150 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize)
151{
152 // don't do anything if we have invalid arguments
153 if (info == NULL || overallSize == NULL || infoSize == NULL ||
154 totalMemory == NULL || backtraceSize == NULL) {
155 return;
156 }
157
158 pthread_mutex_lock(&gAllocationsMutex);
159
160 if (gHashTable.count == 0) {
161 *info = NULL;
162 *overallSize = 0;
163 *infoSize = 0;
164 *totalMemory = 0;
165 *backtraceSize = 0;
166 goto done;
167 }
168
169 void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count);
170
171 // debug_log("*****\ngHashTable.count = %d\n", gHashTable.count);
172 // debug_log("list = %p\n", list);
173
174 // get the entries into an array to be sorted
175 int index = 0;
176 int i;
177 for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
178 HashEntry* entry = gHashTable.slots[i];
179 while (entry != NULL) {
180 list[index] = entry;
181 *totalMemory = *totalMemory +
182 ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
183 index++;
184 entry = entry->next;
185 }
186 }
187
188 // debug_log("sorted list!\n");
189 // XXX: the protocol doesn't allow variable size for the stack trace (yet)
190 *infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE);
191 *overallSize = *infoSize * gHashTable.count;
192 *backtraceSize = BACKTRACE_SIZE;
193
194 // debug_log("infoSize = 0x%x overall = 0x%x\n", *infoSize, *overallSize);
195 // now get A byte array big enough for this
196 *info = (uint8_t*)dlmalloc(*overallSize);
197
198 // debug_log("info = %p\n", info);
199 if (*info == NULL) {
200 *overallSize = 0;
201 goto done;
202 }
203
204 // debug_log("sorting list...\n");
205 qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare);
206
207 uint8_t* head = *info;
208 const int count = gHashTable.count;
209 for (i = 0 ; i < count ; i++) {
210 HashEntry* entry = list[i];
211 size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries);
212 if (entrySize < *infoSize) {
213 /* we're writing less than a full entry, clear out the rest */
214 /* TODO: only clear out the part we're not overwriting? */
215 memset(head, 0, *infoSize);
216 } else {
217 /* make sure the amount we're copying doesn't exceed the limit */
218 entrySize = *infoSize;
219 }
220 memcpy(head, &(entry->size), entrySize);
221 head += *infoSize;
222 }
223
224 dlfree(list);
225
226done:
227 // debug_log("+++++ done!\n");
228 pthread_mutex_unlock(&gAllocationsMutex);
229}
230
231void free_malloc_leak_info(uint8_t* info)
232{
233 dlfree(info);
234}
235
236struct mallinfo mallinfo()
237{
238 return dlmallinfo();
239}
240
241void* valloc(size_t bytes) {
242 /* assume page size of 4096 bytes */
243 return memalign( getpagesize(), bytes );
244}
245
246
247/*
248 * Code guarded by MALLOC_LEAK_CHECK is only needed when malloc check is
249 * enabled. Currently we exclude them in libc.so, and only include them in
250 * libc_debug.so.
251 */
252#ifdef MALLOC_LEAK_CHECK
253#define MALLOC_ALIGNMENT 8
254#define GUARD 0x48151642
255
256#define DEBUG 0
257
258// =============================================================================
259// Structures
260// =============================================================================
261typedef struct AllocationEntry AllocationEntry;
262struct AllocationEntry {
263 HashEntry* entry;
264 uint32_t guard;
265};
266
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800267
268// =============================================================================
269// Hash Table functions
270// =============================================================================
271static uint32_t get_hash(intptr_t* backtrace, size_t numEntries)
272{
273 if (backtrace == NULL) return 0;
274
275 int hash = 0;
276 size_t i;
277 for (i = 0 ; i < numEntries ; i++) {
278 hash = (hash * 33) + (backtrace[i] >> 2);
279 }
280
281 return hash;
282}
283
284static HashEntry* find_entry(HashTable* table, int slot,
285 intptr_t* backtrace, size_t numEntries, size_t size)
286{
287 HashEntry* entry = table->slots[slot];
288 while (entry != NULL) {
289 //debug_log("backtrace: %p, entry: %p entry->backtrace: %p\n",
290 // backtrace, entry, (entry != NULL) ? entry->backtrace : NULL);
291 /*
292 * See if the entry matches exactly. We compare the "size" field,
293 * including the flag bits.
294 */
295 if (entry->size == size && entry->numEntries == numEntries &&
296 !memcmp(backtrace, entry->backtrace, numEntries * sizeof(intptr_t))) {
297 return entry;
298 }
299
300 entry = entry->next;
301 }
302
303 return NULL;
304}
305
306static HashEntry* record_backtrace(intptr_t* backtrace, size_t numEntries, size_t size)
307{
308 size_t hash = get_hash(backtrace, numEntries);
309 size_t slot = hash % HASHTABLE_SIZE;
310
311 if (size & SIZE_FLAG_MASK) {
312 debug_log("malloc_debug: allocation %zx exceeds bit width\n", size);
313 abort();
314 }
315
316 if (gMallocLeakZygoteChild)
317 size |= SIZE_FLAG_ZYGOTE_CHILD;
318
319 HashEntry* entry = find_entry(&gHashTable, slot, backtrace, numEntries, size);
320
321 if (entry != NULL) {
322 entry->allocations++;
323 } else {
324 // create a new entry
325 entry = (HashEntry*)dlmalloc(sizeof(HashEntry) + numEntries*sizeof(intptr_t));
326 entry->allocations = 1;
327 entry->slot = slot;
328 entry->prev = NULL;
329 entry->next = gHashTable.slots[slot];
330 entry->numEntries = numEntries;
331 entry->size = size;
332
333 memcpy(entry->backtrace, backtrace, numEntries * sizeof(intptr_t));
334
335 gHashTable.slots[slot] = entry;
336
337 if (entry->next != NULL) {
338 entry->next->prev = entry;
339 }
340
341 // we just added an entry, increase the size of the hashtable
342 gHashTable.count++;
343 }
344
345 return entry;
346}
347
348static int is_valid_entry(HashEntry* entry)
349{
350 if (entry != NULL) {
351 int i;
352 for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
353 HashEntry* e1 = gHashTable.slots[i];
354
355 while (e1 != NULL) {
356 if (e1 == entry) {
357 return 1;
358 }
359
360 e1 = e1->next;
361 }
362 }
363 }
364
365 return 0;
366}
367
368static void remove_entry(HashEntry* entry)
369{
370 HashEntry* prev = entry->prev;
371 HashEntry* next = entry->next;
372
373 if (prev != NULL) entry->prev->next = next;
374 if (next != NULL) entry->next->prev = prev;
375
376 if (prev == NULL) {
377 // we are the head of the list. set the head to be next
378 gHashTable.slots[entry->slot] = entry->next;
379 }
380
381 // we just removed and entry, decrease the size of the hashtable
382 gHashTable.count--;
383}
384
385
386// =============================================================================
387// stack trace functions
388// =============================================================================
389
390typedef struct
391{
392 size_t count;
393 intptr_t* addrs;
394} stack_crawl_state_t;
395
396
397/* depends how the system includes define this */
398#ifdef HAVE_UNWIND_CONTEXT_STRUCT
399typedef struct _Unwind_Context __unwind_context;
400#else
401typedef _Unwind_Context __unwind_context;
402#endif
403
404static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg)
405{
406 stack_crawl_state_t* state = (stack_crawl_state_t*)arg;
407 if (state->count) {
408 intptr_t ip = (intptr_t)_Unwind_GetIP(context);
409 if (ip) {
410 state->addrs[0] = ip;
411 state->addrs++;
412 state->count--;
413 return _URC_NO_REASON;
414 }
415 }
416 /*
417 * If we run out of space to record the address or 0 has been seen, stop
418 * unwinding the stack.
419 */
420 return _URC_END_OF_STACK;
421}
422
423static inline
424int get_backtrace(intptr_t* addrs, size_t max_entries)
425{
426 stack_crawl_state_t state;
427 state.count = max_entries;
428 state.addrs = (intptr_t*)addrs;
429 _Unwind_Backtrace(trace_function, (void*)&state);
430 return max_entries - state.count;
431}
432
433// =============================================================================
434// malloc leak function dispatcher
435// =============================================================================
436
437static void* leak_malloc(size_t bytes);
438static void leak_free(void* mem);
439static void* leak_calloc(size_t n_elements, size_t elem_size);
440static void* leak_realloc(void* oldMem, size_t bytes);
441static void* leak_memalign(size_t alignment, size_t bytes);
442
443static void* fill_malloc(size_t bytes);
444static void fill_free(void* mem);
445static void* fill_realloc(void* oldMem, size_t bytes);
446static void* fill_memalign(size_t alignment, size_t bytes);
447
448static void* chk_malloc(size_t bytes);
449static void chk_free(void* mem);
450static void* chk_calloc(size_t n_elements, size_t elem_size);
451static void* chk_realloc(void* oldMem, size_t bytes);
452static void* chk_memalign(size_t alignment, size_t bytes);
453
454typedef struct {
455 void* (*malloc)(size_t bytes);
456 void (*free)(void* mem);
457 void* (*calloc)(size_t n_elements, size_t elem_size);
458 void* (*realloc)(void* oldMem, size_t bytes);
459 void* (*memalign)(size_t alignment, size_t bytes);
460} MallocDebug;
461
462static const MallocDebug gMallocEngineTable[] __attribute__((aligned(32))) =
463{
464 { dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign },
465 { leak_malloc, leak_free, leak_calloc, leak_realloc, leak_memalign },
466 { fill_malloc, fill_free, dlcalloc, fill_realloc, fill_memalign },
467 { chk_malloc, chk_free, chk_calloc, chk_realloc, chk_memalign }
468};
469
470enum {
471 INDEX_NORMAL = 0,
472 INDEX_LEAK_CHECK,
473 INDEX_MALLOC_FILL,
474 INDEX_MALLOC_CHECK,
475};
476
477static MallocDebug const * gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
478static int gMallocDebugLevel;
479static int gTrapOnError = 1;
480
481void* malloc(size_t bytes) {
482 return gMallocDispatch->malloc(bytes);
483}
484void free(void* mem) {
485 gMallocDispatch->free(mem);
486}
487void* calloc(size_t n_elements, size_t elem_size) {
488 return gMallocDispatch->calloc(n_elements, elem_size);
489}
490void* realloc(void* oldMem, size_t bytes) {
491 return gMallocDispatch->realloc(oldMem, bytes);
492}
493void* memalign(size_t alignment, size_t bytes) {
494 return gMallocDispatch->memalign(alignment, bytes);
495}
496
497// =============================================================================
498// malloc check functions
499// =============================================================================
500
501#define CHK_FILL_FREE 0xef
502#define CHK_SENTINEL_VALUE 0xeb
503#define CHK_SENTINEL_HEAD_SIZE 16
504#define CHK_SENTINEL_TAIL_SIZE 16
505#define CHK_OVERHEAD_SIZE ( CHK_SENTINEL_HEAD_SIZE + \
506 CHK_SENTINEL_TAIL_SIZE + \
507 sizeof(size_t) )
508
509static void dump_stack_trace()
510{
511 intptr_t addrs[20];
512 int c = get_backtrace(addrs, 20);
513 char buf[16];
514 char tmp[16*20];
515 int i;
516
517 tmp[0] = 0; // Need to initialize tmp[0] for the first strcat
518 for (i=0 ; i<c; i++) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200519 snprintf(buf, sizeof buf, "%2d: %08x\n", i, addrs[i]);
520 strlcat(tmp, buf, sizeof tmp);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800521 }
522 __libc_android_log_print(ANDROID_LOG_ERROR, "libc", "call stack:\n%s", tmp);
523}
524
525static int is_valid_malloc_pointer(void* addr)
526{
527 return 1;
528}
529
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200530static void assert_log_message(const char* format, ...)
531{
532 va_list args;
533
534 pthread_mutex_lock(&gAllocationsMutex);
535 gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
536 va_start(args, format);
537 __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc",
538 format, args);
539 va_end(args);
540 dump_stack_trace();
541 if (gTrapOnError) {
542 __builtin_trap();
543 }
544 gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK];
545 pthread_mutex_unlock(&gAllocationsMutex);
546}
547
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800548static void assert_valid_malloc_pointer(void* mem)
549{
550 if (mem && !is_valid_malloc_pointer(mem)) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200551 assert_log_message(
552 "*** MALLOC CHECK: buffer %p, is not a valid "
553 "malloc pointer (are you mixing up new/delete "
554 "and malloc/free?)", mem);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800555 }
556}
557
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200558/* Check that a given address corresponds to a guarded block,
559 * and returns its original allocation size in '*allocated'.
560 * 'func' is the capitalized name of the caller function.
561 * Returns 0 on success, or -1 on failure.
562 * NOTE: Does not return if gTrapOnError is set.
563 */
564static int chk_mem_check(void* mem,
565 size_t* allocated,
566 const char* func)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800567{
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200568 char* buffer;
569 size_t offset, bytes;
570 int i;
571 char* buf;
572
573 /* first check the bytes in the sentinel header */
574 buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800575 for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) {
576 if (buf[i] != CHK_SENTINEL_VALUE) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200577 assert_log_message(
578 "*** %s CHECK: buffer %p "
579 "corrupted %d bytes before allocation",
580 func, mem, CHK_SENTINEL_HEAD_SIZE-i);
581 return -1;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800582 }
583 }
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200584
585 /* then the ones in the sentinel trailer */
586 buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
587 offset = dlmalloc_usable_size(buffer) - sizeof(size_t);
588 bytes = *(size_t *)(buffer + offset);
589
590 buf = (char*)mem + bytes;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800591 for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) {
592 if (buf[i] != CHK_SENTINEL_VALUE) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200593 assert_log_message(
594 "*** %s CHECK: buffer %p, size=%lu, "
595 "corrupted %d bytes after allocation",
596 func, buffer, bytes, i+1);
597 return -1;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800598 }
599 }
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200600
601 *allocated = bytes;
602 return 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800603}
604
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200605
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800606void* chk_malloc(size_t bytes)
607{
608 char* buffer = (char*)dlmalloc(bytes + CHK_OVERHEAD_SIZE);
609 if (buffer) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200610 memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE);
611 size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t);
612 *(size_t *)(buffer + offset) = bytes;
613 buffer += CHK_SENTINEL_HEAD_SIZE;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800614 }
615 return buffer;
616}
617
618void chk_free(void* mem)
619{
620 assert_valid_malloc_pointer(mem);
621 if (mem) {
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200622 size_t size;
623 char* buffer;
624
625 if (chk_mem_check(mem, &size, "FREE") == 0) {
626 buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
627 memset(buffer, CHK_FILL_FREE, size + CHK_OVERHEAD_SIZE);
628 dlfree(buffer);
629 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800630 }
631}
632
633void* chk_calloc(size_t n_elements, size_t elem_size)
634{
635 size_t size;
636 void* ptr;
637
638 /* Fail on overflow - just to be safe even though this code runs only
639 * within the debugging C library, not the production one */
640 if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
641 return NULL;
642 }
643 size = n_elements * elem_size;
644 ptr = chk_malloc(size);
645 if (ptr != NULL) {
646 memset(ptr, 0, size);
647 }
648 return ptr;
649}
650
651void* chk_realloc(void* mem, size_t bytes)
652{
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200653 char* buffer;
654 int ret;
655 size_t old_bytes = 0;
656
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800657 assert_valid_malloc_pointer(mem);
David 'Digit' Turnerc4eee372009-07-08 14:22:41 +0200658
659 if (mem != NULL && chk_mem_check(mem, &old_bytes, "REALLOC") < 0)
660 return NULL;
661
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800662 char* new_buffer = chk_malloc(bytes);
663 if (mem == NULL) {
664 return new_buffer;
665 }
666
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800667 if (new_buffer) {
André Goddard Rosa291100c2010-02-05 16:32:56 -0200668 if (bytes > old_bytes)
669 bytes = old_bytes;
670 memcpy(new_buffer, mem, bytes);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800671 chk_free(mem);
672 }
673
674 return new_buffer;
675}
676
677void* chk_memalign(size_t alignment, size_t bytes)
678{
679 // XXX: it's better to use malloc, than being wrong
680 return chk_malloc(bytes);
681}
682
683// =============================================================================
684// malloc fill functions
685// =============================================================================
686
687void* fill_malloc(size_t bytes)
688{
689 void* buffer = dlmalloc(bytes);
690 if (buffer) {
691 memset(buffer, CHK_SENTINEL_VALUE, bytes);
692 }
693 return buffer;
694}
695
696void fill_free(void* mem)
697{
698 size_t bytes = dlmalloc_usable_size(mem);
699 memset(mem, CHK_FILL_FREE, bytes);
700 dlfree(mem);
701}
702
703void* fill_realloc(void* mem, size_t bytes)
704{
705 void* buffer = fill_malloc(bytes);
706 if (mem == NULL) {
707 return buffer;
708 }
709 if (buffer) {
710 size_t old_size = dlmalloc_usable_size(mem);
711 size_t size = (bytes < old_size)?(bytes):(old_size);
712 memcpy(buffer, mem, size);
713 fill_free(mem);
714 }
715 return buffer;
716}
717
718void* fill_memalign(size_t alignment, size_t bytes)
719{
720 void* buffer = dlmemalign(alignment, bytes);
721 if (buffer) {
722 memset(buffer, CHK_SENTINEL_VALUE, bytes);
723 }
724 return buffer;
725}
726
727// =============================================================================
728// malloc leak functions
729// =============================================================================
730
731#define MEMALIGN_GUARD ((void*)0xA1A41520)
732
733void* leak_malloc(size_t bytes)
734{
735 // allocate enough space infront of the allocation to store the pointer for
736 // the alloc structure. This will making free'ing the structer really fast!
737
738 // 1. allocate enough memory and include our header
739 // 2. set the base pointer to be right after our header
740
741 void* base = dlmalloc(bytes + sizeof(AllocationEntry));
742 if (base != NULL) {
743 pthread_mutex_lock(&gAllocationsMutex);
744
745 intptr_t backtrace[BACKTRACE_SIZE];
746 size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
747
748 AllocationEntry* header = (AllocationEntry*)base;
749 header->entry = record_backtrace(backtrace, numEntries, bytes);
750 header->guard = GUARD;
751
752 // now increment base to point to after our header.
753 // this should just work since our header is 8 bytes.
754 base = (AllocationEntry*)base + 1;
755
756 pthread_mutex_unlock(&gAllocationsMutex);
757 }
758
759 return base;
760}
761
762void leak_free(void* mem)
763{
764 if (mem != NULL) {
765 pthread_mutex_lock(&gAllocationsMutex);
766
767 // check the guard to make sure it is valid
768 AllocationEntry* header = (AllocationEntry*)mem - 1;
769
770 if (header->guard != GUARD) {
771 // could be a memaligned block
772 if (((void**)mem)[-1] == MEMALIGN_GUARD) {
773 mem = ((void**)mem)[-2];
774 header = (AllocationEntry*)mem - 1;
775 }
776 }
777
778 if (header->guard == GUARD || is_valid_entry(header->entry)) {
779 // decrement the allocations
780 HashEntry* entry = header->entry;
781 entry->allocations--;
782 if (entry->allocations <= 0) {
783 remove_entry(entry);
784 dlfree(entry);
785 }
786
787 // now free the memory!
788 dlfree(header);
789 } else {
790 debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
791 header->guard, header->entry);
792 }
793
794 pthread_mutex_unlock(&gAllocationsMutex);
795 }
796}
797
798void* leak_calloc(size_t n_elements, size_t elem_size)
799{
800 size_t size;
801 void* ptr;
802
803 /* Fail on overflow - just to be safe even though this code runs only
804 * within the debugging C library, not the production one */
805 if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
806 return NULL;
807 }
808 size = n_elements * elem_size;
809 ptr = leak_malloc(size);
810 if (ptr != NULL) {
811 memset(ptr, 0, size);
812 }
813 return ptr;
814}
815
816void* leak_realloc(void* oldMem, size_t bytes)
817{
818 if (oldMem == NULL) {
819 return leak_malloc(bytes);
820 }
821 void* newMem = NULL;
822 AllocationEntry* header = (AllocationEntry*)oldMem - 1;
823 if (header && header->guard == GUARD) {
824 size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK;
825 newMem = leak_malloc(bytes);
826 if (newMem != NULL) {
827 size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
828 memcpy(newMem, oldMem, copySize);
829 leak_free(oldMem);
830 }
831 } else {
832 newMem = dlrealloc(oldMem, bytes);
833 }
834 return newMem;
835}
836
837void* leak_memalign(size_t alignment, size_t bytes)
838{
839 // we can just use malloc
840 if (alignment <= MALLOC_ALIGNMENT)
841 return leak_malloc(bytes);
842
843 // need to make sure it's a power of two
844 if (alignment & (alignment-1))
845 alignment = 1L << (31 - __builtin_clz(alignment));
846
847 // here, aligment is at least MALLOC_ALIGNMENT<<1 bytes
848 // we will align by at least MALLOC_ALIGNMENT bytes
849 // and at most alignment-MALLOC_ALIGNMENT bytes
850 size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
851 void* base = leak_malloc(size);
852 if (base != NULL) {
853 intptr_t ptr = (intptr_t)base;
854 if ((ptr % alignment) == 0)
855 return base;
856
857 // align the pointer
858 ptr += ((-ptr) % alignment);
859
860 // there is always enough space for the base pointer and the guard
861 ((void**)ptr)[-1] = MEMALIGN_GUARD;
862 ((void**)ptr)[-2] = base;
863
864 return (void*)ptr;
865 }
866 return base;
867}
868#endif /* MALLOC_LEAK_CHECK */
869
870// called from libc_init()
871extern char* __progname;
872
873void malloc_debug_init()
874{
875 unsigned int level = 0;
876#ifdef MALLOC_LEAK_CHECK
877 // if MALLOC_LEAK_CHECK is enabled, use level=1 by default
878 level = 1;
879#endif
880 char env[PROP_VALUE_MAX];
881 int len = __system_property_get("libc.debug.malloc", env);
882
883 if (len) {
884 level = atoi(env);
885#ifndef MALLOC_LEAK_CHECK
886 /* Alert the user that libc_debug.so needs to be installed as libc.so
887 * when performing malloc checks.
888 */
889 if (level != 0) {
890 __libc_android_log_print(ANDROID_LOG_INFO, "libc",
891 "Malloc checks need libc_debug.so pushed to the device!\n");
892
893 }
894#endif
895 }
896
897#ifdef MALLOC_LEAK_CHECK
898 gMallocDebugLevel = level;
899 switch (level) {
900 default:
901 case 0:
902 gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
903 break;
904 case 1:
905 __libc_android_log_print(ANDROID_LOG_INFO, "libc",
906 "%s using MALLOC_DEBUG = %d (leak checker)\n",
907 __progname, level);
908 gMallocDispatch = &gMallocEngineTable[INDEX_LEAK_CHECK];
909 break;
910 case 5:
911 __libc_android_log_print(ANDROID_LOG_INFO, "libc",
912 "%s using MALLOC_DEBUG = %d (fill)\n",
913 __progname, level);
914 gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_FILL];
915 break;
916 case 10:
917 __libc_android_log_print(ANDROID_LOG_INFO, "libc",
918 "%s using MALLOC_DEBUG = %d (sentinels, fill)\n",
919 __progname, level);
920 gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK];
921 break;
922 }
923#endif
924}