Merge "meminfo: Add API to read pagemap for a vma within a process."

This commit is contained in:
Sandeep Patil 2019-01-22 22:08:24 +00:00 committed by Gerrit Code Review
commit 15a3a287f2
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