Use the malloc debug heap dumper.

Instead of having the malloc debug heap dump code live in frameworks
code, call into malloc debug to do the dump.

Test: Ran am dumpheap -n <PID> <FILE> of a process with and without
Test: malloc debug enabled.
Change-Id: I7911a3ee7dcbc79dd11003a24e27ff99c8301d43
Merged-In: I7911a3ee7dcbc79dd11003a24e27ff99c8301d43
(cherry picked from commit 38e2c3bc9c)
This commit is contained in:
Christopher Ferris
2018-06-13 13:19:57 -07:00
parent aa5d8dfeb0
commit ffa139e83d

View File

@@ -902,146 +902,6 @@ jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
/* pulled out of bionic */
extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
extern "C" void free_malloc_leak_info(uint8_t* info);
#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
static size_t gNumBacktraceElements;
/*
* This is a qsort() callback.
*
* See dumpNativeHeap() for comments about the data format and sort order.
*/
static int compareHeapRecords(const void* vrec1, const void* vrec2)
{
const size_t* rec1 = (const size_t*) vrec1;
const size_t* rec2 = (const size_t*) vrec2;
size_t size1 = *rec1;
size_t size2 = *rec2;
if (size1 < size2) {
return 1;
} else if (size1 > size2) {
return -1;
}
uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
uintptr_t addr1 = bt1[idx];
uintptr_t addr2 = bt2[idx];
if (addr1 == addr2) {
if (addr1 == 0)
break;
continue;
}
if (addr1 < addr2) {
return -1;
} else if (addr1 > addr2) {
return 1;
}
}
return 0;
}
/*
* The get_malloc_leak_info() call returns an array of structs that
* look like this:
*
* size_t size
* size_t allocations
* intptr_t backtrace[32]
*
* "size" is the size of the allocation, "backtrace" is a fixed-size
* array of function pointers, and "allocations" is the number of
* allocations with the exact same size and backtrace.
*
* The entries are sorted by descending total size (i.e. size*allocations)
* then allocation count. For best results with "diff" we'd like to sort
* primarily by individual size then stack trace. Since the entries are
* fixed-size, and we're allowed (by the current implementation) to mangle
* them, we can do this in place.
*/
static void dumpNativeHeap(FILE* fp)
{
uint8_t* info = NULL;
size_t overallSize, infoSize, totalMemory, backtraceSize;
get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
&backtraceSize);
if (info == NULL) {
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");
return;
}
assert(infoSize != 0);
assert(overallSize % infoSize == 0);
fprintf(fp, "Android Native Heap Dump v1.0\n\n");
size_t recordCount = overallSize / infoSize;
fprintf(fp, "Total memory: %zu\n", totalMemory);
fprintf(fp, "Allocation records: %zd\n", recordCount);
fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
fprintf(fp, "\n");
/* re-sort the entries */
gNumBacktraceElements = backtraceSize;
qsort(info, recordCount, infoSize, compareHeapRecords);
/* dump the entries to the file */
const uint8_t* ptr = info;
for (size_t idx = 0; idx < recordCount; idx++) {
size_t size = *(size_t*) ptr;
size_t allocations = *(size_t*) (ptr + sizeof(size_t));
uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
fprintf(fp, "z %d sz %8zu num %4zu bt",
(size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
size & ~SIZE_FLAG_ZYGOTE_CHILD,
allocations);
for (size_t bt = 0; bt < backtraceSize; bt++) {
if (backtrace[bt] == 0) {
break;
} else {
#ifdef __LP64__
fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
#else
fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
#endif
}
}
fprintf(fp, "\n");
ptr += infoSize;
}
free_malloc_leak_info(info);
fprintf(fp, "MAPS\n");
const char* maps = "/proc/self/maps";
UniqueFile in = MakeUniqueFile(maps, "re");
if (in == nullptr) {
fprintf(fp, "Could not open %s\n", maps);
return;
}
char buf[BUFSIZ];
while (size_t n = fread(buf, sizeof(char), BUFSIZ, in.get())) {
fwrite(buf, sizeof(char), n, fp);
}
fprintf(fp, "END\n");
}
static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
{
if (fileDescriptor == NULL) {
@@ -1072,6 +932,9 @@ static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
return true;
}
/* pulled out of bionic */
extern "C" void write_malloc_leak_info(FILE* fp);
/*
* Dump the native heap, writing human-readable output to the specified
* file descriptor.
@@ -1085,7 +948,9 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject,
}
ALOGD("Native heap dump starting...\n");
dumpNativeHeap(fp.get());
// Formatting of the native heap dump is handled by malloc debug itself.
// See https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md#backtrace-heap-dump-format
write_malloc_leak_info(fp.get());
ALOGD("Native heap dump complete.\n");
}