Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07b745e1 authored by Andy Hung's avatar Andy Hung
Browse files

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
parent d4c4d224
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -337,7 +337,7 @@ ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
    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 @@ status_t DrmManagerService::dump(int fd, const Vector<String16>& /* args */)
            }
        }
        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());
+7 −2
Original line number Diff line number Diff line
@@ -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);

};

+3 −0
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@ LOCAL_SHARED_LIBRARIES := \

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
+62 −144
Original line number Diff line number Diff line
@@ -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;
    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();
    }
                }
                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();
    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++) {
            AllocEntry *e = &entries[i];
        struct AllocEntry {
            size_t size;  // bit 31 is set if this is zygote allocated memory
            size_t allocations;
            uintptr_t backtrace[];
        };

            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");
        const AllocEntry * const e = (AllocEntry *)(info + i * infoSize);

            write(fd, result.string(), result.size());
            result.clear();
        oss << (e->size * e->allocations)
                << " bytes ( " << e->size << " bytes * " << e->allocations << " allocations )\n";
        oss << backtrace_string(e->backtrace, backtraceSize) << "\n";
    }

        delete[] entries;
    oss << "\n";
    free_malloc_leak_info(info);
    }
    return oss.str();
}

#else
// Does nothing
void dumpMemoryAddresses(int fd __unused) {}

#endif
}  // namespace android
+3 −1
Original line number Diff line number Diff line
@@ -536,7 +536,9 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
            }
        }
        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");
Loading