Merge "meminfo: Add API to read pagemap for a vma within a process." am: 15a3a287f2
am: 8f688f5b2d

Change-Id: I4af15538624a777472022ac7a941a69a55d21f7e
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
index 8ddaef2..8483d84 100644
--- a/libmeminfo/include/meminfo/pageacct.h
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -65,5 +65,17 @@
     ::android::base::unique_fd pageidle_fd_;
 };
 
+// Returns if the page present bit is set in the value
+// passed in.
+bool page_present(uint64_t pagemap_val);
+
+// Returns if the page swapped bit is set in the value
+// passed in.
+bool page_swapped(uint64_t pagemap_val);
+
+// Returns the page frame number (physical page) from
+// pagemap value
+uint64_t page_pfn(uint64_t pagemap_val);
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index 0b66074..95e5053 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -73,6 +73,13 @@
 
     const std::vector<uint16_t>& SwapOffsets();
 
+    // Reads /proc/<pid>/pagemap for this process for each page within
+    // the 'vma' and stores that in 'pagemap'. It is assumed that the 'vma'
+    // is obtained by calling Maps() or 'ForEachVma' for the same object. No special checks
+    // are made to see if 'vma' is *valid*.
+    // Returns false if anything goes wrong, 'true' otherwise.
+    bool PageMap(const Vma& vma, std::vector<uint64_t>* pagemap);
+
     ~ProcMemInfo() = default;
 
   private:
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 7d85dd2..0e0212f 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -121,6 +121,23 @@
     EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size());
 }
 
+TEST_F(ValidateProcMemInfo, TestPageMap) {
+    std::vector<uint64_t> pagemap;
+
+    auto vma_callback = [&](const Vma& vma) {
+        uint64_t* pmap_out;
+        size_t len;
+        ASSERT_EQ(0, pm_process_pagemap_range(proc, vma.start, vma.end, &pmap_out, &len));
+        ASSERT_TRUE(proc_mem->PageMap(vma, &pagemap));
+
+        EXPECT_EQ(len, ((vma.end - vma.start) / getpagesize()));
+        for (size_t i = 0; i < len; i++) {
+            EXPECT_EQ(pmap_out[i], pagemap[i]);
+        }
+    };
+    ASSERT_TRUE(proc_mem->ForEachVma(vma_callback));
+}
+
 class ValidateProcMemInfoWss : public ::testing::Test {
   protected:
     void SetUp() override {
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
index 887a74d..0a26c08 100644
--- a/libmeminfo/pageacct.cpp
+++ b/libmeminfo/pageacct.cpp
@@ -138,5 +138,18 @@
     return !!(idle_bits & (1ULL << (pfn % 64)));
 }
 
+// Public methods
+bool page_present(uint64_t pagemap_val) {
+    return PAGE_PRESENT(pagemap_val);
+}
+
+bool page_swapped(uint64_t pagemap_val) {
+    return PAGE_SWAPPED(pagemap_val);
+}
+
+uint64_t page_pfn(uint64_t pagemap_val) {
+    return PAGE_PFN(pagemap_val);
+}
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index d6332a3..1df03b2 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -199,6 +199,33 @@
     return swap_offsets_;
 }
 
+bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
+    pagemap->clear();
+    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;
+        return false;
+    }
+
+    uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
+    pagemap->reserve(nr_pages);
+
+    uint64_t idx = vma.start / getpagesize();
+    uint64_t last = idx + nr_pages;
+    uint64_t val;
+    for (; idx < last; idx++) {
+        if (pread64(pagemap_fd, &val, sizeof(uint64_t), idx * sizeof(uint64_t)) < 0) {
+            PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+            return false;
+        }
+        pagemap->emplace_back(val);
+    }
+
+    return true;
+}
+
 bool ProcMemInfo::ReadMaps(bool get_wss) {
     // 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