__BEGIN_DECLS
struct mallinfo je_mallinfo();
+int je_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+void je_malloc_disable();
+void je_malloc_enable();
+int je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
void* je_memalign_round_up_boundary(size_t, size_t);
void* je_pvalloc(size_t);
// free_malloc_leak_info: Frees the data allocated by the call to
// get_malloc_leak_info.
+#include <pthread.h>
+
#include <private/bionic_config.h>
#include <private/bionic_globals.h>
#include <private/bionic_malloc_dispatch.h>
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
Malloc(valloc),
#endif
+ Malloc(iterate),
+ Malloc(malloc_disable),
+ Malloc(malloc_enable),
};
// In a VM process, this is set to 1 after fork()ing out of zygote.
#if !defined(LIBC_STATIC)
#include <dlfcn.h>
-#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
}
g_debug_free_malloc_leak_info_func(info);
}
+
// =============================================================================
template<typename FunctionType>
prefix, "realloc")) {
return false;
}
+ if (!InitMallocFunction<MallocIterate>(malloc_impl_handler, &table->iterate,
+ prefix, "iterate")) {
+ return false;
+ }
+ if (!InitMallocFunction<MallocMallocDisable>(malloc_impl_handler, &table->malloc_disable,
+ prefix, "malloc_disable")) {
+ return false;
+ }
+ if (!InitMallocFunction<MallocMallocEnable>(malloc_impl_handler, &table->malloc_enable,
+ prefix, "malloc_enable")) {
+ return false;
+ }
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
if (!InitMallocFunction<MallocPvalloc>(malloc_impl_handler, &table->pvalloc,
prefix, "pvalloc")) {
malloc_init_impl(globals);
}
#endif // !LIBC_STATIC
+
+// =============================================================================
+// Exported for use by libmemunreachable.
+// =============================================================================
+
+// Calls callback for every allocation in the anonymous heap mapping
+// [base, base+size). Must be called between malloc_disable and malloc_enable.
+extern "C" int malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
+ auto _iterate = __libc_globals->malloc_dispatch.iterate;
+ if (__predict_false(_iterate != nullptr)) {
+ return _iterate(base, size, callback, arg);
+ }
+ return Malloc(iterate)(base, size, callback, arg);
+}
+
+// Disable calls to malloc so malloc_iterate gets a consistent view of
+// allocated memory.
+extern "C" void malloc_disable() {
+ auto _malloc_disable = __libc_globals->malloc_dispatch.malloc_disable;
+ if (__predict_false(_malloc_disable != nullptr)) {
+ return _malloc_disable();
+ }
+ return Malloc(malloc_disable)();
+}
+
+// Re-enable calls to malloc after a previous call to malloc_disable.
+extern "C" void malloc_enable() {
+ auto _malloc_enable = __libc_globals->malloc_dispatch.malloc_enable;
+ if (__predict_false(_malloc_enable != nullptr)) {
+ return _malloc_enable();
+ }
+ return Malloc(malloc_enable)();
+}
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
madvise;
mallinfo;
malloc;
+ malloc_disable;
+ malloc_enable;
+ malloc_iterate;
malloc_info;
malloc_usable_size;
mbrlen;
#include "malloc_debug.h"
#include "TrackData.h"
-void TrackData::GetList(std::vector<Header*>* list) {
+void TrackData::GetList(std::vector<const Header*>* list) {
ScopedDisableDebugCalls disable;
for (const auto& header : headers_) {
}
// Sort by the size of the allocation.
- std::sort(list->begin(), list->end(), [](Header* a, Header* b) {
+ std::sort(list->begin(), list->end(), [](const Header* a, const Header* b) {
if (a->size == b->size) return a < b;
return a->size > b->size;
});
}
-void TrackData::Add(Header* header, bool backtrace_found) {
+void TrackData::Add(const Header* header, bool backtrace_found) {
ScopedDisableDebugCalls disable;
pthread_mutex_lock(&mutex_);
pthread_mutex_unlock(&mutex_);
}
-void TrackData::Remove(Header* header, bool backtrace_found) {
+void TrackData::Remove(const Header* header, bool backtrace_found) {
ScopedDisableDebugCalls disable;
pthread_mutex_lock(&mutex_);
pthread_mutex_unlock(&mutex_);
}
+bool TrackData::Contains(const Header* header) {
+ ScopedDisableDebugCalls disable;
+
+ pthread_mutex_lock(&mutex_);
+ bool found = headers_.count(header);
+ pthread_mutex_unlock(&mutex_);
+ return found;
+}
+
void TrackData::DisplayLeaks(DebugData& debug) {
ScopedDisableDebugCalls disable;
- std::vector<Header*> list;
+ std::vector<const Header*> list;
GetList(&list);
size_t track_count = 0;
}
*overall_size = *info_size * total_backtrace_allocs_;
- std::vector<Header*> list;
+ std::vector<const Header*> list;
GetList(&list);
uint8_t* data = *info;
TrackData() = default;
virtual ~TrackData() = default;
- void GetList(std::vector<Header*>* list);
+ void GetList(std::vector<const Header*>* list);
- void Add(Header* header, bool backtrace_found);
+ void Add(const Header* header, bool backtrace_found);
- void Remove(Header* header, bool backtrace_found);
+ void Remove(const Header* header, bool backtrace_found);
+
+ bool Contains(const Header *header);
void GetInfo(DebugData& debug, uint8_t** info, size_t* overall_size,
size_t* info_size, size_t* total_memory, size_t* backtrace_size);
private:
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
- std::unordered_set<Header*> headers_;
+ std::unordered_set<const Header*> headers_;
size_t total_backtrace_allocs_ = 0;
DISALLOW_COPY_AND_ASSIGN(TrackData);
debug_free_malloc_leak_info;
debug_get_malloc_leak_info;
debug_initialize;
+ debug_iterate;
debug_mallinfo;
debug_malloc;
+ debug_malloc_disable;
+ debug_malloc_enable;
debug_malloc_usable_size;
debug_memalign;
debug_posix_memalign;
debug_free_malloc_leak_info;
debug_get_malloc_leak_info;
debug_initialize;
+ debug_iterate;
debug_mallinfo;
debug_malloc;
+ debug_malloc_disable;
+ debug_malloc_enable;
debug_malloc_usable_size;
debug_memalign;
debug_posix_memalign;
void* debug_calloc(size_t nmemb, size_t bytes);
struct mallinfo debug_mallinfo();
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
+int debug_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+void debug_malloc_disable();
+void debug_malloc_enable();
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_pvalloc(size_t bytes);
return (*memptr != nullptr) ? 0 : ENOMEM;
}
+int debug_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
+ // Can't allocate, malloc is disabled
+ // Manual capture of the arguments to pass to the lambda below as void* arg
+ struct iterate_ctx {
+ decltype(callback) callback;
+ decltype(arg) arg;
+ } ctx = { callback, arg };
+
+ return g_dispatch->iterate(base, size,
+ [](uintptr_t base, size_t size, void* arg) {
+ const iterate_ctx* ctx = reinterpret_cast<iterate_ctx*>(arg);
+ const void* pointer = reinterpret_cast<void*>(base);
+ if (g_debug->need_header()) {
+ const Header* header = reinterpret_cast<const Header*>(pointer);
+ if (g_debug->config().options & TRACK_ALLOCS) {
+ if (g_debug->track->Contains(header)) {
+ // Return just the body of the allocation if we're sure the header exists
+ ctx->callback(reinterpret_cast<uintptr_t>(g_debug->GetPointer(header)),
+ header->real_size(), ctx->arg);
+ return;
+ }
+ }
+ }
+ // Fall back to returning the whole allocation
+ ctx->callback(base, size, ctx->arg);
+ }, &ctx);
+}
+
+void debug_malloc_disable() {
+ g_dispatch->malloc_disable();
+ if (g_debug->track) {
+ g_debug->track->PrepareFork();
+ }
+}
+
+void debug_malloc_enable() {
+ if (g_debug->track) {
+ g_debug->track->PostForkParent();
+ }
+ g_dispatch->malloc_enable();
+}
+
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_pvalloc(size_t bytes) {
if (DebugCallsDisabled()) {
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
nullptr,
#endif
+ nullptr,
+ nullptr,
+ nullptr,
};
void VerifyAllocCalls() {
#define _PRIVATE_BIONIC_MALLOC_DISPATCH_H
#include <stddef.h>
+#include <stdint.h>
#include <private/bionic_config.h>
// Entry in malloc dispatch table.
typedef void* (*MallocMemalign)(size_t, size_t);
typedef int (*MallocPosixMemalign)(void**, size_t, size_t);
typedef void* (*MallocRealloc)(void*, size_t);
+typedef int (*MallocIterate)(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+typedef void (*MallocMallocDisable)();
+typedef void (*MallocMallocEnable)();
+
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
typedef void* (*MallocPvalloc)(size_t);
typedef void* (*MallocValloc)(size_t);
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
MallocValloc valloc;
#endif
+ MallocIterate iterate;
+ MallocMallocDisable malloc_disable;
+ MallocMallocEnable malloc_enable;
} __attribute__((aligned(32)));
#endif