diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h index 4cce133ae..0b66074aa 100644 --- a/libmeminfo/include/meminfo/procmeminfo.h +++ b/libmeminfo/include/meminfo/procmeminfo.h @@ -64,12 +64,12 @@ class ProcMemInfo final { // private_dirty // SwapPss // All other fields of MemUsage are zeroed. - bool SmapsOrRollup(bool use_rollup, MemUsage* stats) const; + bool SmapsOrRollup(MemUsage* stats) const; // Used to parse either of /proc//{smaps, smaps_rollup} and record the process's - // Pss. The 'use_rollup' parameter decides which file is to be tried. + // Pss. // Returns 'true' on success and the value of Pss in the out parameter. - bool SmapsOrRollupPss(bool use_rollup, uint64_t* pss) const; + bool SmapsOrRollupPss(uint64_t* pss) const; const std::vector& SwapOffsets(); @@ -94,6 +94,12 @@ class ProcMemInfo final { // same format as /proc//smaps. Returns 'false' if the file is malformed. bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback); +// Returns if the kernel supports /proc//smaps_rollup. Assumes that the +// calling process has access to the /proc//smaps_rollup. +// Returns 'false' if the calling process has no permission to read the file if it exists +// of if the file doesn't exist. +bool IsSmapsRollupSupported(pid_t pid); + // Same as ProcMemInfo::SmapsOrRollup but reads the statistics directly // from a file. The file MUST be in the same format as /proc//smaps // or /proc//smaps_rollup diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp index 796a7d0fe..e689a2679 100644 --- a/libmeminfo/libmeminfo_test.cpp +++ b/libmeminfo/libmeminfo_test.cpp @@ -284,13 +284,22 @@ TEST(TestProcMemInfo, SwapOffsetsEmpty) { EXPECT_EQ(swap_offsets.size(), 0); } +TEST(TestProcMemInfo, IsSmapsSupportedTest) { + std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid); + bool supported = IsSmapsRollupSupported(pid); + EXPECT_EQ(!access(path.c_str(), F_OK | R_OK), supported); + // Second call must return what the first one returned regardless of the pid parameter. + // So, deliberately pass invalid pid. + EXPECT_EQ(supported, IsSmapsRollupSupported(-1)); +} + TEST(TestProcMemInfo, SmapsOrRollupReturn) { // if /proc//smaps_rollup file exists, .SmapsRollup() must return true; // false otherwise std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid); ProcMemInfo proc_mem(pid); MemUsage stats; - EXPECT_EQ(!access(path.c_str(), F_OK), proc_mem.SmapsOrRollup(true, &stats)); + EXPECT_EQ(!access(path.c_str(), F_OK), proc_mem.SmapsOrRollup(&stats)); } TEST(TestProcMemInfo, SmapsOrRollupTest) { diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp index f72d46964..347a293a9 100644 --- a/libmeminfo/procmeminfo.cpp +++ b/libmeminfo/procmeminfo.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -172,15 +173,15 @@ bool ProcMemInfo::ForEachVma(const VmaCallback& callback) { return ForEachVmaFromFile(path, callback); } -bool ProcMemInfo::SmapsOrRollup(bool use_rollup, MemUsage* stats) const { - std::string path = ::android::base::StringPrintf("/proc/%d/%s", pid_, - use_rollup ? "smaps_rollup" : "smaps"); +bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const { + std::string path = ::android::base::StringPrintf( + "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps"); return SmapsOrRollupFromFile(path, stats); -}; +} -bool ProcMemInfo::SmapsOrRollupPss(bool use_rollup, uint64_t* pss) const { - std::string path = ::android::base::StringPrintf("/proc/%d/%s", pid_, - use_rollup ? "smaps_rollup" : "smaps"); +bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const { + std::string path = ::android::base::StringPrintf( + "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps"); return SmapsOrRollupPssFromFile(path, pss); } @@ -374,6 +375,31 @@ bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) { return true; } +enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED }; + +static std::atomic g_rollup_support = UNTRIED; + +bool IsSmapsRollupSupported(pid_t pid) { + // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except + // the method only checks if rollup is supported and returns the status + // right away. + enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed); + if (rollup_support != UNTRIED) { + return rollup_support == SUPPORTED; + } + std::string rollup_file = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid); + if (access(rollup_file.c_str(), F_OK | R_OK)) { + // No check for errno = ENOENT necessary here. The caller MUST fallback to + // using /proc//smaps instead anyway. + g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed); + return false; + } + + g_rollup_support.store(SUPPORTED, std::memory_order_relaxed); + LOG(INFO) << "Using smaps_rollup for pss collection"; + return true; +} + bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) { auto fp = std::unique_ptr{fopen(path.c_str(), "re"), fclose}; if (fp == nullptr) {