libmeminfo: Add support for counting swap pages

Adds procmeminfo API to get the vector of swap_offsets
if SWAP is enabled on the device.

Bug: 114325007
Bug: 111694435
Test: libmeminfo_test 1
Change-Id: If0b52d042749a5bcb2c87aa2cb1595190d4769b1
Signed-off-by: Sandeep Patil <sspatil@google.com>
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
index c328648..809054b 100644
--- a/libmeminfo/include/meminfo/meminfo.h
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -31,6 +31,8 @@
     uint64_t pss;
     uint64_t uss;
 
+    uint64_t swap;
+
     uint64_t private_clean;
     uint64_t private_dirty;
     uint64_t shared_clean;
@@ -41,6 +43,7 @@
           rss(0),
           pss(0),
           uss(0),
+          swap(0),
           private_clean(0),
           private_dirty(0),
           shared_clean(0),
@@ -49,7 +52,7 @@
     ~MemUsage() = default;
 
     void clear() {
-        vss = rss = pss = uss = 0;
+        vss = rss = pss = uss = swap = 0;
         private_clean = private_dirty = shared_clean = shared_dirty = 0;
     }
 };
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index b37c56b..dd59912 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -34,6 +34,7 @@
     const std::vector<Vma>& Maps();
     const MemUsage& Usage();
     const MemUsage& Wss();
+    const std::vector<uint16_t>& SwapOffsets() const;
 
     bool WssReset();
     ~ProcMemInfo() = default;
@@ -49,6 +50,7 @@
 
     MemUsage usage_;
     MemUsage wss_;
+    std::vector<uint16_t> swap_offsets_;
 };
 
 }  // namespace meminfo
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
index f5e05bd..87e1464 100644
--- a/libmeminfo/include/meminfo/sysmeminfo.h
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -28,6 +28,22 @@
 class SysMemInfo final {
     // System or Global memory accounting
   public:
+    static constexpr const char* kMemTotal = "MemTotal:";
+    static constexpr const char* kMemFree = "MemFree:";
+    static constexpr const char* kMemBuffers = "Buffers:";
+    static constexpr const char* kMemCached = "Cached:";
+    static constexpr const char* kMemShmem = "Shmem:";
+    static constexpr const char* kMemSlab = "Slab:";
+    static constexpr const char* kMemSReclaim = "SReclaimable:";
+    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:";
+    static constexpr const char* kMemKernelStack = "KernelStack:";
+
     static const std::vector<std::string> kDefaultSysMemInfoTags;
 
     SysMemInfo() = default;
@@ -38,21 +54,21 @@
                      const std::string& path = "/proc/meminfo");
 
     // getters
-    uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
-    uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
-    uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
-    uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
-    uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
-    uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
-    uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
-    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
-    uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
-    uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
-    uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
-    uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
-    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
-    uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
-    uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
+    uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
+    uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
+    uint64_t mem_buffers_kb() { return mem_in_kb_[kMemBuffers]; }
+    uint64_t mem_cached_kb() { return mem_in_kb_[kMemCached]; }
+    uint64_t mem_shmem_kb() { return mem_in_kb_[kMemShmem]; }
+    uint64_t mem_slab_kb() { return mem_in_kb_[kMemSlab]; }
+    uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_[kMemSReclaim]; }
+    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]; }
 
   private:
     std::map<std::string, uint64_t> mem_in_kb_;
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 7a2be41..f973694 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -73,7 +73,7 @@
     }
 }
 
-TEST_F(ValidateProcMemInfo, TestMapsUsage) {
+TEST_F(ValidateProcMemInfo, TestMaps) {
     const std::vector<Vma>& maps = proc_mem->Maps();
     ASSERT_FALSE(maps.empty());
     ASSERT_EQ(proc->num_maps, maps.size());
@@ -96,6 +96,30 @@
     EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
 }
 
+TEST_F(ValidateProcMemInfo, TestSwapUsage) {
+    const std::vector<Vma>& maps = proc_mem->Maps();
+    ASSERT_FALSE(maps.empty());
+    ASSERT_EQ(proc->num_maps, maps.size());
+
+    pm_memusage_t map_usage, proc_usage;
+    pm_memusage_zero(&map_usage);
+    pm_memusage_zero(&proc_usage);
+    for (size_t i = 0; i < maps.size(); i++) {
+        ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
+        EXPECT_EQ(map_usage.swap, maps[i].usage.swap) << "SWAP mismatch for map: " << maps[i].name;
+        pm_memusage_add(&proc_usage, &map_usage);
+    }
+
+    EXPECT_EQ(proc_usage.swap, proc_mem->Usage().swap);
+}
+
+TEST_F(ValidateProcMemInfo, TestSwapOffsets) {
+    const MemUsage& proc_usage = proc_mem->Usage();
+    const std::vector<uint16_t>& swap_offsets = proc_mem->SwapOffsets();
+
+    EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size());
+}
+
 class ValidateProcMemInfoWss : public ::testing::Test {
   protected:
     void SetUp() override {
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index fe91d25..e383934 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -44,6 +44,8 @@
     to->pss += from.pss;
     to->uss += from.uss;
 
+    to->swap += from.swap;
+
     to->private_clean += from.private_clean;
     to->private_dirty += from.private_dirty;
 
@@ -76,6 +78,10 @@
     return wss_;
 }
 
+const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() const {
+    return swap_offsets_;
+}
+
 bool ProcMemInfo::WssReset() {
     if (!get_wss_) {
         LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
@@ -115,8 +121,8 @@
 
     for (auto& vma : maps_) {
         if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
-            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
-                       << "-" << vma.end << "]";
+            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+                       << vma.end << "]";
             maps_.clear();
             return false;
         }
@@ -153,7 +159,8 @@
         if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
 
         if (PAGE_SWAPPED(p)) {
-            // TODO: do what's needed for swapped pages
+            vma.usage.swap += pagesz;
+            swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(p));
             continue;
         }
 
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
index 50fa213..82605b6 100644
--- a/libmeminfo/sysmeminfo.cpp
+++ b/libmeminfo/sysmeminfo.cpp
@@ -37,9 +37,11 @@
 namespace meminfo {
 
 const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
-    "MemTotal:", "MemFree:",      "Buffers:",     "Cached:",     "Shmem:",
-    "Slab:",     "SReclaimable:", "SUnreclaim:",  "SwapTotal:",  "SwapFree:",
-    "ZRam:",     "Mapped:",       "VmallocUsed:", "PageTables:", "KernelStack:",
+        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,
 };
 
 bool SysMemInfo::ReadMemInfo(const std::string& path) {
@@ -99,6 +101,7 @@
     buffer[len] = '\0';
     char* p = buffer;
     uint32_t found = 0;
+    uint32_t lineno = 0;
     while (*p && found < tags.size()) {
         for (auto& tag : tags) {
             if (strncmp(p, tag.c_str(), tag.size()) == 0) {
@@ -107,7 +110,7 @@
                 char* endptr = nullptr;
                 mem_in_kb_[tag] = strtoull(p, &endptr, 10);
                 if (p == endptr) {
-                    PLOG(ERROR) << "Failed to parse line in file: " << path;
+                    PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
                     return false;
                 }
                 p = endptr;
@@ -119,6 +122,7 @@
             p++;
         }
         if (*p) p++;
+        lineno++;
     }
 
     return true;