From cbc8f123d88956dde3c2b649fbe9702dc204ee9c Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Mon, 21 Jan 2019 16:47:24 -0800 Subject: [PATCH] meminfo: Add API to read pagemap for a vma within a process. .. and make sure we have some helper methods exposed to the clients to do some basic checks for the pagemap values. For example, to check if the page is present or swapped. Bug: 111694435 Test: libmeminfo_test 1 --gtest_filter=ValidateProcMemInfo.TestPageMap Test: libmeminfo_test 1 Change-Id: Ic6ae91f4214b42346f3d0b54164a43ac79d5ade1 Signed-off-by: Sandeep Patil --- libmeminfo/include/meminfo/pageacct.h | 12 +++++++++++ libmeminfo/include/meminfo/procmeminfo.h | 7 ++++++ libmeminfo/libmeminfo_test.cpp | 17 +++++++++++++++ libmeminfo/pageacct.cpp | 13 ++++++++++++ libmeminfo/procmeminfo.cpp | 27 ++++++++++++++++++++++++ 5 files changed, 76 insertions(+) diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h index 8ddaef244..8483d8434 100644 --- a/libmeminfo/include/meminfo/pageacct.h +++ b/libmeminfo/include/meminfo/pageacct.h @@ -65,5 +65,17 @@ class PageAcct final { ::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 0b66074aa..95e50539f 100644 --- a/libmeminfo/include/meminfo/procmeminfo.h +++ b/libmeminfo/include/meminfo/procmeminfo.h @@ -73,6 +73,13 @@ class ProcMemInfo final { const std::vector& SwapOffsets(); + // Reads /proc//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* pagemap); + ~ProcMemInfo() = default; private: diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp index 7d85dd239..0e0212fac 100644 --- a/libmeminfo/libmeminfo_test.cpp +++ b/libmeminfo/libmeminfo_test.cpp @@ -121,6 +121,23 @@ TEST_F(ValidateProcMemInfo, TestSwapOffsets) { EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size()); } +TEST_F(ValidateProcMemInfo, TestPageMap) { + std::vector 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 887a74d8b..0a26c0818 100644 --- a/libmeminfo/pageacct.cpp +++ b/libmeminfo/pageacct.cpp @@ -138,5 +138,18 @@ int PageAcct::GetPageIdle(uint64_t pfn) const { 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 d6332a32e..1df03b22e 100644 --- a/libmeminfo/procmeminfo.cpp +++ b/libmeminfo/procmeminfo.cpp @@ -199,6 +199,33 @@ const std::vector& ProcMemInfo::SwapOffsets() { return swap_offsets_; } +bool ProcMemInfo::PageMap(const Vma& vma, std::vector* 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//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