libmeminfo: Add support to read zram memory consumption
Bug: 114325007
Bug: 111694435
Test: libmeminfo_test 1 --gtest_filter=SysMemInfoParse.TestZramTotal
Benchmark: libmeminfo_benchmark --benchmark_filter=BM_.*ZramTotal
Benchmark Result on Blueline:
-----------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------
BM_OldReadZramTotal 3857 ns 3839 ns 134096
BM_NewReadZramTotal 4461 ns 4440 ns 157341
Change-Id: I5220fa17b101981ef859179960fe78fe68e84852
Signed-off-by: Sandeep Patil <sspatil@google.com>
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
index aab3743..3e191ad 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -54,6 +54,11 @@
srcs: [
"libmeminfo_test.cpp"
],
+
+ data: [
+ "testdata1/*",
+ "testdata2/*"
+ ],
}
cc_benchmark {
@@ -67,4 +72,8 @@
"libmeminfo",
"libprocinfo",
],
+
+ data: [
+ "testdata1/*",
+ ],
}
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
index 87e1464..885be1d 100644
--- a/libmeminfo/include/meminfo/sysmeminfo.h
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -38,7 +38,6 @@
static constexpr const char* kMemSUnreclaim = "SUnreclaim:";
static constexpr const char* kMemSwapTotal = "SwapTotal:";
static constexpr const char* kMemSwapFree = "SwapFree:";
- static constexpr const char* kMemZram = "Zram:";
static constexpr const char* kMemMapped = "Mapped:";
static constexpr const char* kMemVmallocUsed = "VmallocUsed:";
static constexpr const char* kMemPageTables = "PageTables:";
@@ -64,14 +63,15 @@
uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_[kMemSUnreclaim]; }
uint64_t mem_swap_kb() { return mem_in_kb_[kMemSwapTotal]; }
uint64_t mem_swap_free_kb() { return mem_in_kb_[kMemSwapFree]; }
- uint64_t mem_zram_kb() { return mem_in_kb_[kMemZram]; }
uint64_t mem_mapped_kb() { return mem_in_kb_[kMemMapped]; }
uint64_t mem_vmalloc_used_kb() { return mem_in_kb_[kMemVmallocUsed]; }
uint64_t mem_page_tables_kb() { return mem_in_kb_[kMemPageTables]; }
uint64_t mem_kernel_stack_kb() { return mem_in_kb_[kMemPageTables]; }
+ uint64_t mem_zram_kb(const std::string& zram_dev = "");
private:
std::map<std::string, uint64_t> mem_in_kb_;
+ bool MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev);
};
} // namespace meminfo
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
index e2239f0..1db0824 100644
--- a/libmeminfo/libmeminfo_benchmark.cpp
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -17,6 +17,8 @@
#include <meminfo/sysmeminfo.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -46,7 +48,7 @@
MEMINFO_COUNT
};
-void get_mem_info(uint64_t mem[], const char* file) {
+static void get_mem_info(uint64_t mem[], const char* file) {
char buffer[4096];
unsigned int numFound = 0;
@@ -67,9 +69,10 @@
buffer[len] = 0;
static const char* const tags[] = {
- "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:", "Slab:",
- "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:", "ZRam:", "Mapped:",
- "VmallocUsed:", "PageTables:", "KernelStack:", NULL};
+ "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};
@@ -78,7 +81,8 @@
while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
int i = 0;
while (tags[i]) {
- //std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
+ // std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) <<
+ // std::endl;
if (strncmp(p, tags[i], tagsLen[i]) == 0) {
p += tagsLen[i];
while (*p == ' ') p++;
@@ -214,4 +218,51 @@
}
BENCHMARK(BM_ReadMemInfo);
+static uint64_t get_zram_mem_used(const std::string& zram_dir) {
+ FILE* f = fopen((zram_dir + "mm_stat").c_str(), "r");
+ if (f) {
+ uint64_t mem_used_total = 0;
+
+ int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
+ if (matched != 1)
+ fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mm_stat").c_str());
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ f = fopen((zram_dir + "mem_used_total").c_str(), "r");
+ if (f) {
+ uint64_t mem_used_total = 0;
+
+ int matched = fscanf(f, "%" SCNu64, &mem_used_total);
+ if (matched != 1)
+ fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mem_used_total").c_str());
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ return 0;
+}
+
+static void BM_OldReadZramTotal(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ for (auto _ : state) {
+ uint64_t zram_total __attribute__((unused)) = get_zram_mem_used(zram_mmstat_dir) / 1024;
+ }
+}
+BENCHMARK(BM_OldReadZramTotal);
+
+static void BM_NewReadZramTotal(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ ::android::meminfo::SysMemInfo mi;
+ for (auto _ : state) {
+ uint64_t zram_total __attribute__((unused)) = mi.mem_zram_kb(zram_mmstat_dir);
+ }
+}
+BENCHMARK(BM_NewReadZramTotal);
+
BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index f973694..923e156 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -312,6 +312,17 @@
EXPECT_EQ(mi.mem_total_kb(), 0);
}
+TEST(SysMemInfoParse, TestZramTotal) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+
+ SysMemInfo mi;
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ EXPECT_EQ(mi.mem_zram_kb(zram_mmstat_dir), 30504);
+
+ std::string zram_memused_dir = exec_dir + "/testdata2/";
+ EXPECT_EQ(mi.mem_zram_kb(zram_memused_dir), 30504);
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc <= 1) {
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
index 82605b6..7e56238 100644
--- a/libmeminfo/sysmeminfo.cpp
+++ b/libmeminfo/sysmeminfo.cpp
@@ -17,10 +17,12 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <cctype>
+#include <cstdio>
#include <fstream>
#include <string>
#include <utility>
@@ -29,7 +31,9 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include "meminfo_private.h"
@@ -37,11 +41,11 @@
namespace meminfo {
const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
- SysMemInfo::kMemTotal, SysMemInfo::kMemFree, SysMemInfo::kMemBuffers,
- SysMemInfo::kMemCached, SysMemInfo::kMemShmem, SysMemInfo::kMemSlab,
- SysMemInfo::kMemSReclaim, SysMemInfo::kMemSUnreclaim, SysMemInfo::kMemSwapTotal,
- SysMemInfo::kMemSwapFree, SysMemInfo::kMemZram, SysMemInfo::kMemMapped,
- SysMemInfo::kMemVmallocUsed, SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+ SysMemInfo::kMemTotal, SysMemInfo::kMemFree, SysMemInfo::kMemBuffers,
+ SysMemInfo::kMemCached, SysMemInfo::kMemShmem, SysMemInfo::kMemSlab,
+ SysMemInfo::kMemSReclaim, SysMemInfo::kMemSUnreclaim, SysMemInfo::kMemSwapTotal,
+ SysMemInfo::kMemSwapFree, SysMemInfo::kMemMapped, SysMemInfo::kMemVmallocUsed,
+ SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
};
bool SysMemInfo::ReadMemInfo(const std::string& path) {
@@ -129,5 +133,68 @@
}
#endif
+uint64_t SysMemInfo::mem_zram_kb(const std::string& zram_dev) {
+ uint64_t mem_zram_total = 0;
+ if (!zram_dev.empty()) {
+ if (!MemZramDevice(zram_dev, &mem_zram_total)) {
+ return 0;
+ }
+ return mem_zram_total / 1024;
+ }
+
+ constexpr uint32_t kMaxZramDevices = 256;
+ for (uint32_t i = 0; i < kMaxZramDevices; i++) {
+ std::string zram_dev = ::android::base::StringPrintf("/sys/block/zram%u/", i);
+ if (access(zram_dev.c_str(), F_OK)) {
+ // We assume zram devices appear in range 0-255 and appear always in sequence
+ // under /sys/block. So, stop looking for them once we find one is missing.
+ break;
+ }
+
+ uint64_t mem_zram_dev;
+ if (!MemZramDevice(zram_dev, &mem_zram_dev)) {
+ return 0;
+ }
+
+ mem_zram_total += mem_zram_dev;
+ }
+
+ return mem_zram_total / 1024;
+}
+
+bool SysMemInfo::MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev) {
+ std::string content;
+ if (android::base::ReadFileToString(zram_dev + "mm_stat", &content)) {
+ std::vector<std::string> values = ::android::base::Split(content, " ");
+ if (values.size() < 3) {
+ LOG(ERROR) << "Malformed mm_stat file for zram dev: " << zram_dev
+ << " content: " << content;
+ return false;
+ }
+
+ if (!::android::base::ParseUint(values[2], mem_zram_dev)) {
+ LOG(ERROR) << "Malformed mm_stat file for zram dev: " << zram_dev
+ << " value: " << values[2];
+ return false;
+ }
+
+ return true;
+ }
+
+ if (::android::base::ReadFileToString(zram_dev + "mem_used_total", &content)) {
+ *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
+ if (*mem_zram_dev == ULLONG_MAX) {
+ PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
+ << " content: " << content;
+ return false;
+ }
+
+ return true;
+ }
+
+ LOG(ERROR) << "Can't find memory status under: " << zram_dev;
+ return false;
+}
+
} // namespace meminfo
} // namespace android
diff --git a/libmeminfo/testdata1/mm_stat b/libmeminfo/testdata1/mm_stat
new file mode 100644
index 0000000..684f567
--- /dev/null
+++ b/libmeminfo/testdata1/mm_stat
@@ -0,0 +1 @@
+145674240 26801454 31236096 0 45772800 3042 1887 517
diff --git a/libmeminfo/testdata2/mem_used_total b/libmeminfo/testdata2/mem_used_total
new file mode 100644
index 0000000..97fcf41
--- /dev/null
+++ b/libmeminfo/testdata2/mem_used_total
@@ -0,0 +1 @@
+31236096