From 23f2a0b166dc542bd40da80c9680bfccee96cc85 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 10 Oct 2019 13:25:00 -0700 Subject: [PATCH] Add method to get usage stats for a single map. The new scudo allocator creates a huge map in 64 bit address space. Some tests would get the usage stats of all maps, but only really cared about a small set of maps. In this case, provide a method to only get the usage stats for the maps you care about. Test: Unit tests pass. Change-Id: Ie845e47a8c789a57bf689c9f0e5878a921640e30 --- libmeminfo/include/meminfo/procmeminfo.h | 4 ++++ libmeminfo/libmeminfo_test.cpp | 27 +++++++++++++++++++++ libmeminfo/procmeminfo.cpp | 30 ++++++++++++++++++++---- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h index f782ec5fb..8c1280ff3 100644 --- a/libmeminfo/include/meminfo/procmeminfo.h +++ b/libmeminfo/include/meminfo/procmeminfo.h @@ -48,6 +48,10 @@ class ProcMemInfo final { // Same as Maps() except, do not read the usage stats for each map. const std::vector& MapsWithoutUsageStats(); + // If MapsWithoutUsageStats was called, this function will fill in + // usage stats for this single vma. + bool FillInVmaStats(Vma& vma); + // Collect all 'vma' or 'maps' from /proc//smaps and store them in 'maps_'. Returns a // constant reference to the vma vector after the collection is done. // diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp index cf5341d14..378a4cd4a 100644 --- a/libmeminfo/libmeminfo_test.cpp +++ b/libmeminfo/libmeminfo_test.cpp @@ -101,6 +101,33 @@ TEST(ProcMemInfo, MapsUsageEmpty) { } } +TEST(ProcMemInfo, MapsUsageFillInLater) { + ProcMemInfo proc_mem(pid); + const std::vector& maps = proc_mem.MapsWithoutUsageStats(); + EXPECT_FALSE(maps.empty()); + for (auto& map : maps) { + Vma update_map(map); + ASSERT_EQ(map.start, update_map.start); + ASSERT_EQ(map.end, update_map.end); + ASSERT_EQ(map.offset, update_map.offset); + ASSERT_EQ(map.flags, update_map.flags); + ASSERT_EQ(map.name, update_map.name); + ASSERT_EQ(0, update_map.usage.vss); + ASSERT_EQ(0, update_map.usage.rss); + ASSERT_EQ(0, update_map.usage.pss); + ASSERT_EQ(0, update_map.usage.uss); + ASSERT_EQ(0, update_map.usage.swap); + ASSERT_EQ(0, update_map.usage.swap_pss); + ASSERT_EQ(0, update_map.usage.private_clean); + ASSERT_EQ(0, update_map.usage.private_dirty); + ASSERT_EQ(0, update_map.usage.shared_clean); + ASSERT_EQ(0, update_map.usage.shared_dirty); + ASSERT_TRUE(proc_mem.FillInVmaStats(update_map)); + // Check that at least one usage stat was updated. + ASSERT_NE(0, update_map.usage.vss); + } +} + TEST(ProcMemInfo, PageMapPresent) { static constexpr size_t kNumPages = 20; size_t pagesize = getpagesize(); diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp index 6f68ab464..9e9a70551 100644 --- a/libmeminfo/procmeminfo.cpp +++ b/libmeminfo/procmeminfo.cpp @@ -244,6 +244,15 @@ bool ProcMemInfo::PageMap(const Vma& vma, std::vector* pagemap) { return true; } +static int GetPagemapFd(pid_t pid) { + std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid); + int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << pagemap_file; + } + return fd; +} + bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) { // 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 @@ -269,11 +278,8 @@ bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats return true; } - 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; + ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_)); + if (pagemap_fd == -1) { return false; } @@ -290,6 +296,20 @@ bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats return true; } +bool ProcMemInfo::FillInVmaStats(Vma& vma) { + ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_)); + if (pagemap_fd == -1) { + return false; + } + + if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false)) { + LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-" + << vma.end << "]"; + return false; + } + return true; +} + bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) { PageAcct& pinfo = PageAcct::Instance(); if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {