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: Sandeep Patil <sspatil@google.com>
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43f8d00..21fa75e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -274,6 +274,7 @@
"libicuuc",
"libmedia",
"libmediametrics",
+ "libmeminfo",
"libaudioclient",
"libjpeg",
"libusbhost",
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fa1da4b..888dab1 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -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 @@
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 @@
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);
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 102a0b7..0c1a8aa 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -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 @@
return *((const jint*)v1) - *((const jint*)v2);
}
-static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
-{
- 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;
-
- 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++;
- }
-
- return numFound > 0 ? mem : -1;
-}
-
static jlong android_os_Process_getFreeMemory(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);
+ 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;
+
+ if (!smi.ReadMemInfo(memFreeTags, &mem)) {
+ jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
+ return -1L;
+ }
+
+ jlong sum = 0;
+ std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
+ return sum * 1024;
}
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);
+ struct sysinfo si;
+ if (sysinfo(&si) == -1) {
+ ALOGE("sysinfo failed: %s", strerror(errno));
+ return -1;
+ }
+
+ return si.totalram;
}
void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,