#ifdefined(LIBC_STATIC) #errorThis file should not be compiled forstatic targets. #endif
// Contains a thin layer that calls whatever real native allocator // has been defined. For the libc shared library, this allows the // implementation of a debug malloc that can intercept all of the allocation // calls and add special debugging code to attempt to catch allocation // errors. All of the debugging code is implemented in a separate shared // library that is only loaded when the property "libc.debug.malloc.options" // is set to a non-zero value. There are three functions exported to // allow ddms, or other external users to get information from the debug // allocation. // get_malloc_leak_info: Returns information about all of the known native // allocations that are currently in use. // free_malloc_leak_info: Frees the data allocated by the call to // get_malloc_leak_info. // write_malloc_leak_info: Writes the leak info data to a file.
// In a Zygote child process, this is set to true if profiling of this process // is allowed. Note that this is set at a later time than gZygoteChild. The // latter is set during the fork (while still in zygote's SELinux domain). While // this bit is set after the child is specialized (and has transferred SELinux // domains if applicable). These two flags are read by the // BIONIC_SIGNAL_PROFILER handler, which does nothing if the process is not // profileable.
_Atomic bool gZygoteChildProfileable = false;
staticbool InitMallocFunctions(void* impl_handler, MallocDispatch* table, constchar* prefix) { if (!InitMallocFunction<MallocFree>(impl_handler, &table->free, prefix, "free")) { returnfalse;
} if (!InitMallocFunction<MallocCalloc>(impl_handler, &table->calloc, prefix, "calloc")) { returnfalse;
} if (!InitMallocFunction<MallocMallinfo>(impl_handler, &table->mallinfo, prefix, "mallinfo")) { returnfalse;
} if (!InitMallocFunction<MallocMallopt>(impl_handler, &table->mallopt, prefix, "mallopt")) { returnfalse;
} if (!InitMallocFunction<MallocMalloc>(impl_handler, &table->malloc, prefix, "malloc")) { returnfalse;
} if (!InitMallocFunction<MallocMallocInfo>(impl_handler, &table->malloc_info, prefix, "malloc_info")) { returnfalse;
} if (!InitMallocFunction<MallocMallocUsableSize>(impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size")) { returnfalse;
} if (!InitMallocFunction<MallocMemalign>(impl_handler, &table->memalign, prefix, "memalign")) { returnfalse;
} if (!InitMallocFunction<MallocPosixMemalign>(impl_handler, &table->posix_memalign, prefix, "posix_memalign")) { returnfalse;
} if (!InitMallocFunction<MallocAlignedAlloc>(impl_handler, &table->aligned_alloc,
prefix, "aligned_alloc")) { returnfalse;
} if (!InitMallocFunction<MallocRealloc>(impl_handler, &table->realloc, prefix, "realloc")) { returnfalse;
} if (!InitMallocFunction<MallocReallocArray>(impl_handler, &table->reallocarray, prefix, "reallocarray")) { returnfalse;
} if (!InitMallocFunction<MallocIterate>(impl_handler, &table->malloc_iterate, prefix, "malloc_iterate")) { returnfalse;
} if (!InitMallocFunction<MallocMallocDisable>(impl_handler, &table->malloc_disable, prefix, "malloc_disable")) { returnfalse;
} if (!InitMallocFunction<MallocMallocEnable>(impl_handler, &table->malloc_enable, prefix, "malloc_enable")) { returnfalse;
} #ifdefined(HAVE_DEPRECATED_MALLOC_FUNCS) if (!InitMallocFunction<MallocPvalloc>(impl_handler, &table->pvalloc, prefix, "pvalloc")) { returnfalse;
} if (!InitMallocFunction<MallocValloc>(impl_handler, &table->valloc, prefix, "valloc")) { returnfalse;
} #endif
returntrue;
}
staticvoid MallocFiniImpl(void*) { // Our BSD stdio implementation doesn't close the standard streams, // it only flushes them. Other unclosed FILE*s will show up as // malloc leaks, but to avoid the standard streams showing up in // leak reports, close them here.
fclose(stdin);
fclose(stdout);
fclose(stderr);
staticbool CheckLoadMallocDebug(char** options) { // If kDebugMallocEnvOptions is set then it overrides the system properties. char* env = getenv(kDebugEnvOptions); if (env == nullptr || env[0] == '\0') { if (__system_property_get(kDebugPropertyOptions, *options) == 0 || *options[0] == '\0') { returnfalse;
}
// Check to see if only a specific program should have debug malloc enabled. char program[PROP_VALUE_MAX]; if (__system_property_get(kDebugPropertyProgram, program) != 0 &&
strstr(getprogname(), program) == nullptr) { returnfalse;
}
} else {
*options = env;
} returntrue;
}
void SetGlobalFunctions(void* functions[]) { for (size_t i = 0; i < FUNC_LAST; i++) {
gFunctions[i] = functions[i];
}
}
staticvoid ClearGlobalFunctions() { for (size_t i = 0; i < FUNC_LAST; i++) {
gFunctions[i] = nullptr;
}
}
bool InitSharedLibrary(void* impl_handle, constchar* shared_lib, constchar* prefix, MallocDispatch* dispatch_table) { static constexpr constchar* names[] = { "initialize", "finalize", "get_malloc_leak_info", "free_malloc_leak_info", "malloc_backtrace", "write_malloc_leak_info",
}; for (size_t i = 0; i < FUNC_LAST; i++) { char symbol[128];
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, names[i]);
gFunctions[i] = dlsym(impl_handle, symbol); if (gFunctions[i] == nullptr) {
error_log("%s: %s routine not found in %s", getprogname(), symbol, shared_lib);
ClearGlobalFunctions(); returnfalse;
}
}
if (!InitMallocFunctions(impl_handle, dispatch_table, prefix)) {
ClearGlobalFunctions(); returnfalse;
} returntrue;
}
void* LoadSharedLibrary(constchar* shared_lib, constchar* prefix, MallocDispatch* dispatch_table) { void* impl_handle = nullptr; // TODO(b/369944471) Remove com.android.runtime support // Try to load the libc_malloc_* libs from the "runtime" namespace and then // fall back to dlopen() to load them from the default namespace. // // The libraries are packaged in the runtime APEX together with libc.so. // However, since the libc.so is searched via the symlink in the system // partition (/system/lib/libc.so -> /apex/com.android.runtime/bionic/libc.so) // libc.so is loaded into the default namespace. If we just dlopen() here, the // linker will load the libs found in /system/lib which might be incompatible // with libc.so in the runtime APEX. Use android_dlopen_ext to explicitly load // the ones in the runtime APEX. struct android_namespace_t* runtime_ns = android_get_exported_namespace("com_android_runtime"); if (runtime_ns != nullptr) { const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = runtime_ns,
};
impl_handle = android_dlopen_ext(shared_lib, RTLD_NOW | RTLD_LOCAL, &dlextinfo);
}
// If GWP-ASan was initialised, we should use it as the dispatch table for // heapprofd/malloc_debug/malloc_debug. const MallocDispatch* prev_dispatch = GetDefaultDispatchTable(); if (prev_dispatch == nullptr) {
prev_dispatch = NativeAllocatorDispatch();
}
if (!init_func(prev_dispatch, &gZygoteChild, options)) {
error_log("%s: failed to enable malloc %s", getprogname(), prefix);
ClearGlobalFunctions(); returnfalse;
}
// Do a pointer swap so that all of the functions become valid at once to // avoid any initialization order problems.
atomic_store(&globals->default_dispatch_table, &globals->malloc_dispatch_table); if (!MallocLimitInstalled()) {
atomic_store(&globals->current_dispatch_table, &globals->malloc_dispatch_table);
}
// Use atexit to trigger the cleanup function. This avoids a problem // where another atexit function is used to cleanup allocated memory, // but the finalize function was already called. This particular error // seems to be triggered by a zygote spawned process calling exit. int ret_value = __cxa_atexit(MallocFiniImpl, nullptr, nullptr); if (ret_value != 0) { // We don't consider this a fatal error.
warning_log("failed to set atexit cleanup function: %d", ret_value);
} returntrue;
}
// Prefer malloc debug since it existed first and is a more complete // malloc interceptor than the hooks. bool hook_installed = false; if (CheckLoadMallocDebug(&options)) {
hook_installed = InstallHooks(globals, options, kDebugPrefix, kDebugSharedLib);
} elseif (CheckLoadMallocHooks(&options)) {
hook_installed = InstallHooks(globals, options, kHooksPrefix, kHooksSharedLib);
}
if (!hook_installed) { if (HeapprofdShouldLoad()) {
HeapprofdInstallHooksAtInit(globals);
}
} else { // Record the fact that incompatible hooks are active, to skip any later // heapprofd signal handler invocations.
HeapprofdRememberHookConflict();
}
}
// ============================================================================= // Functions to support dumping of native heap allocations using malloc debug. // ============================================================================= bool GetMallocLeakInfo(android_mallopt_leak_info_t* leak_info) { void* func = gFunctions[FUNC_GET_MALLOC_LEAK_INFO]; if (func == nullptr) {
errno = ENOTSUP; returnfalse;
} reinterpret_cast<get_malloc_leak_info_func_t>(func)(
&leak_info->buffer, &leak_info->overall_size, &leak_info->info_size,
&leak_info->total_memory, &leak_info->backtrace_size); returntrue;
}
bool WriteMallocLeakInfo(FILE* fp) { void* func = gFunctions[FUNC_WRITE_LEAK_INFO]; bool written = false; if (func != nullptr) {
written = reinterpret_cast<write_malloc_leak_info_func_t>(func)(fp);
}
if (!written) {
fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
fprintf(fp, "# adb shell stop\n");
fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
fprintf(fp, "# adb shell start\n");
errno = ENOTSUP;
} return written;
} // =============================================================================
// ============================================================================= // Exported for use by libmemunreachable. // ============================================================================= extern"C" ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) { void* func = gFunctions[FUNC_MALLOC_BACKTRACE]; if (func == nullptr) { return0;
} returnreinterpret_cast<malloc_backtrace_func_t>(func)(pointer, frames, frame_count);
} // =============================================================================
#if !defined(__LP64__) && defined(__arm__) // ============================================================================= // Old platform only functions that some old 32 bit apps are still using. // See b/132175052. // Only compile the functions for 32 bit arm, so that new apps do not use // these functions. // ============================================================================= extern"C"void get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size,
size_t* total_memory, size_t* backtrace_size) { if (info == nullptr || overall_size == nullptr || info_size == nullptr ||
total_memory == nullptr || backtrace_size == nullptr) { return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.