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