am fdfa1677: am 362b2aab: Merge change Ib4550a04 into eclair-mr2

Merge commit 'fdfa16776ca8f33f671cbac5dd9fa8bd761e16a8'

* commit 'fdfa16776ca8f33f671cbac5dd9fa8bd761e16a8':
  Split libc_debug.so into two .so modules loaded on demand from libc.so
This commit is contained in:
Vladimir Chtchetkine 2009-11-19 17:37:46 -08:00 committed by Android Git Automerger
commit 870fa11830
9 changed files with 694 additions and 376 deletions

View File

@ -521,17 +521,12 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
$(libc_arch_static_src_files) \ $(libc_arch_static_src_files) \
bionic/dlmalloc.c \ bionic/dlmalloc.c \
bionic/malloc_debug_common.c \
bionic/libc_init_static.c bionic/libc_init_static.c
LOCAL_CFLAGS := $(libc_common_cflags) LOCAL_CFLAGS := $(libc_common_cflags) \
-DLIBC_STATIC
ifeq ($(WITH_MALLOC_CHECK_LIBC_A),true)
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
LOCAL_SRC_FILES += bionic/malloc_leak.c.arm
endif
LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_MODULE := libc LOCAL_MODULE := libc
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
LOCAL_SYSTEM_SHARED_LIBRARIES := LOCAL_SYSTEM_SHARED_LIBRARIES :=
@ -550,7 +545,7 @@ LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
$(libc_arch_dynamic_src_files) \ $(libc_arch_dynamic_src_files) \
bionic/dlmalloc.c \ bionic/dlmalloc.c \
bionic/malloc_leak.c.arm \ bionic/malloc_debug_common.c \
bionic/libc_init_dynamic.c bionic/libc_init_dynamic.c
LOCAL_MODULE:= libc LOCAL_MODULE:= libc
@ -570,8 +565,16 @@ LOCAL_SYSTEM_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY) include $(BUILD_SHARED_LIBRARY)
# For all builds, except for the -user build we will enable memory
# allocation checking (including memory leaks, buffer overwrites, etc.)
# Note that all these checks are also controlled by env. settings
# that can enable, or disable specific checks. Note also that some of
# the checks are available only in emulator and are implemeted in
# libc_malloc_qemu_instrumented.so.
ifneq ($(TARGET_BUILD_VARIANT),user)
# ======================================================== # ========================================================
# libc_debug.so # libc_malloc_debug_leak.so
# ======================================================== # ========================================================
include $(CLEAR_VARS) include $(CLEAR_VARS)
@ -582,30 +585,49 @@ LOCAL_CFLAGS := \
LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
$(libc_arch_dynamic_src_files) \ bionic/malloc_debug_leak.c
bionic/dlmalloc.c \
bionic/malloc_leak.c.arm \
bionic/libc_init_dynamic.c
LOCAL_MODULE:= libc_debug LOCAL_MODULE:= libc_malloc_debug_leak
# WARNING: The only library libc.so should depend on is libdl.so! If you add other libraries, LOCAL_SHARED_LIBRARIES := libc
# make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries. This
# ensures that symbols that are pulled into those new libraries from libgcc.a are not declared
# external; if that were the case, then libc would not pull those symbols from libgcc.a as it
# should, instead relying on the external symbols from the dependent libraries. That would
# create an "cloaked" dependency on libgcc.a in libc though the libraries, which is not what
# you wanted!
LOCAL_SHARED_LIBRARIES := libdl
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
LOCAL_SYSTEM_SHARED_LIBRARIES := LOCAL_SYSTEM_SHARED_LIBRARIES :=
# Don't prelink # Don't prelink
LOCAL_PRELINK_MODULE := false LOCAL_PRELINK_MODULE := false
# Don't install on release build # Don't install on release build
LOCAL_MODULE_TAGS := eng LOCAL_MODULE_TAGS := eng debug
include $(BUILD_SHARED_LIBRARY) include $(BUILD_SHARED_LIBRARY)
# ========================================================
# libc_malloc_debug_qemu.so
# ========================================================
include $(CLEAR_VARS)
LOCAL_CFLAGS := \
$(libc_common_cflags) \
-DMALLOC_QEMU_INSTRUMENT
LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_SRC_FILES := \
bionic/malloc_debug_qemu.c
LOCAL_MODULE:= libc_malloc_debug_qemu
LOCAL_SHARED_LIBRARIES := libc
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
LOCAL_SYSTEM_SHARED_LIBRARIES :=
# Don't prelink
LOCAL_PRELINK_MODULE := false
# Don't install on release build
LOCAL_MODULE_TAGS := eng debug
include $(BUILD_SHARED_LIBRARY)
endif #!user
# ======================================================== # ========================================================
include $(call all-makefiles-under,$(LOCAL_PATH)) include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@ -390,9 +390,9 @@ MALLINFO_FIELD_TYPE default: size_t
size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set
REALLOC_ZERO_BYTES_FREES default: not defined REALLOC_ZERO_BYTES_FREES default: not defined
This should be set if a call to realloc with zero bytes should This should be set if a call to realloc with zero bytes should
be the same as a call to free. Some people think it should. Otherwise, be the same as a call to free. Some people think it should. Otherwise,
since this malloc returns a unique pointer for malloc(0), so does since this malloc returns a unique pointer for malloc(0), so does
realloc(p, 0). realloc(p, 0).
LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
@ -671,7 +671,7 @@ extern "C" {
/* ------------------- Declarations of public routines ------------------- */ /* ------------------- Declarations of public routines ------------------- */
/* Check an additional macro for the five primary functions */ /* Check an additional macro for the five primary functions */
#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK) #ifndef USE_DL_PREFIX
#define dlcalloc calloc #define dlcalloc calloc
#define dlfree free #define dlfree free
#define dlmalloc malloc #define dlmalloc malloc
@ -3627,7 +3627,7 @@ static void* sys_alloc(mstate m, size_t nb) {
m->seg.sflags = mmap_flag; m->seg.sflags = mmap_flag;
m->magic = mparams.magic; m->magic = mparams.magic;
init_bins(m); init_bins(m);
if (is_global(m)) if (is_global(m))
init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
else { else {
/* Offset top by embedded malloc_state */ /* Offset top by embedded malloc_state */
@ -3778,7 +3778,7 @@ static int sys_trim(mstate m, size_t pad) {
} }
/* Unmap any unused mmapped segments */ /* Unmap any unused mmapped segments */
if (HAVE_MMAP) if (HAVE_MMAP)
released += release_unused_segments(m); released += release_unused_segments(m);
/* On failure, disable autotrim to avoid repeated failed future calls */ /* On failure, disable autotrim to avoid repeated failed future calls */
@ -3986,7 +3986,7 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
while (a < alignment) a <<= 1; while (a < alignment) a <<= 1;
alignment = a; alignment = a;
} }
if (bytes >= MAX_REQUEST - alignment) { if (bytes >= MAX_REQUEST - alignment) {
if (m != 0) { /* Test isn't needed but avoids compiler warning */ if (m != 0) { /* Test isn't needed but avoids compiler warning */
MALLOC_FAILURE_ACTION; MALLOC_FAILURE_ACTION;
@ -5446,5 +5446,5 @@ History:
Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu)
* Based loosely on libg++-1.2X malloc. (It retains some of the overall * Based loosely on libg++-1.2X malloc. (It retains some of the overall
structure of old version, but most details differ.) structure of old version, but most details differ.)
*/ */

View File

@ -1,14 +1,14 @@
/* /*
Default header file for malloc-2.8.x, written by Doug Lea Default header file for malloc-2.8.x, written by Doug Lea
and released to the public domain, as explained at and released to the public domain, as explained at
http://creativecommons.org/licenses/publicdomain. http://creativecommons.org/licenses/publicdomain.
last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee) last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee)
This header is for ANSI C/C++ only. You can set any of This header is for ANSI C/C++ only. You can set any of
the following #defines before including: the following #defines before including:
* If USE_DL_PREFIX is defined, it is assumed that malloc.c * If USE_DL_PREFIX is defined, it is assumed that malloc.c
was also compiled with this option, so all routines was also compiled with this option, so all routines
have names starting with "dl". have names starting with "dl".
@ -34,7 +34,7 @@ extern "C" {
#if !ONLY_MSPACES #if !ONLY_MSPACES
/* Check an additional macro for the five primary functions */ /* Check an additional macro for the five primary functions */
#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK) #if !defined(USE_DL_PREFIX)
#define dlcalloc calloc #define dlcalloc calloc
#define dlfree free #define dlfree free
#define dlmalloc malloc #define dlmalloc malloc

View File

@ -52,8 +52,6 @@
#include "libc_init_common.h" #include "libc_init_common.h"
#include <bionic_tls.h> #include <bionic_tls.h>
extern void malloc_debug_init();
/* We flag the __libc_preinit function as a constructor to ensure /* We flag the __libc_preinit function as a constructor to ensure
* that its address is listed in libc.so's .init_array section. * that its address is listed in libc.so's .init_array section.
* This ensures that the function is called by the dynamic linker * This ensures that the function is called by the dynamic linker
@ -78,12 +76,11 @@ void __libc_prenit(void)
__libc_init_common(elfdata); __libc_init_common(elfdata);
#ifdef MALLOC_LEAK_CHECK /* Setup malloc routines accordingly to the environment.
/* setup malloc leak checker, requires system properties */ * Requires system properties
*/
extern void malloc_debug_init(void); extern void malloc_debug_init(void);
malloc_debug_init(); malloc_debug_init();
#endif
} }
__noreturn void __libc_init(uintptr_t *elfdata, __noreturn void __libc_init(uintptr_t *elfdata,

View File

@ -68,12 +68,6 @@ __noreturn void __libc_init(uintptr_t *elfdata,
/* Initialize the C runtime environment */ /* Initialize the C runtime environment */
__libc_init_common(elfdata); __libc_init_common(elfdata);
#ifdef MALLOC_LEAK_CHECK
/* setup malloc leak checker, requires system properties */
extern void malloc_debug_init(void);
malloc_debug_init();
#endif
/* Several Linux ABIs don't pass the onexit pointer, and the ones that /* Several Linux ABIs don't pass the onexit pointer, and the ones that
* do never use it. Therefore, we ignore it. * do never use it. Therefore, we ignore it.
*/ */

View File

@ -0,0 +1,432 @@
/*
* Copyright (C) 2009 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Contains definition of global variables and implementation of routines
* that are used by malloc leak detection code and other components in
* the system. The trick is that some components expect these data and
* routines to be defined / implemented in libc.so library, regardless
* whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
* more tricky, malloc leak detection code, implemented in
* libc_malloc_debug.so also requires access to these variables and routines
* (to fill allocation entry hash table, for example). So, all relevant
* variables and routines are defined / implemented here and exported
* to all, leak detection code and other components via dynamic (libc.so),
* or static (libc.a) linking.
*/
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "dlmalloc.h"
#include "malloc_debug_common.h"
/*
* In a VM process, this is set to 1 after fork()ing out of zygote.
*/
int gMallocLeakZygoteChild = 0;
pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER;
HashTable gHashTable;
// =============================================================================
// output functions
// =============================================================================
static int hash_entry_compare(const void* arg1, const void* arg2)
{
HashEntry* e1 = *(HashEntry**)arg1;
HashEntry* e2 = *(HashEntry**)arg2;
size_t nbAlloc1 = e1->allocations;
size_t nbAlloc2 = e2->allocations;
size_t size1 = e1->size & ~SIZE_FLAG_MASK;
size_t size2 = e2->size & ~SIZE_FLAG_MASK;
size_t alloc1 = nbAlloc1 * size1;
size_t alloc2 = nbAlloc2 * size2;
// sort in descending order by:
// 1) total size
// 2) number of allocations
//
// This is used for sorting, not determination of equality, so we don't
// need to compare the bit flags.
int result;
if (alloc1 > alloc2) {
result = -1;
} else if (alloc1 < alloc2) {
result = 1;
} else {
if (nbAlloc1 > nbAlloc2) {
result = -1;
} else if (nbAlloc1 < nbAlloc2) {
result = 1;
} else {
result = 0;
}
}
return result;
}
/*
* Retrieve native heap information.
*
* "*info" is set to a buffer we allocate
* "*overallSize" is set to the size of the "info" buffer
* "*infoSize" is set to the size of a single entry
* "*totalMemory" is set to the sum of all allocations we're tracking; does
* not include heap overhead
* "*backtraceSize" is set to the maximum number of entries in the back trace
*/
void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize)
{
// don't do anything if we have invalid arguments
if (info == NULL || overallSize == NULL || infoSize == NULL ||
totalMemory == NULL || backtraceSize == NULL) {
return;
}
pthread_mutex_lock(&gAllocationsMutex);
if (gHashTable.count == 0) {
*info = NULL;
*overallSize = 0;
*infoSize = 0;
*totalMemory = 0;
*backtraceSize = 0;
goto done;
}
void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count);
// get the entries into an array to be sorted
int index = 0;
int i;
for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
HashEntry* entry = gHashTable.slots[i];
while (entry != NULL) {
list[index] = entry;
*totalMemory = *totalMemory +
((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
index++;
entry = entry->next;
}
}
// XXX: the protocol doesn't allow variable size for the stack trace (yet)
*infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE);
*overallSize = *infoSize * gHashTable.count;
*backtraceSize = BACKTRACE_SIZE;
// now get A byte array big enough for this
*info = (uint8_t*)dlmalloc(*overallSize);
if (*info == NULL) {
*overallSize = 0;
goto done;
}
qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare);
uint8_t* head = *info;
const int count = gHashTable.count;
for (i = 0 ; i < count ; i++) {
HashEntry* entry = list[i];
size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries);
if (entrySize < *infoSize) {
/* we're writing less than a full entry, clear out the rest */
/* TODO: only clear out the part we're not overwriting? */
memset(head, 0, *infoSize);
} else {
/* make sure the amount we're copying doesn't exceed the limit */
entrySize = *infoSize;
}
memcpy(head, &(entry->size), entrySize);
head += *infoSize;
}
dlfree(list);
done:
pthread_mutex_unlock(&gAllocationsMutex);
}
void free_malloc_leak_info(uint8_t* info)
{
dlfree(info);
}
struct mallinfo mallinfo()
{
return dlmallinfo();
}
void* valloc(size_t bytes) {
/* assume page size of 4096 bytes */
return memalign( getpagesize(), bytes );
}
/* Support for malloc debugging.
* Note that if USE_DL_PREFIX is not defined, it's assumed that memory
* allocation routines are implemented somewhere else, so all our custom
* malloc routines should not be compiled at all.
*/
#ifdef USE_DL_PREFIX
/* Table for dispatching malloc calls, initialized with default dispatchers. */
const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) =
{
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
};
/* Selector of dispatch table to use for dispatching malloc calls. */
const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
void* malloc(size_t bytes) {
return __libc_malloc_dispatch->malloc(bytes);
}
void free(void* mem) {
__libc_malloc_dispatch->free(mem);
}
void* calloc(size_t n_elements, size_t elem_size) {
return __libc_malloc_dispatch->calloc(n_elements, elem_size);
}
void* realloc(void* oldMem, size_t bytes) {
return __libc_malloc_dispatch->realloc(oldMem, bytes);
}
void* memalign(size_t alignment, size_t bytes) {
return __libc_malloc_dispatch->memalign(alignment, bytes);
}
/* We implement malloc debugging only in libc.so, so code bellow
* must be excluded if we compile this file for static libc.a
*/
#ifndef LIBC_STATIC
#include <sys/system_properties.h>
#include <dlfcn.h>
#include "logd.h"
// =============================================================================
// log functions
// =============================================================================
#define debug_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ )
#define error_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
#define info_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
/* Table for dispatching malloc calls, depending on environment. */
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
};
extern char* __progname;
/* Handle to shared library where actual memory allocation is implemented.
* This library is loaded and memory allocation calls are redirected there
* when libc.debug.malloc environment variable contains value other than
* zero:
* 1 - For memory leak detections.
* 5 - For filling allocated / freed memory with patterns defined by
* CHK_SENTINEL_VALUE, and CHK_FILL_FREE macros.
* 10 - For adding pre-, and post- allocation stubs in order to detect
* buffer overruns.
* 20 - For enabling emulator memory allocation instrumentation detecting
* memory leaks and buffer overruns.
* Actual functionality for debug levels 1-10 is implemented in
* libc_malloc_debug_leak.so, while functionality for debug level 20 is
* implemented in libc_malloc_debug_qemu.so and can be run inside the
* emulator only.
*/
static void* libc_malloc_impl_handle = NULL;
/* Initializes memory allocation framework once per process. */
static void malloc_init_impl(void)
{
const char* so_name = NULL;
unsigned int debug_level = 0;
unsigned int qemu_running = 0;
int len;
char env[PROP_VALUE_MAX];
// Get custom malloc debug level.
len = __system_property_get("libc.debug.malloc", env);
if (len) {
debug_level = atoi(env);
}
/* Debug level 0 means that we should use dlxxx allocation
* routines (default).
*/
if (!debug_level) {
return;
}
// Get emulator running status.
len = __system_property_get("ro.kernel.qemu", env);
if (len) {
qemu_running = atoi(env);
}
// Lets see which .so must be loaded for the requested debug level
switch (debug_level) {
case 1:
case 5:
case 10:
so_name = "/system/lib/libc_malloc_debug_leak.so";
break;
case 20:
// Quick check: debug level 20 can only be handled in emulator
if (!qemu_running) {
info_log("%s: Debug level %d can only be set in emulator\n",
__progname, debug_level);
return;
}
so_name = "/system/lib/libc_malloc_debug_qemu.so";
break;
default:
info_log("%s: Debug level %d is unknown\n",
__progname, debug_level);
return;
}
// Load .so that implements the required malloc debugging functionality.
libc_malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
if (libc_malloc_impl_handle == NULL) {
error_log("%s: Missing module %s required for malloc debug level %d\n",
__progname, so_name, debug_level);
return;
}
// Initialize malloc dispatch table with appropriate routines.
switch (debug_level) {
case 1:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (leak checker)\n",
__progname, debug_level);
gMallocUse.malloc =
dlsym(libc_malloc_impl_handle, "leak_malloc");
gMallocUse.free =
dlsym(libc_malloc_impl_handle, "leak_free");
gMallocUse.calloc =
dlsym(libc_malloc_impl_handle, "leak_calloc");
gMallocUse.realloc =
dlsym(libc_malloc_impl_handle, "leak_realloc");
gMallocUse.memalign =
dlsym(libc_malloc_impl_handle, "leak_memalign");
break;
case 5:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (fill)\n",
__progname, debug_level);
gMallocUse.malloc =
dlsym(libc_malloc_impl_handle, "fill_malloc");
gMallocUse.free =
dlsym(libc_malloc_impl_handle, "fill_free");
gMallocUse.calloc = dlcalloc;
gMallocUse.realloc =
dlsym(libc_malloc_impl_handle, "fill_realloc");
gMallocUse.memalign =
dlsym(libc_malloc_impl_handle, "fill_memalign");
break;
case 10:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (sentinels, fill)\n",
__progname, debug_level);
gMallocUse.malloc =
dlsym(libc_malloc_impl_handle, "chk_malloc");
gMallocUse.free =
dlsym(libc_malloc_impl_handle, "chk_free");
gMallocUse.calloc =
dlsym(libc_malloc_impl_handle, "chk_calloc");
gMallocUse.realloc =
dlsym(libc_malloc_impl_handle, "chk_realloc");
gMallocUse.memalign =
dlsym(libc_malloc_impl_handle, "chk_memalign");
break;
case 20:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (instrumented for emulator)\n",
__progname, debug_level);
gMallocUse.malloc =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_malloc");
gMallocUse.free =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_free");
gMallocUse.calloc =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_calloc");
gMallocUse.realloc =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_realloc");
gMallocUse.memalign =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_memalign");
break;
default:
break;
}
// Make sure dispatch table is initialized
if ((gMallocUse.malloc == NULL) ||
(gMallocUse.free == NULL) ||
(gMallocUse.calloc == NULL) ||
(gMallocUse.realloc == NULL) ||
(gMallocUse.memalign == NULL)) {
error_log("%s: Cannot initialize malloc dispatch table for debug level"
" %d: %p, %p, %p, %p, %p\n",
__progname, debug_level,
gMallocUse.malloc, gMallocUse.free,
gMallocUse.calloc, gMallocUse.realloc,
gMallocUse.memalign);
dlclose(libc_malloc_impl_handle);
libc_malloc_impl_handle = NULL;
} else {
__libc_malloc_dispatch = &gMallocUse;
}
}
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
#endif // !LIBC_STATIC
#endif // USE_DL_PREFIX
/* Initializes memory allocation framework.
* This routine is called from __libc_init routines implemented
* in libc_init_static.c and libc_init_dynamic.c files.
*/
void malloc_debug_init(void)
{
/* We need to initialize malloc iff we impelement here custom
* malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so
*/
#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
error_log("Unable to initialize malloc_debug component.");
}
#endif // USE_DL_PREFIX && !LIBC_STATIC
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2009 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Contains declarations of types and constants used by malloc leak
* detection code in both, libc and libc_malloc_debug libraries.
*/
#ifndef MALLOC_DEBUG_COMMON_H
#define MALLOC_DEBUG_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
#define HASHTABLE_SIZE 1543
#define BACKTRACE_SIZE 32
/* flag definitions, currently sharing storage with "size" */
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
#define MAX_SIZE_T (~(size_t)0)
// =============================================================================
// Structures
// =============================================================================
typedef struct HashEntry HashEntry;
struct HashEntry {
size_t slot;
HashEntry* prev;
HashEntry* next;
size_t numEntries;
// fields above "size" are NOT sent to the host
size_t size;
size_t allocations;
intptr_t backtrace[0];
};
typedef struct HashTable HashTable;
struct HashTable {
size_t count;
HashEntry* slots[HASHTABLE_SIZE];
};
/* Entry in malloc dispatch table. */
typedef struct MallocDebug MallocDebug;
struct MallocDebug {
void* (*malloc)(size_t bytes);
void (*free)(void* mem);
void* (*calloc)(size_t n_elements, size_t elem_size);
void* (*realloc)(void* oldMem, size_t bytes);
void* (*memalign)(size_t alignment, size_t bytes);
};
#ifdef __cplusplus
}; /* end of extern "C" */
#endif
#endif // MALLOC_DEBUG_COMMON_H

View File

@ -38,6 +38,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <fcntl.h> #include <fcntl.h>
#include <unwind.h> #include <unwind.h>
#include <dlfcn.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@ -47,212 +48,37 @@
#include "dlmalloc.h" #include "dlmalloc.h"
#include "logd.h" #include "logd.h"
#include "malloc_debug_common.h"
// ============================================================================= // This file should be included into the build only when
// Utilities directly used by Dalvik // MALLOC_LEAK_CHECK, or MALLOC_QEMU_INSTRUMENT, or both
// ============================================================================= // macros are defined.
#ifndef MALLOC_LEAK_CHECK
#error MALLOC_LEAK_CHECK is not defined.
#endif // !MALLOC_LEAK_CHECK
#define HASHTABLE_SIZE 1543 // Global variables defined in malloc_debug_common.c
#define BACKTRACE_SIZE 32 extern int gMallocLeakZygoteChild;
/* flag definitions, currently sharing storage with "size" */ extern pthread_mutex_t gAllocationsMutex;
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) extern HashTable gHashTable;
#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD) extern const MallocDebug __libc_malloc_default_dispatch;
extern const MallocDebug* __libc_malloc_dispatch;
#define MAX_SIZE_T (~(size_t)0)
/*
* In a VM process, this is set to 1 after fork()ing out of zygote.
*/
int gMallocLeakZygoteChild = 0;
// =============================================================================
// Structures
// =============================================================================
typedef struct HashEntry HashEntry;
struct HashEntry {
size_t slot;
HashEntry* prev;
HashEntry* next;
size_t numEntries;
// fields above "size" are NOT sent to the host
size_t size;
size_t allocations;
intptr_t backtrace[0];
};
typedef struct HashTable HashTable;
struct HashTable {
size_t count;
HashEntry* slots[HASHTABLE_SIZE];
};
static pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER;
static HashTable gHashTable;
// ============================================================================= // =============================================================================
// log functions // log functions
// ============================================================================= // =============================================================================
#define debug_log(format, ...) \ #define debug_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak", (format), ##__VA_ARGS__ ) __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ )
#define error_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ )
#define info_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
// ============================================================================= static int gTrapOnError = 1;
// output functions
// =============================================================================
static int hash_entry_compare(const void* arg1, const void* arg2)
{
HashEntry* e1 = *(HashEntry**)arg1;
HashEntry* e2 = *(HashEntry**)arg2;
size_t nbAlloc1 = e1->allocations;
size_t nbAlloc2 = e2->allocations;
size_t size1 = e1->size & ~SIZE_FLAG_MASK;
size_t size2 = e2->size & ~SIZE_FLAG_MASK;
size_t alloc1 = nbAlloc1 * size1;
size_t alloc2 = nbAlloc2 * size2;
// sort in descending order by:
// 1) total size
// 2) number of allocations
//
// This is used for sorting, not determination of equality, so we don't
// need to compare the bit flags.
int result;
if (alloc1 > alloc2) {
result = -1;
} else if (alloc1 < alloc2) {
result = 1;
} else {
if (nbAlloc1 > nbAlloc2) {
result = -1;
} else if (nbAlloc1 < nbAlloc2) {
result = 1;
} else {
result = 0;
}
}
return result;
}
/*
* Retrieve native heap information.
*
* "*info" is set to a buffer we allocate
* "*overallSize" is set to the size of the "info" buffer
* "*infoSize" is set to the size of a single entry
* "*totalMemory" is set to the sum of all allocations we're tracking; does
* not include heap overhead
* "*backtraceSize" is set to the maximum number of entries in the back trace
*/
void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize)
{
// don't do anything if we have invalid arguments
if (info == NULL || overallSize == NULL || infoSize == NULL ||
totalMemory == NULL || backtraceSize == NULL) {
return;
}
pthread_mutex_lock(&gAllocationsMutex);
if (gHashTable.count == 0) {
*info = NULL;
*overallSize = 0;
*infoSize = 0;
*totalMemory = 0;
*backtraceSize = 0;
goto done;
}
void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count);
// debug_log("*****\ngHashTable.count = %d\n", gHashTable.count);
// debug_log("list = %p\n", list);
// get the entries into an array to be sorted
int index = 0;
int i;
for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
HashEntry* entry = gHashTable.slots[i];
while (entry != NULL) {
list[index] = entry;
*totalMemory = *totalMemory +
((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
index++;
entry = entry->next;
}
}
// debug_log("sorted list!\n");
// XXX: the protocol doesn't allow variable size for the stack trace (yet)
*infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE);
*overallSize = *infoSize * gHashTable.count;
*backtraceSize = BACKTRACE_SIZE;
// debug_log("infoSize = 0x%x overall = 0x%x\n", *infoSize, *overallSize);
// now get A byte array big enough for this
*info = (uint8_t*)dlmalloc(*overallSize);
// debug_log("info = %p\n", info);
if (*info == NULL) {
*overallSize = 0;
goto done;
}
// debug_log("sorting list...\n");
qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare);
uint8_t* head = *info;
const int count = gHashTable.count;
for (i = 0 ; i < count ; i++) {
HashEntry* entry = list[i];
size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries);
if (entrySize < *infoSize) {
/* we're writing less than a full entry, clear out the rest */
/* TODO: only clear out the part we're not overwriting? */
memset(head, 0, *infoSize);
} else {
/* make sure the amount we're copying doesn't exceed the limit */
entrySize = *infoSize;
}
memcpy(head, &(entry->size), entrySize);
head += *infoSize;
}
dlfree(list);
done:
// debug_log("+++++ done!\n");
pthread_mutex_unlock(&gAllocationsMutex);
}
void free_malloc_leak_info(uint8_t* info)
{
dlfree(info);
}
struct mallinfo mallinfo()
{
return dlmallinfo();
}
void* valloc(size_t bytes) {
/* assume page size of 4096 bytes */
return memalign( getpagesize(), bytes );
}
/*
* Code guarded by MALLOC_LEAK_CHECK is only needed when malloc check is
* enabled. Currently we exclude them in libc.so, and only include them in
* libc_debug.so.
*/
#ifdef MALLOC_LEAK_CHECK
#define MALLOC_ALIGNMENT 8 #define MALLOC_ALIGNMENT 8
#define GUARD 0x48151642 #define GUARD 0x48151642
#define DEBUG 0 #define DEBUG 0
// ============================================================================= // =============================================================================
@ -407,13 +233,13 @@ static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg)
if (state->count) { if (state->count) {
intptr_t ip = (intptr_t)_Unwind_GetIP(context); intptr_t ip = (intptr_t)_Unwind_GetIP(context);
if (ip) { if (ip) {
state->addrs[0] = ip; state->addrs[0] = ip;
state->addrs++; state->addrs++;
state->count--; state->count--;
return _URC_NO_REASON; return _URC_NO_REASON;
} }
} }
/* /*
* If we run out of space to record the address or 0 has been seen, stop * If we run out of space to record the address or 0 has been seen, stop
* unwinding the stack. * unwinding the stack.
*/ */
@ -430,70 +256,6 @@ int get_backtrace(intptr_t* addrs, size_t max_entries)
return max_entries - state.count; return max_entries - state.count;
} }
// =============================================================================
// malloc leak function dispatcher
// =============================================================================
static void* leak_malloc(size_t bytes);
static void leak_free(void* mem);
static void* leak_calloc(size_t n_elements, size_t elem_size);
static void* leak_realloc(void* oldMem, size_t bytes);
static void* leak_memalign(size_t alignment, size_t bytes);
static void* fill_malloc(size_t bytes);
static void fill_free(void* mem);
static void* fill_realloc(void* oldMem, size_t bytes);
static void* fill_memalign(size_t alignment, size_t bytes);
static void* chk_malloc(size_t bytes);
static void chk_free(void* mem);
static void* chk_calloc(size_t n_elements, size_t elem_size);
static void* chk_realloc(void* oldMem, size_t bytes);
static void* chk_memalign(size_t alignment, size_t bytes);
typedef struct {
void* (*malloc)(size_t bytes);
void (*free)(void* mem);
void* (*calloc)(size_t n_elements, size_t elem_size);
void* (*realloc)(void* oldMem, size_t bytes);
void* (*memalign)(size_t alignment, size_t bytes);
} MallocDebug;
static const MallocDebug gMallocEngineTable[] __attribute__((aligned(32))) =
{
{ dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign },
{ leak_malloc, leak_free, leak_calloc, leak_realloc, leak_memalign },
{ fill_malloc, fill_free, dlcalloc, fill_realloc, fill_memalign },
{ chk_malloc, chk_free, chk_calloc, chk_realloc, chk_memalign }
};
enum {
INDEX_NORMAL = 0,
INDEX_LEAK_CHECK,
INDEX_MALLOC_FILL,
INDEX_MALLOC_CHECK,
};
static MallocDebug const * gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
static int gMallocDebugLevel;
static int gTrapOnError = 1;
void* malloc(size_t bytes) {
return gMallocDispatch->malloc(bytes);
}
void free(void* mem) {
gMallocDispatch->free(mem);
}
void* calloc(size_t n_elements, size_t elem_size) {
return gMallocDispatch->calloc(n_elements, elem_size);
}
void* realloc(void* oldMem, size_t bytes) {
return gMallocDispatch->realloc(oldMem, bytes);
}
void* memalign(size_t alignment, size_t bytes) {
return gMallocDispatch->memalign(alignment, bytes);
}
// ============================================================================= // =============================================================================
// malloc check functions // malloc check functions
// ============================================================================= // =============================================================================
@ -532,7 +294,9 @@ static void assert_log_message(const char* format, ...)
va_list args; va_list args;
pthread_mutex_lock(&gAllocationsMutex); pthread_mutex_lock(&gAllocationsMutex);
gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL]; {
const MallocDebug* current_dispatch = __libc_malloc_dispatch;
__libc_malloc_dispatch = &__libc_malloc_default_dispatch;
va_start(args, format); va_start(args, format);
__libc_android_log_vprint(ANDROID_LOG_ERROR, "libc", __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc",
format, args); format, args);
@ -541,7 +305,8 @@ static void assert_log_message(const char* format, ...)
if (gTrapOnError) { if (gTrapOnError) {
__builtin_trap(); __builtin_trap();
} }
gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK]; __libc_malloc_dispatch = current_dispatch;
}
pthread_mutex_unlock(&gAllocationsMutex); pthread_mutex_unlock(&gAllocationsMutex);
} }
@ -574,7 +339,7 @@ static int chk_mem_check(void* mem,
buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE; buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) { for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) {
if (buf[i] != CHK_SENTINEL_VALUE) { if (buf[i] != CHK_SENTINEL_VALUE) {
assert_log_message( assert_log_message(
"*** %s CHECK: buffer %p " "*** %s CHECK: buffer %p "
"corrupted %d bytes before allocation", "corrupted %d bytes before allocation",
func, mem, CHK_SENTINEL_HEAD_SIZE-i); func, mem, CHK_SENTINEL_HEAD_SIZE-i);
@ -590,7 +355,7 @@ static int chk_mem_check(void* mem,
buf = (char*)mem + bytes; buf = (char*)mem + bytes;
for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) { for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) {
if (buf[i] != CHK_SENTINEL_VALUE) { if (buf[i] != CHK_SENTINEL_VALUE) {
assert_log_message( assert_log_message(
"*** %s CHECK: buffer %p, size=%lu, " "*** %s CHECK: buffer %p, size=%lu, "
"corrupted %d bytes after allocation", "corrupted %d bytes after allocation",
func, buffer, bytes, i+1); func, buffer, bytes, i+1);
@ -743,11 +508,11 @@ void* leak_malloc(size_t bytes)
intptr_t backtrace[BACKTRACE_SIZE]; intptr_t backtrace[BACKTRACE_SIZE];
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE); size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
AllocationEntry* header = (AllocationEntry*)base; AllocationEntry* header = (AllocationEntry*)base;
header->entry = record_backtrace(backtrace, numEntries, bytes); header->entry = record_backtrace(backtrace, numEntries, bytes);
header->guard = GUARD; header->guard = GUARD;
// now increment base to point to after our header. // now increment base to point to after our header.
// this should just work since our header is 8 bytes. // this should just work since our header is 8 bytes.
base = (AllocationEntry*)base + 1; base = (AllocationEntry*)base + 1;
@ -765,7 +530,7 @@ void leak_free(void* mem)
// check the guard to make sure it is valid // check the guard to make sure it is valid
AllocationEntry* header = (AllocationEntry*)mem - 1; AllocationEntry* header = (AllocationEntry*)mem - 1;
if (header->guard != GUARD) { if (header->guard != GUARD) {
// could be a memaligned block // could be a memaligned block
if (((void**)mem)[-1] == MEMALIGN_GUARD) { if (((void**)mem)[-1] == MEMALIGN_GUARD) {
@ -773,7 +538,7 @@ void leak_free(void* mem)
header = (AllocationEntry*)mem - 1; header = (AllocationEntry*)mem - 1;
} }
} }
if (header->guard == GUARD || is_valid_entry(header->entry)) { if (header->guard == GUARD || is_valid_entry(header->entry)) {
// decrement the allocations // decrement the allocations
HashEntry* entry = header->entry; HashEntry* entry = header->entry;
@ -842,7 +607,7 @@ void* leak_memalign(size_t alignment, size_t bytes)
// need to make sure it's a power of two // need to make sure it's a power of two
if (alignment & (alignment-1)) if (alignment & (alignment-1))
alignment = 1L << (31 - __builtin_clz(alignment)); alignment = 1L << (31 - __builtin_clz(alignment));
// here, aligment is at least MALLOC_ALIGNMENT<<1 bytes // here, aligment is at least MALLOC_ALIGNMENT<<1 bytes
// we will align by at least MALLOC_ALIGNMENT bytes // we will align by at least MALLOC_ALIGNMENT bytes
// and at most alignment-MALLOC_ALIGNMENT bytes // and at most alignment-MALLOC_ALIGNMENT bytes
@ -855,7 +620,7 @@ void* leak_memalign(size_t alignment, size_t bytes)
// align the pointer // align the pointer
ptr += ((-ptr) % alignment); ptr += ((-ptr) % alignment);
// there is always enough space for the base pointer and the guard // there is always enough space for the base pointer and the guard
((void**)ptr)[-1] = MEMALIGN_GUARD; ((void**)ptr)[-1] = MEMALIGN_GUARD;
((void**)ptr)[-2] = base; ((void**)ptr)[-2] = base;
@ -864,60 +629,3 @@ void* leak_memalign(size_t alignment, size_t bytes)
} }
return base; return base;
} }
#endif /* MALLOC_LEAK_CHECK */
// called from libc_init()
extern char* __progname;
void malloc_debug_init()
{
unsigned int level = 0;
#ifdef MALLOC_LEAK_CHECK
// if MALLOC_LEAK_CHECK is enabled, use level=1 by default
level = 1;
#endif
char env[PROP_VALUE_MAX];
int len = __system_property_get("libc.debug.malloc", env);
if (len) {
level = atoi(env);
#ifndef MALLOC_LEAK_CHECK
/* Alert the user that libc_debug.so needs to be installed as libc.so
* when performing malloc checks.
*/
if (level != 0) {
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"Malloc checks need libc_debug.so pushed to the device!\n");
}
#endif
}
#ifdef MALLOC_LEAK_CHECK
gMallocDebugLevel = level;
switch (level) {
default:
case 0:
gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
break;
case 1:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (leak checker)\n",
__progname, level);
gMallocDispatch = &gMallocEngineTable[INDEX_LEAK_CHECK];
break;
case 5:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (fill)\n",
__progname, level);
gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_FILL];
break;
case 10:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
"%s using MALLOC_DEBUG = %d (sentinels, fill)\n",
__progname, level);
gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK];
break;
}
#endif
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2009 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Contains implementation of memeory allocation routines instrumented for
* usage in the emulator to detect memory allocation violations, such as
* memory leaks, buffer overruns, etc.
*/
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "dlmalloc.h"
#include "logd.h"
// This file should be included into the build only when
// MALLOC_QEMU_INSTRUMENT macro is defined.
#ifndef MALLOC_QEMU_INSTRUMENT
#error MALLOC_QEMU_INSTRUMENT is not defined.
#endif // MALLOC_QEMU_INSTRUMENT
// =============================================================================
// log functions
// =============================================================================
#define debug_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_qemu", (format), ##__VA_ARGS__ )
#define error_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_ERROR, "malloc_qemu", (format), ##__VA_ARGS__ )
#define info_log(format, ...) \
__libc_android_log_print(ANDROID_LOG_INFO, "malloc_qemu", (format), ##__VA_ARGS__ )
void* qemu_instrumented_malloc(size_t bytes)
{
return dlmalloc(bytes);
}
void qemu_instrumented_free(void* mem)
{
dlfree(mem);
}
void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
{
return dlcalloc(n_elements, elem_size);
}
void* qemu_instrumented_realloc(void* mem, size_t bytes)
{
return dlrealloc(mem, bytes);
}
void* qemu_instrumented_memalign(size_t alignment, size_t bytes)
{
return dlmemalign(alignment, bytes);
}