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 <sspatil@google.com>
This commit is contained in:
Sandeep Patil 2019-01-21 16:47:24 -08:00
parent 380995adc7
commit cbc8f123d8
5 changed files with 76 additions and 0 deletions

View File

@ -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

View File

@ -73,6 +73,13 @@ class ProcMemInfo final {
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:

View File

@ -121,6 +121,23 @@ TEST_F(ValidateProcMemInfo, TestSwapOffsets) {
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 {

View File

@ -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

View File

@ -199,6 +199,33 @@ const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
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