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 <[email protected]>
diff --git a/sysmeminfo.cpp b/sysmeminfo.cpp
index 82605b6..7e56238 100644
--- a/sysmeminfo.cpp
+++ b/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