$(libc_arch_dynamic_src_files) \
$(libc_static_common_src_files) \
bionic/malloc_debug_common.cpp \
- bionic/debug_mapinfo.cpp \
- bionic/debug_stacktrace.cpp \
bionic/libc_init_dynamic.cpp \
bionic/NetdClient.cpp \
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
LOCAL_CPPFLAGS := $(libc_common_cppflags)
-LOCAL_C_INCLUDES := $(libc_common_c_includes)
+# Make sure that unwind.h comes from libunwind.
+LOCAL_C_INCLUDES := \
+ external/libunwind/include \
+ $(libc_common_c_includes) \
LOCAL_SRC_FILES := \
bionic/debug_mapinfo.cpp \
LOCAL_SHARED_LIBRARIES := libc libdl
LOCAL_SYSTEM_SHARED_LIBRARIES :=
+LOCAL_WHOLE_STATIC_LIBRARIES := libunwindbacktrace
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
# Don't install on release build
* SUCH DAMAGE.
*/
+#include <ctype.h>
+#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-#include <sys/mman.h>
#include "debug_mapinfo.h"
+#include "malloc_debug_disable.h"
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
-// 012345678901234567890123456789012345678901234567890123456789
-// 0 1 2 3 4 5
-
+// Format of /proc/<PID>/maps:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
static mapinfo_t* parse_maps_line(char* line) {
- int len = strlen(line);
-
- if (len < 1) return 0;
- line[--len] = 0;
-
- if (len < 50) return 0;
- if (line[20] != 'x') return 0;
-
- mapinfo_t* mi = static_cast<mapinfo_t*>(
- mmap(NULL, sizeof(mapinfo_t) + (len - 47), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0));
- if (mi == MAP_FAILED) return 0;
+ uintptr_t start;
+ uintptr_t end;
+ int name_pos;
+ if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %*4s %*x %*x:%*x %*d%n", &start,
+ &end, &name_pos) < 2) {
+ return NULL;
+ }
- mi->start = strtoul(line, 0, 16);
- mi->end = strtoul(line + 9, 0, 16);
- mi->next = 0;
- strcpy(mi->name, line + 49);
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+ if (name_len && name[name_len - 1] == '\n') {
+ name_len -= 1;
+ }
+ mapinfo_t* mi = reinterpret_cast<mapinfo_t*>(calloc(1, sizeof(mapinfo_t) + name_len + 1));
+ if (mi) {
+ mi->start = start;
+ mi->end = end;
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len] = '\0';
+ }
return mi;
}
__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
+ ScopedDisableDebugCalls disable;
+
struct mapinfo_t* milist = NULL;
char data[1024]; // Used to read lines as well as to construct the filename.
snprintf(data, sizeof(data), "/proc/%d/maps", pid);
}
__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
+ ScopedDisableDebugCalls disable;
+
while (mi != NULL) {
mapinfo_t* del = mi;
mi = mi->next;
- munmap(del, sizeof(mapinfo_t) + strlen(del->name) + 2);
+ free(del);
}
}
struct mapinfo_t {
struct mapinfo_t* next;
- unsigned start;
- unsigned end;
+ uintptr_t start;
+ uintptr_t end;
char name[];
};
#include <sys/types.h>
#include "debug_mapinfo.h"
+#include "malloc_debug_disable.h"
#include "private/libc_logging.h"
#if defined(__LP64__)
static DemanglerFn g_demangler_fn = NULL;
__LIBC_HIDDEN__ void backtrace_startup() {
+ ScopedDisableDebugCalls disable;
+
g_map_info = mapinfo_create(getpid());
g_demangler = dlopen("libgccdemangle.so", RTLD_NOW);
if (g_demangler != NULL) {
}
__LIBC_HIDDEN__ void backtrace_shutdown() {
+ ScopedDisableDebugCalls disable;
+
mapinfo_destroy(g_map_info);
dlclose(g_demangler);
}
return _URC_NO_REASON;
}
-#ifdef __arm__
+#if defined(__arm__)
/*
* The instruction pointer is pointing at the instruction after the bl(x), and
* the _Unwind_Backtrace routine already masks the Thumb mode indicator (LSB
}
__LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) {
+ ScopedDisableDebugCalls disable;
+
stack_crawl_state_t state(frames, max_depth);
_Unwind_Backtrace(trace_function, &state);
return state.frame_count;
}
__LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) {
+ ScopedDisableDebugCalls disable;
+
uintptr_t self_bt[16];
if (frames == NULL) {
frame_count = get_backtrace(self_bt, 16);
symbol = info.dli_sname;
}
- uintptr_t rel_pc;
+ uintptr_t rel_pc = offset;
const mapinfo_t* mi = (g_map_info != NULL) ? mapinfo_find(g_map_info, frames[i], &rel_pc) : NULL;
const char* soname = (mi != NULL) ? mi->name : info.dli_fname;
if (soname == NULL) {
#include "debug_mapinfo.h"
#include "debug_stacktrace.h"
#include "malloc_debug_common.h"
+#include "malloc_debug_disable.h"
#include "private/bionic_macros.h"
#include "private/libc_logging.h"
#include "private/ScopedPthreadMutexLocker.h"
extern "C" void* chk_malloc(size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc(bytes);
+ }
size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
if (size < bytes) { // Overflow
}
extern "C" void* chk_memalign(size_t alignment, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->memalign(alignment, bytes);
+ }
+
if (alignment <= MALLOC_ALIGNMENT) {
return chk_malloc(bytes);
}
extern "C" void chk_free(void* ptr) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->free(ptr);
+ }
if (!ptr) /* ignore free(NULL) */
return;
extern "C" void* chk_realloc(void* ptr, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->realloc(ptr, bytes);
+ }
if (!ptr) {
return chk_malloc(bytes);
extern "C" void* chk_calloc(size_t nmemb, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->calloc(nmemb, bytes);
+ }
+
size_t total_bytes = nmemb * bytes;
size_t size = sizeof(hdr_t) + total_bytes + sizeof(ftr_t);
if (size < total_bytes || (nmemb && SIZE_MAX / nmemb < bytes)) { // Overflow
}
extern "C" size_t chk_malloc_usable_size(const void* ptr) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc_usable_size(ptr);
+ }
+
// malloc_usable_size returns 0 for NULL and unknown blocks.
if (ptr == NULL)
return 0;
}
extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
+ }
+
if (!powerof2(alignment)) {
return EINVAL;
}
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* chk_pvalloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->pvalloc(bytes);
+ }
+
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
if (size < bytes) { // Overflow
}
extern "C" void* chk_valloc(size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->valloc(size);
+ }
return chk_memalign(getpagesize(), size);
}
+#endif
static void ReportMemoryLeaks() {
+ ScopedDisableDebugCalls disable;
+
// 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];
}
}
+pthread_key_t g_debug_calls_disabled;
+
extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug* malloc_dispatch) {
g_hash_table = hash_table;
g_malloc_dispatch = malloc_dispatch;
+ pthread_key_create(&g_debug_calls_disabled, NULL);
+
char debug_backlog[PROP_VALUE_MAX];
if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
g_malloc_debug_backlog = atoi(debug_backlog);
ReportMemoryLeaks();
}
backtrace_shutdown();
+
+ pthread_setspecific(g_debug_calls_disabled, NULL);
}
--- /dev/null
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef MALLOC_DEBUG_DISABLE_H
+#define MALLOC_DEBUG_DISABLE_H
+
+#include <pthread.h>
+
+#include "private/bionic_macros.h"
+
+// =============================================================================
+// Used to disable the debug allocation calls.
+// =============================================================================
+extern pthread_key_t g_debug_calls_disabled;
+
+static inline bool DebugCallsDisabled() {
+ return pthread_getspecific(g_debug_calls_disabled) != NULL;
+}
+
+class ScopedDisableDebugCalls {
+ public:
+ ScopedDisableDebugCalls() : disabled_(DebugCallsDisabled()) {
+ if (!disabled_) {
+ pthread_setspecific(g_debug_calls_disabled, reinterpret_cast<const void*>(1));
+ }
+ }
+ ~ScopedDisableDebugCalls() {
+ if (!disabled_) {
+ pthread_setspecific(g_debug_calls_disabled, NULL);
+ }
+ }
+
+ private:
+ bool disabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls);
+};
+
+#endif // MALLOC_DEBUG_DISABLE_H
#include "debug_stacktrace.h"
#include "malloc_debug_common.h"
+#include "malloc_debug_disable.h"
#include "private/bionic_macros.h"
#include "private/libc_logging.h"
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* fill_pvalloc(size_t bytes) {
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
extern "C" void* fill_valloc(size_t size) {
return fill_memalign(getpagesize(), size);
}
+#endif
// =============================================================================
// malloc leak functions
static uint32_t MEMALIGN_GUARD = 0xA1A41520;
extern "C" void* leak_malloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc(bytes);
+ }
+
// allocate enough space infront of the allocation to store the pointer for
// the alloc structure. This will making free'ing the structer really fast!
}
extern "C" void leak_free(void* mem) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->free(mem);
+ }
+
if (mem == NULL) {
return;
}
}
extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->calloc(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) {
}
extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->realloc(oldMem, bytes);
+ }
+
if (oldMem == NULL) {
return leak_malloc(bytes);
}
}
extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->memalign(alignment, bytes);
+ }
+
// we can just use malloc
if (alignment <= MALLOC_ALIGNMENT) {
return leak_malloc(bytes);
}
extern "C" size_t leak_malloc_usable_size(const void* mem) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc_usable_size(mem);
+ }
+
if (mem != NULL) {
// Check the guard to make sure it is valid.
const AllocationEntry* header = const_to_header((void*)mem);
}
extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
+ }
+
if (!powerof2(alignment)) {
return EINVAL;
}
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* leak_pvalloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->pvalloc(bytes);
+ }
+
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
if (size < bytes) { // Overflow
}
extern "C" void* leak_valloc(size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->valloc(size);
+ }
+
return leak_memalign(getpagesize(), size);
}
+#endif