Add memory heap checks for mediaserver and audioserver
Memory heap check updated and restored to mediaserver,
added as new option to audioserver.
Requires properties to be set (example for audioserver):
$ adb shell setprop libc.debug.malloc.program audioserver
$ adb shell setprop libc.debug.malloc.options backtrace=8
$ adb shell pkill audioserver
...
and to dump:
$ adb shell dumpsys media.audio_flinger -m
Bug: 28909124
Bug: 27500825
Change-Id: Ifa8c2c02b022daf1a799ee4a1d75282a3c921763
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 814ffcc..dad599b 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -337,7 +337,7 @@
return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
}
-status_t DrmManagerService::dump(int fd, const Vector<String16>& /* args */)
+status_t DrmManagerService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -357,8 +357,12 @@
}
}
if (dumpMem) {
- dumpMemoryAddresses(fd);
+ result.append("\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ result.append(s.c_str(), s.size());
}
+#else
+ (void)args;
#endif
}
write(fd, result.string(), result.size());
diff --git a/include/media/MemoryLeakTrackUtil.h b/include/media/MemoryLeakTrackUtil.h
index d2618aa..4c1a60c 100644
--- a/include/media/MemoryLeakTrackUtil.h
+++ b/include/media/MemoryLeakTrackUtil.h
@@ -16,11 +16,16 @@
#ifndef MEMORY_LEAK_TRACK_UTIL_H
#define MEMORY_LEAK_TRACK_UTIL_H
+#include <iostream>
+
namespace android {
/*
- * Dump the memory address of the calling process to the given fd.
+ * Dump the heap memory of the calling process, sorted by total size
+ * (allocation size * number of allocations).
+ *
+ * limit is the number of unique allocations to return.
*/
-extern void dumpMemoryAddresses(int fd);
+extern std::string dumpMemoryAddresses(size_t limit);
};
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 63f9ed7..6586f41 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -81,6 +81,9 @@
LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
+# for memory heap analysis
+LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
+
LOCAL_MODULE:= libmedia
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp
index 554dbae..18f5f25 100644
--- a/media/libmedia/MemoryLeakTrackUtil.cpp
+++ b/media/libmedia/MemoryLeakTrackUtil.cpp
@@ -14,166 +14,84 @@
* limitations under the License.
*/
-#include <media/MemoryLeakTrackUtil.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MemoryLeackTrackUtil"
+#include <utils/Log.h>
+
+#include "media/MemoryLeakTrackUtil.h"
+#include <sstream>
/*
- * The code here originally resided in MediaPlayerService.cpp and was
- * shamelessly copied over to support memory leak tracking from
- * multiple places.
+ * The code here originally resided in MediaPlayerService.cpp
*/
-namespace android {
+// Figure out the abi based on defined macros.
#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
+extern std::string backtrace_string(const uintptr_t* frames, size_t frame_count);
+
+namespace android {
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);
-// Use the String-class below instead of String8 to allocate all memory
-// beforehand and not reenter the heap while we are examining it...
-struct MyString8 {
- static const size_t MAX_SIZE = 256 * 1024;
-
- MyString8()
- : mPtr((char *)malloc(MAX_SIZE)) {
- *mPtr = '\0';
- }
-
- ~MyString8() {
- free(mPtr);
- }
-
- void append(const char *s) {
- strncat(mPtr, s, MAX_SIZE - size() - 1);
- }
-
- const char *string() const {
- return mPtr;
- }
-
- size_t size() const {
- return strlen(mPtr);
- }
-
- void clear() {
- *mPtr = '\0';
- }
-
-private:
- char *mPtr;
-
- MyString8(const MyString8 &);
- MyString8 &operator=(const MyString8 &);
-};
-
-void dumpMemoryAddresses(int fd)
+std::string dumpMemoryAddresses(size_t limit)
{
- const size_t SIZE = 256;
- char buffer[SIZE];
- MyString8 result;
-
- typedef struct {
- size_t size;
- size_t dups;
- intptr_t * backtrace;
- } AllocEntry;
-
- uint8_t *info = NULL;
- size_t overallSize = 0;
- size_t infoSize = 0;
- size_t totalMemory = 0;
- size_t backtraceSize = 0;
-
+ uint8_t *info;
+ size_t overallSize;
+ size_t infoSize;
+ size_t totalMemory;
+ size_t backtraceSize;
get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize);
- if (info) {
- uint8_t *ptr = info;
- size_t count = overallSize / infoSize;
- snprintf(buffer, SIZE, " Allocation count %i\n", count);
- result.append(buffer);
- snprintf(buffer, SIZE, " Total memory %i\n", totalMemory);
- result.append(buffer);
-
- AllocEntry * entries = new AllocEntry[count];
-
- for (size_t i = 0; i < count; i++) {
- // Each entry should be size_t, size_t, intptr_t[backtraceSize]
- AllocEntry *e = &entries[i];
-
- e->size = *reinterpret_cast<size_t *>(ptr);
- ptr += sizeof(size_t);
-
- e->dups = *reinterpret_cast<size_t *>(ptr);
- ptr += sizeof(size_t);
-
- e->backtrace = reinterpret_cast<intptr_t *>(ptr);
- ptr += sizeof(intptr_t) * backtraceSize;
- }
-
- // Now we need to sort the entries. They come sorted by size but
- // not by stack trace which causes problems using diff.
- bool moved;
- do {
- moved = false;
- for (size_t i = 0; i < (count - 1); i++) {
- AllocEntry *e1 = &entries[i];
- AllocEntry *e2 = &entries[i+1];
-
- bool swap = e1->size < e2->size;
- if (e1->size == e2->size) {
- for(size_t j = 0; j < backtraceSize; j++) {
- if (e1->backtrace[j] == e2->backtrace[j]) {
- continue;
- }
- swap = e1->backtrace[j] < e2->backtrace[j];
- break;
- }
- }
- if (swap) {
- AllocEntry t = entries[i];
- entries[i] = entries[i+1];
- entries[i+1] = t;
- moved = true;
- }
- }
- } while (moved);
-
- write(fd, result.string(), result.size());
- result.clear();
-
- for (size_t i = 0; i < count; i++) {
- AllocEntry *e = &entries[i];
-
- snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups);
- result.append(buffer);
- for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) {
- if (ct) {
- result.append(", ");
- }
- snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]);
- result.append(buffer);
- }
- result.append("\n");
-
- write(fd, result.string(), result.size());
- result.clear();
- }
-
- delete[] entries;
- free_malloc_leak_info(info);
+ size_t count;
+ if (info == nullptr || overallSize == 0 || infoSize == 0
+ || (count = overallSize / infoSize) == 0) {
+ ALOGD("no malloc info, libc.debug.malloc.program property should be set");
+ return std::string();
}
+
+ std::ostringstream oss;
+ oss << totalMemory << " bytes in " << count << " allocations\n";
+ oss << " ABI: '" ABI_STRING "'" << "\n\n";
+ if (count > limit) count = limit;
+
+ // The memory is sorted based on total size which is useful for finding
+ // worst memory offenders. For diffs, sometimes it is preferable to sort
+ // based on the backtrace.
+ for (size_t i = 0; i < count; i++) {
+ struct AllocEntry {
+ size_t size; // bit 31 is set if this is zygote allocated memory
+ size_t allocations;
+ uintptr_t backtrace[];
+ };
+
+ const AllocEntry * const e = (AllocEntry *)(info + i * infoSize);
+
+ oss << (e->size * e->allocations)
+ << " bytes ( " << e->size << " bytes * " << e->allocations << " allocations )\n";
+ oss << backtrace_string(e->backtrace, backtraceSize) << "\n";
+ }
+ oss << "\n";
+ free_malloc_leak_info(info);
+ return oss.str();
}
-#else
-// Does nothing
-void dumpMemoryAddresses(int fd __unused) {}
-
-#endif
} // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index f0190c4..acba6d7 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -536,7 +536,9 @@
}
}
if (dumpMem) {
- dumpMemoryAddresses(fd);
+ result.append("\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ result.append(s.c_str(), s.size());
}
if (unreachableMemory) {
result.append("\nDumping unreachable memory:\n");
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2b0d4c8..75998ef 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -58,7 +58,7 @@
#include <powermanager/PowerManager.h>
#include <media/IMediaLogService.h>
-
+#include <media/MemoryLeakTrackUtil.h>
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
#include <media/AudioParameter.h>
@@ -471,17 +471,25 @@
}
// check for optional arguments
+ bool dumpMem = false;
bool unreachableMemory = false;
for (const auto &arg : args) {
- if (arg == String16("--unreachable")) {
+ if (arg == String16("-m")) {
+ dumpMem = true;
+ } else if (arg == String16("--unreachable")) {
unreachableMemory = true;
}
}
+ if (dumpMem) {
+ dprintf(fd, "\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ write(fd, s.c_str(), s.size());
+ }
if (unreachableMemory) {
dprintf(fd, "\nDumping unreachable memory:\n");
// TODO - should limit be an argument parameter?
- std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+ std::string s = GetUnreachableMemoryString(true /* contents */, 100 /* limit */);
write(fd, s.c_str(), s.size());
}
}