Fix debug malloc.
Previously the dynamically-loaded part of the debug malloc implementation wanted to access hidden symbols in libc itself. Bug: 15426546 Change-Id: I6a366ef626854d1af1d705ca24842817b1c02a19
This commit is contained in:
parent
e120cba31d
commit
8e52e8fe83
5 changed files with 175 additions and 205 deletions
|
@ -51,10 +51,6 @@
|
|||
#include "malloc_debug_common.h"
|
||||
#include "private/ScopedPthreadMutexLocker.h"
|
||||
|
||||
/* libc.debug.malloc.backlog */
|
||||
extern unsigned int g_malloc_debug_backlog;
|
||||
extern int g_malloc_debug_level;
|
||||
|
||||
#define MAX_BACKTRACE_DEPTH 16
|
||||
#define ALLOCATION_TAG 0x1ee7d00d
|
||||
#define BACKLOG_TAG 0xbabecafe
|
||||
|
@ -120,6 +116,12 @@ static hdr_t* backlog_tail;
|
|||
static hdr_t* backlog_head;
|
||||
static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// This variable is set to the value of property libc.debug.malloc.backlog.
|
||||
// It determines the size of the backlog we use to detect multiple frees.
|
||||
static unsigned g_malloc_debug_backlog = 100;
|
||||
|
||||
__LIBC_HIDDEN__ HashTable* g_hash_table;
|
||||
|
||||
static inline void init_front_guard(hdr_t* hdr) {
|
||||
memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN);
|
||||
}
|
||||
|
@ -508,11 +510,6 @@ extern "C" size_t chk_malloc_usable_size(const void* ptr) {
|
|||
}
|
||||
|
||||
static void ReportMemoryLeaks() {
|
||||
// We only track leaks at level 10.
|
||||
if (g_malloc_debug_level != 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use /proc/self/exe link to obtain the program name for logging
|
||||
// purposes. If it's not available, we set it to "<unknown>".
|
||||
char exe[PATH_MAX];
|
||||
|
@ -546,12 +543,23 @@ static void ReportMemoryLeaks() {
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" int malloc_debug_initialize() {
|
||||
extern "C" bool malloc_debug_initialize(HashTable* hash_table) {
|
||||
g_hash_table = hash_table;
|
||||
|
||||
char debug_backlog[PROP_VALUE_MAX];
|
||||
if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
|
||||
g_malloc_debug_backlog = atoi(debug_backlog);
|
||||
info_log("%s: setting backlog length to %d\n", getprogname(), g_malloc_debug_backlog);
|
||||
}
|
||||
|
||||
backtrace_startup();
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void malloc_debug_finalize() {
|
||||
ReportMemoryLeaks();
|
||||
extern "C" void malloc_debug_finalize(int malloc_debug_level) {
|
||||
// We only track leaks at level 10.
|
||||
if (malloc_debug_level == 10) {
|
||||
ReportMemoryLeaks();
|
||||
}
|
||||
backtrace_shutdown();
|
||||
}
|
||||
|
|
|
@ -48,13 +48,48 @@
|
|||
|
||||
#include "private/ScopedPthreadMutexLocker.h"
|
||||
|
||||
/*
|
||||
* In a VM process, this is set to 1 after fork()ing out of zygote.
|
||||
*/
|
||||
// In a VM process, this is set to 1 after fork()ing out of zygote.
|
||||
int gMallocLeakZygoteChild = 0;
|
||||
|
||||
__LIBC_HIDDEN__ pthread_mutex_t g_allocations_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
__LIBC_HIDDEN__ HashTable g_hash_table;
|
||||
static HashTable g_hash_table;
|
||||
|
||||
// Support for malloc debugging.
|
||||
// Table for dispatching malloc calls, initialized with default dispatchers.
|
||||
static const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = {
|
||||
Malloc(malloc), Malloc(free), Malloc(calloc), Malloc(realloc), Malloc(memalign), Malloc(malloc_usable_size),
|
||||
};
|
||||
|
||||
// Selector of dispatch table to use for dispatching malloc calls.
|
||||
static const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
|
||||
|
||||
// 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.
|
||||
// Note that emulator's memory allocation instrumentation is not controlled by
|
||||
// libc.debug.malloc value, but rather by emulator, started with -memcheck
|
||||
// option. Note also, that if emulator has started with -memcheck option,
|
||||
// emulator's instrumented memory allocation will take over value saved in
|
||||
// libc.debug.malloc. In other words, if emulator has started with -memcheck
|
||||
// option, libc.debug.malloc value is ignored.
|
||||
// Actual functionality for debug levels 1-10 is implemented in
|
||||
// libc_malloc_debug_leak.so, while functionality for emulator's instrumented
|
||||
// allocations is implemented in libc_malloc_debug_qemu.so and can be run inside
|
||||
// the emulator only.
|
||||
#if !defined(LIBC_STATIC)
|
||||
static void* libc_malloc_impl_handle = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
// The value of libc.debug.malloc.
|
||||
#if !defined(LIBC_STATIC)
|
||||
static int g_malloc_debug_level = 0;
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
// output functions
|
||||
|
@ -123,7 +158,7 @@ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
|
|||
}
|
||||
*totalMemory = 0;
|
||||
|
||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
||||
ScopedPthreadMutexLocker locker(&g_hash_table.lock);
|
||||
|
||||
if (g_hash_table.count == 0) {
|
||||
*info = NULL;
|
||||
|
@ -204,17 +239,6 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
|
|||
return Malloc(posix_memalign)(memptr, alignment, size);
|
||||
}
|
||||
|
||||
// Support for malloc debugging.
|
||||
// Table for dispatching malloc calls, initialized with default dispatchers.
|
||||
extern const MallocDebug __libc_malloc_default_dispatch;
|
||||
const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) =
|
||||
{
|
||||
Malloc(malloc), Malloc(free), Malloc(calloc), Malloc(realloc), Malloc(memalign), Malloc(malloc_usable_size),
|
||||
};
|
||||
|
||||
/* Selector of dispatch table to use for dispatching malloc calls. */
|
||||
const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
|
||||
|
||||
extern "C" void* malloc(size_t bytes) {
|
||||
return __libc_malloc_dispatch->malloc(bytes);
|
||||
}
|
||||
|
@ -248,59 +272,19 @@ extern "C" size_t malloc_usable_size(const void* mem) {
|
|||
#include <stdio.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
/* Table for dispatching malloc calls, depending on environment. */
|
||||
static MallocDebug g_malloc_dispatch_table __attribute__((aligned(32))) = {
|
||||
Malloc(malloc), Malloc(free), Malloc(calloc), Malloc(realloc), Malloc(memalign), Malloc(malloc_usable_size)
|
||||
};
|
||||
|
||||
extern const 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.
|
||||
* Note that emulator's memory allocation instrumentation is not controlled by
|
||||
* libc.debug.malloc value, but rather by emulator, started with -memcheck
|
||||
* option. Note also, that if emulator has started with -memcheck option,
|
||||
* emulator's instrumented memory allocation will take over value saved in
|
||||
* libc.debug.malloc. In other words, if emulator has started with -memcheck
|
||||
* option, libc.debug.malloc value is ignored.
|
||||
* Actual functionality for debug levels 1-10 is implemented in
|
||||
* libc_malloc_debug_leak.so, while functionality for emultor's instrumented
|
||||
* allocations is implemented in libc_malloc_debug_qemu.so and can be run inside
|
||||
* the emulator only.
|
||||
*/
|
||||
static void* libc_malloc_impl_handle = NULL;
|
||||
|
||||
/* This variable is set to the value of property libc.debug.malloc.backlog,
|
||||
* when the value of libc.debug.malloc = 10. It determines the size of the
|
||||
* backlog we use to detect multiple frees. If the property is not set, the
|
||||
* backlog length defaults to BACKLOG_DEFAULT_LEN.
|
||||
*/
|
||||
__LIBC_HIDDEN__ unsigned int g_malloc_debug_backlog;
|
||||
#define BACKLOG_DEFAULT_LEN 100
|
||||
|
||||
/* The value of libc.debug.malloc. */
|
||||
__LIBC_HIDDEN__ int g_malloc_debug_level;
|
||||
|
||||
template<typename FunctionType>
|
||||
static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
|
||||
char symbol[128];
|
||||
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
|
||||
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
|
||||
if (*func == NULL) {
|
||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
||||
error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
|
||||
}
|
||||
}
|
||||
|
||||
static void InitMalloc(void* malloc_impl_handler, MallocDebug* table, const char* prefix) {
|
||||
__libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
|
||||
__progname, g_malloc_debug_level, prefix);
|
||||
getprogname(), g_malloc_debug_level, prefix);
|
||||
|
||||
InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
|
||||
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
|
||||
|
@ -349,14 +333,14 @@ static void malloc_init_impl() {
|
|||
* then exit.
|
||||
*/
|
||||
if (__system_property_get("libc.debug.malloc.program", debug_program)) {
|
||||
if (!strstr(__progname, debug_program)) {
|
||||
if (!strstr(getprogname(), debug_program)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// mksh is way too leaky. http://b/7291287.
|
||||
if (g_malloc_debug_level >= 10) {
|
||||
if (strcmp(__progname, "sh") == 0 || strcmp(__progname, "/system/bin/sh") == 0) {
|
||||
if (strcmp(getprogname(), "sh") == 0 || strcmp(getprogname(), "/system/bin/sh") == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -365,35 +349,26 @@ static void malloc_init_impl() {
|
|||
switch (g_malloc_debug_level) {
|
||||
case 1:
|
||||
case 5:
|
||||
case 10: {
|
||||
char debug_backlog[PROP_VALUE_MAX];
|
||||
if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
|
||||
g_malloc_debug_backlog = atoi(debug_backlog);
|
||||
info_log("%s: setting backlog length to %d\n", __progname, g_malloc_debug_backlog);
|
||||
}
|
||||
if (g_malloc_debug_backlog == 0) {
|
||||
g_malloc_debug_backlog = BACKLOG_DEFAULT_LEN;
|
||||
}
|
||||
case 10:
|
||||
so_name = "libc_malloc_debug_leak.so";
|
||||
break;
|
||||
}
|
||||
case 20:
|
||||
// Quick check: debug level 20 can only be handled in emulator.
|
||||
if (!qemu_running) {
|
||||
error_log("%s: Debug level %d can only be set in emulator\n",
|
||||
__progname, g_malloc_debug_level);
|
||||
getprogname(), g_malloc_debug_level);
|
||||
return;
|
||||
}
|
||||
// Make sure that memory checking has been enabled in emulator.
|
||||
if (!memcheck_enabled) {
|
||||
error_log("%s: Memory checking is not enabled in the emulator\n",
|
||||
__progname);
|
||||
getprogname());
|
||||
return;
|
||||
}
|
||||
so_name = "libc_malloc_debug_qemu.so";
|
||||
break;
|
||||
default:
|
||||
error_log("%s: Debug level %d is unknown\n", __progname, g_malloc_debug_level);
|
||||
error_log("%s: Debug level %d is unknown\n", getprogname(), g_malloc_debug_level);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -401,7 +376,7 @@ static void malloc_init_impl() {
|
|||
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
|
||||
if (malloc_impl_handle == NULL) {
|
||||
error_log("%s: Missing module %s required for malloc debug level %d: %s",
|
||||
__progname, so_name, g_malloc_debug_level, dlerror());
|
||||
getprogname(), so_name, g_malloc_debug_level, dlerror());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -410,11 +385,11 @@ static void malloc_init_impl() {
|
|||
"malloc_debug_initialize"));
|
||||
if (malloc_debug_initialize == NULL) {
|
||||
error_log("%s: Initialization routine is not found in %s\n",
|
||||
__progname, so_name);
|
||||
getprogname(), so_name);
|
||||
dlclose(malloc_impl_handle);
|
||||
return;
|
||||
}
|
||||
if (malloc_debug_initialize() == -1) {
|
||||
if (malloc_debug_initialize(&g_hash_table) == -1) {
|
||||
dlclose(malloc_impl_handle);
|
||||
return;
|
||||
}
|
||||
|
@ -427,7 +402,7 @@ static void malloc_init_impl() {
|
|||
"memcheck_initialize"));
|
||||
if (memcheck_initialize == NULL) {
|
||||
error_log("%s: memcheck_initialize routine is not found in %s\n",
|
||||
__progname, so_name);
|
||||
getprogname(), so_name);
|
||||
dlclose(malloc_impl_handle);
|
||||
return;
|
||||
}
|
||||
|
@ -438,44 +413,52 @@ static void malloc_init_impl() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize malloc dispatch table with appropriate routines.
|
||||
static MallocDebug malloc_dispatch_table __attribute__((aligned(32))) = {
|
||||
Malloc(malloc),
|
||||
Malloc(free),
|
||||
Malloc(calloc),
|
||||
Malloc(realloc),
|
||||
Malloc(memalign),
|
||||
Malloc(malloc_usable_size)
|
||||
};
|
||||
|
||||
switch (g_malloc_debug_level) {
|
||||
case 1:
|
||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "leak");
|
||||
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "leak");
|
||||
break;
|
||||
case 5:
|
||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "fill");
|
||||
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "fill");
|
||||
break;
|
||||
case 10:
|
||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "chk");
|
||||
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "chk");
|
||||
break;
|
||||
case 20:
|
||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "qemu_instrumented");
|
||||
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "qemu_instrumented");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure dispatch table is initialized
|
||||
if ((g_malloc_dispatch_table.malloc == NULL) ||
|
||||
(g_malloc_dispatch_table.free == NULL) ||
|
||||
(g_malloc_dispatch_table.calloc == NULL) ||
|
||||
(g_malloc_dispatch_table.realloc == NULL) ||
|
||||
(g_malloc_dispatch_table.memalign == NULL) ||
|
||||
(g_malloc_dispatch_table.malloc_usable_size == NULL)) {
|
||||
if ((malloc_dispatch_table.malloc == NULL) ||
|
||||
(malloc_dispatch_table.free == NULL) ||
|
||||
(malloc_dispatch_table.calloc == NULL) ||
|
||||
(malloc_dispatch_table.realloc == NULL) ||
|
||||
(malloc_dispatch_table.memalign == NULL) ||
|
||||
(malloc_dispatch_table.malloc_usable_size == NULL)) {
|
||||
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
|
||||
__progname, g_malloc_debug_level);
|
||||
getprogname(), g_malloc_debug_level);
|
||||
dlclose(malloc_impl_handle);
|
||||
} else {
|
||||
__libc_malloc_dispatch = &g_malloc_dispatch_table;
|
||||
__libc_malloc_dispatch = &malloc_dispatch_table;
|
||||
libc_malloc_impl_handle = malloc_impl_handle;
|
||||
}
|
||||
}
|
||||
|
||||
static void malloc_fini_impl() {
|
||||
// Our BSD stdio implementation doesn't close the standard streams, it only flushes them.
|
||||
// And it doesn't do that until its atexit handler (_cleanup) is run, and we run first!
|
||||
// And it doesn't do that until its atexit handler is run, and we run first!
|
||||
// It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually
|
||||
// clean up the standard streams ourselves.
|
||||
fclose(stdin);
|
||||
|
@ -487,14 +470,11 @@ static void malloc_fini_impl() {
|
|||
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle,
|
||||
"malloc_debug_finalize"));
|
||||
if (malloc_debug_finalize != NULL) {
|
||||
malloc_debug_finalize();
|
||||
malloc_debug_finalize(g_malloc_debug_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
|
||||
static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
||||
|
||||
#endif // !LIBC_STATIC
|
||||
|
||||
/* Initializes memory allocation framework.
|
||||
|
@ -502,21 +482,19 @@ static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
|||
* in libc_init_static.c and libc_init_dynamic.c files.
|
||||
*/
|
||||
extern "C" __LIBC_HIDDEN__ void malloc_debug_init() {
|
||||
/* We need to initialize malloc iff we implement 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.");
|
||||
}
|
||||
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
|
||||
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
|
||||
error_log("Unable to initialize malloc_debug component.");
|
||||
}
|
||||
#endif // USE_DL_PREFIX && !LIBC_STATIC
|
||||
}
|
||||
|
||||
extern "C" __LIBC_HIDDEN__ void malloc_debug_fini() {
|
||||
/* We need to finalize malloc iff we implement 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_fini_once_ctl, malloc_fini_impl)) {
|
||||
error_log("Unable to finalize malloc_debug component.");
|
||||
}
|
||||
static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
||||
if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
|
||||
error_log("Unable to finalize malloc_debug component.");
|
||||
}
|
||||
#endif // USE_DL_PREFIX && !LIBC_STATIC
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#ifndef MALLOC_DEBUG_COMMON_H
|
||||
#define MALLOC_DEBUG_COMMON_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "private/libc_logging.h"
|
||||
|
@ -43,8 +45,6 @@
|
|||
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
|
||||
#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
|
||||
|
||||
#define MAX_SIZE_T (~(size_t)0)
|
||||
|
||||
// This must match the alignment used by the malloc implementation.
|
||||
#ifndef MALLOC_ALIGNMENT
|
||||
#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
|
||||
|
@ -77,6 +77,7 @@ struct HashEntry {
|
|||
};
|
||||
|
||||
struct HashTable {
|
||||
pthread_mutex_t lock;
|
||||
size_t count;
|
||||
HashEntry* slots[HASHTABLE_SIZE];
|
||||
};
|
||||
|
@ -97,20 +98,8 @@ struct MallocDebug {
|
|||
MallocDebugMallocUsableSize malloc_usable_size;
|
||||
};
|
||||
|
||||
/* Malloc debugging initialization and finalization routines.
|
||||
*
|
||||
* These routines must be implemented in .so modules that implement malloc
|
||||
* debugging. The are is called once per process from malloc_init_impl and
|
||||
* malloc_fini_impl respectively.
|
||||
*
|
||||
* They are implemented in bionic/libc/bionic/malloc_debug_common.c when malloc
|
||||
* debugging gets initialized for the process.
|
||||
*
|
||||
* MallocDebugInit returns:
|
||||
* 0 on success, -1 on failure.
|
||||
*/
|
||||
typedef int (*MallocDebugInit)();
|
||||
typedef void (*MallocDebugFini)();
|
||||
typedef bool (*MallocDebugInit)(HashTable*);
|
||||
typedef void (*MallocDebugFini)(int);
|
||||
|
||||
// =============================================================================
|
||||
// log functions
|
||||
|
|
|
@ -58,10 +58,8 @@
|
|||
#error MALLOC_LEAK_CHECK is not defined.
|
||||
#endif // !MALLOC_LEAK_CHECK
|
||||
|
||||
// Global variables defined in malloc_debug_common.c
|
||||
extern int gMallocLeakZygoteChild;
|
||||
extern pthread_mutex_t g_allocations_mutex;
|
||||
extern HashTable g_hash_table;
|
||||
extern HashTable* g_hash_table;
|
||||
|
||||
// =============================================================================
|
||||
// stack trace functions
|
||||
|
@ -137,7 +135,7 @@ static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size
|
|||
size |= SIZE_FLAG_ZYGOTE_CHILD;
|
||||
}
|
||||
|
||||
HashEntry* entry = find_entry(&g_hash_table, slot, backtrace, numEntries, size);
|
||||
HashEntry* entry = find_entry(g_hash_table, slot, backtrace, numEntries, size);
|
||||
|
||||
if (entry != NULL) {
|
||||
entry->allocations++;
|
||||
|
@ -150,58 +148,54 @@ static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size
|
|||
entry->allocations = 1;
|
||||
entry->slot = slot;
|
||||
entry->prev = NULL;
|
||||
entry->next = g_hash_table.slots[slot];
|
||||
entry->next = g_hash_table->slots[slot];
|
||||
entry->numEntries = numEntries;
|
||||
entry->size = size;
|
||||
|
||||
memcpy(entry->backtrace, backtrace, numEntries * sizeof(uintptr_t));
|
||||
|
||||
g_hash_table.slots[slot] = entry;
|
||||
g_hash_table->slots[slot] = entry;
|
||||
|
||||
if (entry->next != NULL) {
|
||||
entry->next->prev = entry;
|
||||
}
|
||||
|
||||
// we just added an entry, increase the size of the hashtable
|
||||
g_hash_table.count++;
|
||||
g_hash_table->count++;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int is_valid_entry(HashEntry* entry) {
|
||||
if (entry != NULL) {
|
||||
int i;
|
||||
for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
|
||||
HashEntry* e1 = g_hash_table.slots[i];
|
||||
|
||||
while (e1 != NULL) {
|
||||
if (e1 == entry) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
e1 = e1->next;
|
||||
}
|
||||
if (entry != NULL) {
|
||||
for (size_t i = 0; i < HASHTABLE_SIZE; ++i) {
|
||||
HashEntry* e1 = g_hash_table->slots[i];
|
||||
while (e1 != NULL) {
|
||||
if (e1 == entry) {
|
||||
return 1;
|
||||
}
|
||||
e1 = e1->next;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_entry(HashEntry* entry) {
|
||||
HashEntry* prev = entry->prev;
|
||||
HashEntry* next = entry->next;
|
||||
HashEntry* prev = entry->prev;
|
||||
HashEntry* next = entry->next;
|
||||
|
||||
if (prev != NULL) entry->prev->next = next;
|
||||
if (next != NULL) entry->next->prev = prev;
|
||||
if (prev != NULL) entry->prev->next = next;
|
||||
if (next != NULL) entry->next->prev = prev;
|
||||
|
||||
if (prev == NULL) {
|
||||
// we are the head of the list. set the head to be next
|
||||
g_hash_table.slots[entry->slot] = entry->next;
|
||||
}
|
||||
if (prev == NULL) {
|
||||
// we are the head of the list. set the head to be next
|
||||
g_hash_table->slots[entry->slot] = entry->next;
|
||||
}
|
||||
|
||||
// we just removed and entry, decrease the size of the hashtable
|
||||
g_hash_table.count--;
|
||||
// we just removed and entry, decrease the size of the hashtable
|
||||
g_hash_table->count--;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
@ -276,7 +270,7 @@ extern "C" void* leak_malloc(size_t bytes) {
|
|||
|
||||
void* base = Malloc(malloc)(size);
|
||||
if (base != NULL) {
|
||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
||||
ScopedPthreadMutexLocker locker(&g_hash_table->lock);
|
||||
|
||||
uintptr_t backtrace[BACKTRACE_SIZE];
|
||||
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
|
||||
|
@ -294,43 +288,45 @@ extern "C" void* leak_malloc(size_t bytes) {
|
|||
}
|
||||
|
||||
extern "C" void leak_free(void* mem) {
|
||||
if (mem != NULL) {
|
||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
||||
if (mem == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check the guard to make sure it is valid
|
||||
AllocationEntry* header = to_header(mem);
|
||||
ScopedPthreadMutexLocker locker(&g_hash_table->lock);
|
||||
|
||||
if (header->guard != GUARD) {
|
||||
// could be a memaligned block
|
||||
if (header->guard == MEMALIGN_GUARD) {
|
||||
// For memaligned blocks, header->entry points to the memory
|
||||
// allocated through leak_malloc.
|
||||
header = to_header(header->entry);
|
||||
}
|
||||
}
|
||||
// check the guard to make sure it is valid
|
||||
AllocationEntry* header = to_header(mem);
|
||||
|
||||
if (header->guard == GUARD || is_valid_entry(header->entry)) {
|
||||
// decrement the allocations
|
||||
HashEntry* entry = header->entry;
|
||||
entry->allocations--;
|
||||
if (entry->allocations <= 0) {
|
||||
remove_entry(entry);
|
||||
Malloc(free)(entry);
|
||||
}
|
||||
|
||||
// now free the memory!
|
||||
Malloc(free)(header);
|
||||
} else {
|
||||
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
|
||||
header->guard, header->entry);
|
||||
}
|
||||
if (header->guard != GUARD) {
|
||||
// could be a memaligned block
|
||||
if (header->guard == MEMALIGN_GUARD) {
|
||||
// For memaligned blocks, header->entry points to the memory
|
||||
// allocated through leak_malloc.
|
||||
header = to_header(header->entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (header->guard == GUARD || is_valid_entry(header->entry)) {
|
||||
// decrement the allocations
|
||||
HashEntry* entry = header->entry;
|
||||
entry->allocations--;
|
||||
if (entry->allocations <= 0) {
|
||||
remove_entry(entry);
|
||||
Malloc(free)(entry);
|
||||
}
|
||||
|
||||
// now free the memory!
|
||||
Malloc(free)(header);
|
||||
} else {
|
||||
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
|
||||
header->guard, header->entry);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
|
||||
/* Fail on overflow - just to be safe even though this code runs only
|
||||
* within the debugging C library, not the production one */
|
||||
if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
|
||||
// Fail on overflow - just to be safe even though this code runs only
|
||||
// within the debugging C library, not the production one.
|
||||
if (n_elements && SIZE_MAX / n_elements < elem_size) {
|
||||
return NULL;
|
||||
}
|
||||
size_t size = n_elements * elem_size;
|
||||
|
|
|
@ -589,7 +589,7 @@ extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem);
|
|||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
extern "C" int malloc_debug_initialize() {
|
||||
extern "C" bool malloc_debug_initialize(HashTable*) {
|
||||
/* We will be using emulator's magic page to report memory allocation
|
||||
* activities. In essence, what magic page does, it translates writes to
|
||||
* the memory mapped spaces into writes to an I/O port that emulator
|
||||
|
@ -598,7 +598,7 @@ extern "C" int malloc_debug_initialize() {
|
|||
int fd = open("/dev/qemu_trace", O_RDWR);
|
||||
if (fd < 0) {
|
||||
error_log("Unable to open /dev/qemu_trace");
|
||||
return -1;
|
||||
return false;
|
||||
} else {
|
||||
qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
|
@ -606,14 +606,13 @@ extern "C" int malloc_debug_initialize() {
|
|||
if (qtrace == MAP_FAILED) {
|
||||
qtrace = NULL;
|
||||
error_log("Unable to mmap /dev/qemu_trace");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache pid of the process this library has been initialized for. */
|
||||
malloc_pid = getpid();
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Completes malloc debugging instrumentation for the emulator.
|
||||
|
@ -759,9 +758,9 @@ extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
|
|||
return qemu_instrumented_malloc(0);
|
||||
}
|
||||
|
||||
/* Fail on overflow - just to be safe even though this code runs only
|
||||
* within the debugging C library, not the production one */
|
||||
if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
|
||||
// Fail on overflow - just to be safe even though this code runs only
|
||||
// within the debugging C library, not the production one.
|
||||
if (n_elements && SIZE_MAX / n_elements < elem_size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue