Add method to get usage stats for a single map.

The new scudo allocator creates a huge map in 64 bit address space. Some
tests would get the usage stats of all maps, but only really cared about
a small set of maps. In this case, provide a method to only get the usage
stats for the maps you care about.

Test: Unit tests pass.
Change-Id: Ie845e47a8c789a57bf689c9f0e5878a921640e30
diff --git a/include/meminfo/procmeminfo.h b/include/meminfo/procmeminfo.h
index f782ec5..8c1280f 100644
--- a/include/meminfo/procmeminfo.h
+++ b/include/meminfo/procmeminfo.h
@@ -48,6 +48,10 @@
     // Same as Maps() except, do not read the usage stats for each map.
     const std::vector<Vma>& MapsWithoutUsageStats();
 
+    // If MapsWithoutUsageStats was called, this function will fill in
+    // usage stats for this single vma.
+    bool FillInVmaStats(Vma& vma);
+
     // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
     // constant reference to the vma vector after the collection is done.
     //
diff --git a/libmeminfo_test.cpp b/libmeminfo_test.cpp
index cf5341d..378a4cd 100644
--- a/libmeminfo_test.cpp
+++ b/libmeminfo_test.cpp
@@ -101,6 +101,33 @@
     }
 }
 
+TEST(ProcMemInfo, MapsUsageFillInLater) {
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+    EXPECT_FALSE(maps.empty());
+    for (auto& map : maps) {
+        Vma update_map(map);
+        ASSERT_EQ(map.start, update_map.start);
+        ASSERT_EQ(map.end, update_map.end);
+        ASSERT_EQ(map.offset, update_map.offset);
+        ASSERT_EQ(map.flags, update_map.flags);
+        ASSERT_EQ(map.name, update_map.name);
+        ASSERT_EQ(0, update_map.usage.vss);
+        ASSERT_EQ(0, update_map.usage.rss);
+        ASSERT_EQ(0, update_map.usage.pss);
+        ASSERT_EQ(0, update_map.usage.uss);
+        ASSERT_EQ(0, update_map.usage.swap);
+        ASSERT_EQ(0, update_map.usage.swap_pss);
+        ASSERT_EQ(0, update_map.usage.private_clean);
+        ASSERT_EQ(0, update_map.usage.private_dirty);
+        ASSERT_EQ(0, update_map.usage.shared_clean);
+        ASSERT_EQ(0, update_map.usage.shared_dirty);
+        ASSERT_TRUE(proc_mem.FillInVmaStats(update_map));
+        // Check that at least one usage stat was updated.
+        ASSERT_NE(0, update_map.usage.vss);
+    }
+}
+
 TEST(ProcMemInfo, PageMapPresent) {
     static constexpr size_t kNumPages = 20;
     size_t pagesize = getpagesize();
diff --git a/procmeminfo.cpp b/procmeminfo.cpp
index 6f68ab4..9e9a705 100644
--- a/procmeminfo.cpp
+++ b/procmeminfo.cpp
@@ -244,6 +244,15 @@
     return true;
 }
 
+static int GetPagemapFd(pid_t pid) {
+    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid);
+    int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << pagemap_file;
+    }
+    return fd;
+}
+
 bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
     // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
     // running for the lifetime of the system can recycle the objects and don't have to
@@ -269,11 +278,8 @@
         return true;
     }
 
-    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
-    ::android::base::unique_fd pagemap_fd(
-            TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (pagemap_fd < 0) {
-        PLOG(ERROR) << "Failed to open " << pagemap_file;
+    ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
+    if (pagemap_fd == -1) {
         return false;
     }
 
@@ -290,6 +296,20 @@
     return true;
 }
 
+bool ProcMemInfo::FillInVmaStats(Vma& vma) {
+    ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
+    if (pagemap_fd == -1) {
+        return false;
+    }
+
+    if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false)) {
+        LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+                   << vma.end << "]";
+        return false;
+    }
+    return true;
+}
+
 bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
     PageAcct& pinfo = PageAcct::Instance();
     if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {