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

Commit be825416 authored by Sandeep Patil's avatar Sandeep Patil
Browse files

Start using libmeminfo for all memory stats gathering.



This begins the change by gathering the system memory info
in android_os_Debug. The memory stats parsing code is planned
to get centralized in system/core/libmeminfo. So, we can also
add things like

 1. 'dumpsys meminfo -w' to get the working set.
 2. Use library to take a diff between 2 working sets of a process.
 3. Provide WSS / PSS monitoring via library as well.

Have all of memory info parsing code unified, tested and benchmarked
in one place so any changes can be tracked, reverted, modified easily.

Bug: 114325007
Bug: 111694435
Test: dumpsys meminfo (compare with /proc/meminfo for accuracy)

Change-Id: I6600bbce007de38dcba890905b466dfe2622aa2c
Signed-off-by: default avatarSandeep Patil <sspatil@google.com>
parent 2ffad2ba
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -274,6 +274,7 @@ cc_library_shared {
        "libicuuc",
        "libmedia",
        "libmediametrics",
        "libmeminfo",
        "libaudioclient",
        "libjpeg",
        "libusbhost",
+23 −114
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <atomic>
#include <iomanip>
#include <string>
#include <vector>

#include <debuggerd/client.h>
#include <log/log.h>
@@ -41,6 +42,7 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
#include <meminfo/sysmeminfo.h>
#include <memtrack/memtrack.h>
#include <memunreachable/memunreachable.h>
#include "android_os_Debug.h"
@@ -712,6 +714,8 @@ static long get_allocated_vmalloc_memory() {
    return vmalloc_allocated_size;
}

// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
// Debug.java.
enum {
    MEMINFO_TOTAL,
    MEMINFO_FREE,
@@ -731,138 +735,43 @@ enum {
    MEMINFO_COUNT
};

static long long get_zram_mem_used()
{
#define ZRAM_SYSFS "/sys/block/zram0/"
    UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
    if (mm_stat_file) {
        long long mem_used_total = 0;

        int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
        if (matched != 1)
            ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");

        return mem_used_total;
    }

    UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
    if (mem_used_total_file) {
        long long mem_used_total = 0;

        int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
        if (matched != 1)
            ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");

        return mem_used_total;
    }

    return 0;
}

static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
{
    char buffer[4096];
    size_t numFound = 0;

    if (out == NULL) {
        jniThrowNullPointerException(env, "out == null");
        return;
    }

    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);

    if (fd < 0) {
        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
    int outLen = env->GetArrayLength(out);
    if (outLen < MEMINFO_COUNT) {
        jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT");
        return;
    }

    int len = read(fd, buffer, sizeof(buffer)-1);
    close(fd);

    if (len < 0) {
        ALOGW("Empty /proc/meminfo");
    // Read system memory info including ZRAM. The values are stored in the vector
    // in the same order as MEMINFO_* enum
    std::vector<uint64_t> mem(MEMINFO_COUNT);
    std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags);
    tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:");
    ::android::meminfo::SysMemInfo smi;
    if (!smi.ReadMemInfo(tags, &mem)) {
        jniThrowRuntimeException(env, "SysMemInfo read failed");
        return;
    }
    buffer[len] = 0;

    static const char* const tags[] = {
            "MemTotal:",
            "MemFree:",
            "Buffers:",
            "Cached:",
            "Shmem:",
            "Slab:",
            "SReclaimable:",
            "SUnreclaim:",
            "SwapTotal:",
            "SwapFree:",
            "ZRam:",
            "Mapped:",
            "VmallocUsed:",
            "PageTables:",
            "KernelStack:",
            NULL
    };
    static const int tagsLen[] = {
            9,
            8,
            8,
            7,
            6,
            5,
            13,
            11,
            10,
            9,
            5,
            7,
            12,
            11,
            12,
            0
    };
    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    char* p = buffer;
    while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
        int i = 0;
        while (tags[i]) {
            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
                p += tagsLen[i];
                while (*p == ' ') p++;
                char* num = p;
                while (*p >= '0' && *p <= '9') p++;
                if (*p != 0) {
                    *p = 0;
                    p++;
                }
                mem[i] = atoll(num);
                numFound++;
                break;
            }
            i++;
        }
        while (*p && *p != '\n') {
            p++;
        }
        if (*p) p++;
    }

    mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
    // Recompute Vmalloc Used since the value in meminfo
    // doesn't account for I/O remapping which doesn't use RAM.
    mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;

    int maxNum = env->GetArrayLength(out);
    if (maxNum > MEMINFO_COUNT) {
        maxNum = MEMINFO_COUNT;
    }
    jlong* outArray = env->GetLongArrayElements(out, 0);
    if (outArray != NULL) {
        for (int i=0; i<maxNum; i++) {
        outLen = MEMINFO_COUNT;
        for (int i = 0; i < outLen; i++) {
            // TODO: move get_allocated_vmalloc_memory() to libmeminfo
            if (i == MEMINFO_VMALLOC_USED) {
                outArray[i] = get_allocated_vmalloc_memory() / 1024;
                continue;
            }
            outArray[i] = mem[i];
        }
    }

    env->ReleaseLongArrayElements(out, outArray, 0);
}

+26 −52
Original line number Diff line number Diff line
@@ -25,8 +25,12 @@
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <meminfo/sysmeminfo.h>
#include <processgroup/processgroup.h>

#include <string>
#include <vector>

#include "core_jni_helpers.h"

#include "android_util_Binder.h"
@@ -39,9 +43,11 @@
#include <inttypes.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>

@@ -603,66 +609,34 @@ static int pid_compare(const void* v1, const void* v2)
    return *((const jint*)v1) - *((const jint*)v2);
}

static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);

    if (fd < 0) {
        ALOGW("Unable to open /proc/meminfo");
        return -1;
    }

    char buffer[2048];
    const int len = read(fd, buffer, sizeof(buffer)-1);
    close(fd);

    if (len < 0) {
        ALOGW("Unable to read /proc/meminfo");
        return -1;
    }
    buffer[len] = 0;

    size_t numFound = 0;
    jlong mem = 0;
    static const std::vector<std::string> memFreeTags = {
        ::android::meminfo::SysMemInfo::kMemFree,
        ::android::meminfo::SysMemInfo::kMemCached,
    };
    std::vector<uint64_t> mem(memFreeTags.size());
    ::android::meminfo::SysMemInfo smi;

    char* p = buffer;
    while (*p && numFound < num) {
        int i = 0;
        while (sums[i]) {
            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
                p += sumsLen[i];
                while (*p == ' ') p++;
                char* num = p;
                while (*p >= '0' && *p <= '9') p++;
                if (*p != 0) {
                    *p = 0;
                    p++;
                    if (*p == 0) p--;
                }
                mem += atoll(num) * 1024;
                numFound++;
                break;
            }
            i++;
        }
        p++;
    if (!smi.ReadMemInfo(memFreeTags, &mem)) {
        jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
        return -1L;
    }

    return numFound > 0 ? mem : -1;
    jlong sum = 0;
    std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
    return sum * 1024;
}

static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
    static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
    return getFreeMemoryImpl(sums, sumsLen, 2);
    struct sysinfo si;
    if (sysinfo(&si) == -1) {
        ALOGE("sysinfo failed: %s", strerror(errno));
        return -1;
    }

static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
    static const char* const sums[] = { "MemTotal:", NULL };
    static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
    return getFreeMemoryImpl(sums, sumsLen, 1);
    return si.totalram;
}

void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,