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
@ -51,10 +51,6 @@
|
|||||||
#include "malloc_debug_common.h"
|
#include "malloc_debug_common.h"
|
||||||
#include "private/ScopedPthreadMutexLocker.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 MAX_BACKTRACE_DEPTH 16
|
||||||
#define ALLOCATION_TAG 0x1ee7d00d
|
#define ALLOCATION_TAG 0x1ee7d00d
|
||||||
#define BACKLOG_TAG 0xbabecafe
|
#define BACKLOG_TAG 0xbabecafe
|
||||||
@ -120,6 +116,12 @@ static hdr_t* backlog_tail;
|
|||||||
static hdr_t* backlog_head;
|
static hdr_t* backlog_head;
|
||||||
static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER;
|
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) {
|
static inline void init_front_guard(hdr_t* hdr) {
|
||||||
memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN);
|
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() {
|
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
|
// Use /proc/self/exe link to obtain the program name for logging
|
||||||
// purposes. If it's not available, we set it to "<unknown>".
|
// purposes. If it's not available, we set it to "<unknown>".
|
||||||
char exe[PATH_MAX];
|
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) {
|
||||||
backtrace_startup();
|
g_hash_table = hash_table;
|
||||||
return 0;
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void malloc_debug_finalize() {
|
backtrace_startup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void malloc_debug_finalize(int malloc_debug_level) {
|
||||||
|
// We only track leaks at level 10.
|
||||||
|
if (malloc_debug_level == 10) {
|
||||||
ReportMemoryLeaks();
|
ReportMemoryLeaks();
|
||||||
|
}
|
||||||
backtrace_shutdown();
|
backtrace_shutdown();
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,48 @@
|
|||||||
|
|
||||||
#include "private/ScopedPthreadMutexLocker.h"
|
#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;
|
int gMallocLeakZygoteChild = 0;
|
||||||
|
|
||||||
__LIBC_HIDDEN__ pthread_mutex_t g_allocations_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static HashTable g_hash_table;
|
||||||
__LIBC_HIDDEN__ 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
|
// output functions
|
||||||
@ -123,7 +158,7 @@ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
|
|||||||
}
|
}
|
||||||
*totalMemory = 0;
|
*totalMemory = 0;
|
||||||
|
|
||||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
ScopedPthreadMutexLocker locker(&g_hash_table.lock);
|
||||||
|
|
||||||
if (g_hash_table.count == 0) {
|
if (g_hash_table.count == 0) {
|
||||||
*info = NULL;
|
*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);
|
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) {
|
extern "C" void* malloc(size_t bytes) {
|
||||||
return __libc_malloc_dispatch->malloc(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 <stdio.h>
|
||||||
#include "private/libc_logging.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>
|
template<typename FunctionType>
|
||||||
static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
|
static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
|
||||||
char symbol[128];
|
char symbol[128];
|
||||||
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
|
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
|
||||||
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
|
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
|
||||||
if (*func == NULL) {
|
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) {
|
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",
|
__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<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
|
||||||
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
|
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
|
||||||
@ -349,14 +333,14 @@ static void malloc_init_impl() {
|
|||||||
* then exit.
|
* then exit.
|
||||||
*/
|
*/
|
||||||
if (__system_property_get("libc.debug.malloc.program", debug_program)) {
|
if (__system_property_get("libc.debug.malloc.program", debug_program)) {
|
||||||
if (!strstr(__progname, debug_program)) {
|
if (!strstr(getprogname(), debug_program)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mksh is way too leaky. http://b/7291287.
|
// mksh is way too leaky. http://b/7291287.
|
||||||
if (g_malloc_debug_level >= 10) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,35 +349,26 @@ static void malloc_init_impl() {
|
|||||||
switch (g_malloc_debug_level) {
|
switch (g_malloc_debug_level) {
|
||||||
case 1:
|
case 1:
|
||||||
case 5:
|
case 5:
|
||||||
case 10: {
|
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;
|
|
||||||
}
|
|
||||||
so_name = "libc_malloc_debug_leak.so";
|
so_name = "libc_malloc_debug_leak.so";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 20:
|
case 20:
|
||||||
// Quick check: debug level 20 can only be handled in emulator.
|
// Quick check: debug level 20 can only be handled in emulator.
|
||||||
if (!qemu_running) {
|
if (!qemu_running) {
|
||||||
error_log("%s: Debug level %d can only be set in emulator\n",
|
error_log("%s: Debug level %d can only be set in emulator\n",
|
||||||
__progname, g_malloc_debug_level);
|
getprogname(), g_malloc_debug_level);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Make sure that memory checking has been enabled in emulator.
|
// Make sure that memory checking has been enabled in emulator.
|
||||||
if (!memcheck_enabled) {
|
if (!memcheck_enabled) {
|
||||||
error_log("%s: Memory checking is not enabled in the emulator\n",
|
error_log("%s: Memory checking is not enabled in the emulator\n",
|
||||||
__progname);
|
getprogname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
so_name = "libc_malloc_debug_qemu.so";
|
so_name = "libc_malloc_debug_qemu.so";
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +376,7 @@ static void malloc_init_impl() {
|
|||||||
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
|
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
|
||||||
if (malloc_impl_handle == NULL) {
|
if (malloc_impl_handle == NULL) {
|
||||||
error_log("%s: Missing module %s required for malloc debug level %d: %s",
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,11 +385,11 @@ static void malloc_init_impl() {
|
|||||||
"malloc_debug_initialize"));
|
"malloc_debug_initialize"));
|
||||||
if (malloc_debug_initialize == NULL) {
|
if (malloc_debug_initialize == NULL) {
|
||||||
error_log("%s: Initialization routine is not found in %s\n",
|
error_log("%s: Initialization routine is not found in %s\n",
|
||||||
__progname, so_name);
|
getprogname(), so_name);
|
||||||
dlclose(malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (malloc_debug_initialize() == -1) {
|
if (malloc_debug_initialize(&g_hash_table) == -1) {
|
||||||
dlclose(malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -427,7 +402,7 @@ static void malloc_init_impl() {
|
|||||||
"memcheck_initialize"));
|
"memcheck_initialize"));
|
||||||
if (memcheck_initialize == NULL) {
|
if (memcheck_initialize == NULL) {
|
||||||
error_log("%s: memcheck_initialize routine is not found in %s\n",
|
error_log("%s: memcheck_initialize routine is not found in %s\n",
|
||||||
__progname, so_name);
|
getprogname(), so_name);
|
||||||
dlclose(malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -438,44 +413,52 @@ static void malloc_init_impl() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize malloc dispatch table with appropriate routines.
|
// 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) {
|
switch (g_malloc_debug_level) {
|
||||||
case 1:
|
case 1:
|
||||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "leak");
|
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "leak");
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "fill");
|
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "fill");
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "chk");
|
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "chk");
|
||||||
break;
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
InitMalloc(malloc_impl_handle, &g_malloc_dispatch_table, "qemu_instrumented");
|
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "qemu_instrumented");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure dispatch table is initialized
|
// Make sure dispatch table is initialized
|
||||||
if ((g_malloc_dispatch_table.malloc == NULL) ||
|
if ((malloc_dispatch_table.malloc == NULL) ||
|
||||||
(g_malloc_dispatch_table.free == NULL) ||
|
(malloc_dispatch_table.free == NULL) ||
|
||||||
(g_malloc_dispatch_table.calloc == NULL) ||
|
(malloc_dispatch_table.calloc == NULL) ||
|
||||||
(g_malloc_dispatch_table.realloc == NULL) ||
|
(malloc_dispatch_table.realloc == NULL) ||
|
||||||
(g_malloc_dispatch_table.memalign == NULL) ||
|
(malloc_dispatch_table.memalign == NULL) ||
|
||||||
(g_malloc_dispatch_table.malloc_usable_size == NULL)) {
|
(malloc_dispatch_table.malloc_usable_size == NULL)) {
|
||||||
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
|
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);
|
dlclose(malloc_impl_handle);
|
||||||
} else {
|
} else {
|
||||||
__libc_malloc_dispatch = &g_malloc_dispatch_table;
|
__libc_malloc_dispatch = &malloc_dispatch_table;
|
||||||
libc_malloc_impl_handle = malloc_impl_handle;
|
libc_malloc_impl_handle = malloc_impl_handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void malloc_fini_impl() {
|
static void malloc_fini_impl() {
|
||||||
// Our BSD stdio implementation doesn't close the standard streams, it only flushes them.
|
// 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
|
// It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually
|
||||||
// clean up the standard streams ourselves.
|
// clean up the standard streams ourselves.
|
||||||
fclose(stdin);
|
fclose(stdin);
|
||||||
@ -487,14 +470,11 @@ static void malloc_fini_impl() {
|
|||||||
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle,
|
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle,
|
||||||
"malloc_debug_finalize"));
|
"malloc_debug_finalize"));
|
||||||
if (malloc_debug_finalize != NULL) {
|
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
|
#endif // !LIBC_STATIC
|
||||||
|
|
||||||
/* Initializes memory allocation framework.
|
/* Initializes memory allocation framework.
|
||||||
@ -502,9 +482,8 @@ static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
|||||||
* in libc_init_static.c and libc_init_dynamic.c files.
|
* in libc_init_static.c and libc_init_dynamic.c files.
|
||||||
*/
|
*/
|
||||||
extern "C" __LIBC_HIDDEN__ void malloc_debug_init() {
|
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 defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
|
||||||
|
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
|
||||||
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
|
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
|
||||||
error_log("Unable to initialize malloc_debug component.");
|
error_log("Unable to initialize malloc_debug component.");
|
||||||
}
|
}
|
||||||
@ -512,9 +491,8 @@ extern "C" __LIBC_HIDDEN__ void malloc_debug_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" __LIBC_HIDDEN__ void malloc_debug_fini() {
|
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 defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
|
||||||
|
static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
||||||
if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
|
if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
|
||||||
error_log("Unable to finalize malloc_debug component.");
|
error_log("Unable to finalize malloc_debug component.");
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#ifndef MALLOC_DEBUG_COMMON_H
|
#ifndef MALLOC_DEBUG_COMMON_H
|
||||||
#define MALLOC_DEBUG_COMMON_H
|
#define MALLOC_DEBUG_COMMON_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "private/libc_logging.h"
|
#include "private/libc_logging.h"
|
||||||
@ -43,8 +45,6 @@
|
|||||||
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
|
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
|
||||||
#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
|
#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.
|
// This must match the alignment used by the malloc implementation.
|
||||||
#ifndef MALLOC_ALIGNMENT
|
#ifndef MALLOC_ALIGNMENT
|
||||||
#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
|
#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
|
||||||
@ -77,6 +77,7 @@ struct HashEntry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct HashTable {
|
struct HashTable {
|
||||||
|
pthread_mutex_t lock;
|
||||||
size_t count;
|
size_t count;
|
||||||
HashEntry* slots[HASHTABLE_SIZE];
|
HashEntry* slots[HASHTABLE_SIZE];
|
||||||
};
|
};
|
||||||
@ -97,20 +98,8 @@ struct MallocDebug {
|
|||||||
MallocDebugMallocUsableSize malloc_usable_size;
|
MallocDebugMallocUsableSize malloc_usable_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Malloc debugging initialization and finalization routines.
|
typedef bool (*MallocDebugInit)(HashTable*);
|
||||||
*
|
typedef void (*MallocDebugFini)(int);
|
||||||
* 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)();
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// log functions
|
// log functions
|
||||||
|
@ -58,10 +58,8 @@
|
|||||||
#error MALLOC_LEAK_CHECK is not defined.
|
#error MALLOC_LEAK_CHECK is not defined.
|
||||||
#endif // !MALLOC_LEAK_CHECK
|
#endif // !MALLOC_LEAK_CHECK
|
||||||
|
|
||||||
// Global variables defined in malloc_debug_common.c
|
|
||||||
extern int gMallocLeakZygoteChild;
|
extern int gMallocLeakZygoteChild;
|
||||||
extern pthread_mutex_t g_allocations_mutex;
|
extern HashTable* g_hash_table;
|
||||||
extern HashTable g_hash_table;
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// stack trace functions
|
// stack trace functions
|
||||||
@ -137,7 +135,7 @@ static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size
|
|||||||
size |= SIZE_FLAG_ZYGOTE_CHILD;
|
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) {
|
if (entry != NULL) {
|
||||||
entry->allocations++;
|
entry->allocations++;
|
||||||
@ -150,20 +148,20 @@ static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size
|
|||||||
entry->allocations = 1;
|
entry->allocations = 1;
|
||||||
entry->slot = slot;
|
entry->slot = slot;
|
||||||
entry->prev = NULL;
|
entry->prev = NULL;
|
||||||
entry->next = g_hash_table.slots[slot];
|
entry->next = g_hash_table->slots[slot];
|
||||||
entry->numEntries = numEntries;
|
entry->numEntries = numEntries;
|
||||||
entry->size = size;
|
entry->size = size;
|
||||||
|
|
||||||
memcpy(entry->backtrace, backtrace, numEntries * sizeof(uintptr_t));
|
memcpy(entry->backtrace, backtrace, numEntries * sizeof(uintptr_t));
|
||||||
|
|
||||||
g_hash_table.slots[slot] = entry;
|
g_hash_table->slots[slot] = entry;
|
||||||
|
|
||||||
if (entry->next != NULL) {
|
if (entry->next != NULL) {
|
||||||
entry->next->prev = entry;
|
entry->next->prev = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we just added an entry, increase the size of the hashtable
|
// we just added an entry, increase the size of the hashtable
|
||||||
g_hash_table.count++;
|
g_hash_table->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
@ -171,20 +169,16 @@ static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size
|
|||||||
|
|
||||||
static int is_valid_entry(HashEntry* entry) {
|
static int is_valid_entry(HashEntry* entry) {
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
int i;
|
for (size_t i = 0; i < HASHTABLE_SIZE; ++i) {
|
||||||
for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
|
HashEntry* e1 = g_hash_table->slots[i];
|
||||||
HashEntry* e1 = g_hash_table.slots[i];
|
|
||||||
|
|
||||||
while (e1 != NULL) {
|
while (e1 != NULL) {
|
||||||
if (e1 == entry) {
|
if (e1 == entry) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
e1 = e1->next;
|
e1 = e1->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,11 +191,11 @@ static void remove_entry(HashEntry* entry) {
|
|||||||
|
|
||||||
if (prev == NULL) {
|
if (prev == NULL) {
|
||||||
// we are the head of the list. set the head to be next
|
// we are the head of the list. set the head to be next
|
||||||
g_hash_table.slots[entry->slot] = entry->next;
|
g_hash_table->slots[entry->slot] = entry->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we just removed and entry, decrease the size of the hashtable
|
// we just removed and entry, decrease the size of the hashtable
|
||||||
g_hash_table.count--;
|
g_hash_table->count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@ -276,7 +270,7 @@ extern "C" void* leak_malloc(size_t bytes) {
|
|||||||
|
|
||||||
void* base = Malloc(malloc)(size);
|
void* base = Malloc(malloc)(size);
|
||||||
if (base != NULL) {
|
if (base != NULL) {
|
||||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
ScopedPthreadMutexLocker locker(&g_hash_table->lock);
|
||||||
|
|
||||||
uintptr_t backtrace[BACKTRACE_SIZE];
|
uintptr_t backtrace[BACKTRACE_SIZE];
|
||||||
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
|
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
|
||||||
@ -294,8 +288,11 @@ extern "C" void* leak_malloc(size_t bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void leak_free(void* mem) {
|
extern "C" void leak_free(void* mem) {
|
||||||
if (mem != NULL) {
|
if (mem == NULL) {
|
||||||
ScopedPthreadMutexLocker locker(&g_allocations_mutex);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedPthreadMutexLocker locker(&g_hash_table->lock);
|
||||||
|
|
||||||
// check the guard to make sure it is valid
|
// check the guard to make sure it is valid
|
||||||
AllocationEntry* header = to_header(mem);
|
AllocationEntry* header = to_header(mem);
|
||||||
@ -325,12 +322,11 @@ extern "C" void leak_free(void* mem) {
|
|||||||
header->guard, header->entry);
|
header->guard, header->entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
|
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
|
// Fail on overflow - just to be safe even though this code runs only
|
||||||
* within the debugging C library, not the production one */
|
// within the debugging C library, not the production one.
|
||||||
if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
|
if (n_elements && SIZE_MAX / n_elements < elem_size) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
size_t size = n_elements * elem_size;
|
size_t size = n_elements * elem_size;
|
||||||
|
@ -589,7 +589,7 @@ extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem);
|
|||||||
* Return:
|
* Return:
|
||||||
* 0 on success, or -1 on failure.
|
* 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
|
/* We will be using emulator's magic page to report memory allocation
|
||||||
* activities. In essence, what magic page does, it translates writes to
|
* activities. In essence, what magic page does, it translates writes to
|
||||||
* the memory mapped spaces into writes to an I/O port that emulator
|
* 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);
|
int fd = open("/dev/qemu_trace", O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_log("Unable to open /dev/qemu_trace");
|
error_log("Unable to open /dev/qemu_trace");
|
||||||
return -1;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -606,14 +606,13 @@ extern "C" int malloc_debug_initialize() {
|
|||||||
if (qtrace == MAP_FAILED) {
|
if (qtrace == MAP_FAILED) {
|
||||||
qtrace = NULL;
|
qtrace = NULL;
|
||||||
error_log("Unable to mmap /dev/qemu_trace");
|
error_log("Unable to mmap /dev/qemu_trace");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cache pid of the process this library has been initialized for. */
|
/* Cache pid of the process this library has been initialized for. */
|
||||||
malloc_pid = getpid();
|
malloc_pid = getpid();
|
||||||
|
return true;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Completes malloc debugging instrumentation for the emulator.
|
/* 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);
|
return qemu_instrumented_malloc(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fail on overflow - just to be safe even though this code runs only
|
// Fail on overflow - just to be safe even though this code runs only
|
||||||
* within the debugging C library, not the production one */
|
// within the debugging C library, not the production one.
|
||||||
if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
|
if (n_elements && SIZE_MAX / n_elements < elem_size) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user