Merge changes from topic "meminfo-apis"
* changes: meminfo: Add IsSmapsRollupSupported Api meminfo: Add SmapsOrRollupPss meminfo: Remove unnecessary working set stats meminfo: Fix ProcMemInfo ForEachVmaFromFile
This commit is contained in:
commit
e16c2ce649
6 changed files with 153 additions and 50 deletions
|
@ -72,15 +72,10 @@ struct Vma {
|
|||
: start(s), end(e), offset(off), flags(f), name(n) {}
|
||||
~Vma() = default;
|
||||
|
||||
void clear() {
|
||||
memset(&usage, 0, sizeof(usage));
|
||||
memset(&wss, 0, sizeof(wss));
|
||||
}
|
||||
void clear() { memset(&usage, 0, sizeof(usage)); }
|
||||
|
||||
// Memory usage of this mapping.
|
||||
MemUsage usage;
|
||||
// Working set within this mapping.
|
||||
MemUsage wss;
|
||||
};
|
||||
|
||||
} // namespace meminfo
|
||||
|
|
|
@ -64,7 +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/<pid>/{smaps, smaps_rollup} and record the process's
|
||||
// Pss.
|
||||
// Returns 'true' on success and the value of Pss in the out parameter.
|
||||
bool SmapsOrRollupPss(uint64_t* pss) const;
|
||||
|
||||
const std::vector<uint16_t>& SwapOffsets();
|
||||
|
||||
|
@ -82,7 +87,6 @@ class ProcMemInfo final {
|
|||
std::vector<Vma> maps_;
|
||||
|
||||
MemUsage usage_;
|
||||
MemUsage wss_;
|
||||
std::vector<uint16_t> swap_offsets_;
|
||||
};
|
||||
|
||||
|
@ -90,10 +94,21 @@ class ProcMemInfo final {
|
|||
// same format as /proc/<pid>/smaps. Returns 'false' if the file is malformed.
|
||||
bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback);
|
||||
|
||||
// Returns if the kernel supports /proc/<pid>/smaps_rollup. Assumes that the
|
||||
// calling process has access to the /proc/<pid>/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/<pid>/smaps
|
||||
// or /proc/<pid>/smaps_rollup
|
||||
bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats);
|
||||
|
||||
// Same as ProcMemInfo::SmapsOrRollupPss but reads the statistics directly
|
||||
// from a file and returns total Pss in kB. The file MUST be in the same format
|
||||
// as /proc/<pid>/smaps or /proc/<pid>/smaps_rollup
|
||||
bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss);
|
||||
|
||||
} // namespace meminfo
|
||||
} // namespace android
|
||||
|
|
|
@ -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/<pid>/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) {
|
||||
|
@ -365,6 +374,50 @@ VmFlags: rd wr mr mw me ac
|
|||
EXPECT_EQ(stats.swap_pss, 70);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, SmapsOrRollupPssRollupTest) {
|
||||
// This is a made up smaps for the test
|
||||
std::string smaps =
|
||||
R"smaps(12c00000-13440000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
|
||||
Name: [anon:dalvik-main space (region space)]
|
||||
Size: 8448 kB
|
||||
KernelPageSize: 4 kB
|
||||
MMUPageSize: 4 kB
|
||||
Rss: 2652 kB
|
||||
Pss: 2652 kB
|
||||
Shared_Clean: 840 kB
|
||||
Shared_Dirty: 40 kB
|
||||
Private_Clean: 84 kB
|
||||
Private_Dirty: 2652 kB
|
||||
Referenced: 2652 kB
|
||||
Anonymous: 2652 kB
|
||||
AnonHugePages: 0 kB
|
||||
ShmemPmdMapped: 0 kB
|
||||
Shared_Hugetlb: 0 kB
|
||||
Private_Hugetlb: 0 kB
|
||||
Swap: 102 kB
|
||||
SwapPss: 70 kB
|
||||
Locked: 2652 kB
|
||||
VmFlags: rd wr mr mw me ac
|
||||
)smaps";
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
|
||||
|
||||
uint64_t pss;
|
||||
ASSERT_EQ(SmapsOrRollupPssFromFile(tf.path, &pss), true);
|
||||
EXPECT_EQ(pss, 2652);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, SmapsOrRollupPssSmapsTest) {
|
||||
std::string exec_dir = ::android::base::GetExecutableDirectory();
|
||||
std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
|
||||
|
||||
uint64_t pss;
|
||||
ASSERT_EQ(SmapsOrRollupPssFromFile(path, &pss), true);
|
||||
EXPECT_EQ(pss, 19119);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, ForEachVmaFromFileTest) {
|
||||
std::string exec_dir = ::android::base::GetExecutableDirectory();
|
||||
std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
|
||||
|
@ -374,6 +427,9 @@ TEST(TestProcMemInfo, ForEachVmaFromFileTest) {
|
|||
auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
|
||||
ASSERT_TRUE(ForEachVmaFromFile(path, collect_vmas));
|
||||
|
||||
// We should get a total of 6 vmas
|
||||
ASSERT_EQ(vmas.size(), 6);
|
||||
|
||||
// Expect values to be equal to what we have in testdata1/smaps_short
|
||||
// Check for sizes first
|
||||
ASSERT_EQ(vmas[0].usage.vss, 32768);
|
||||
|
@ -468,6 +524,8 @@ TEST(TestProcMemInfo, SmapsTest) {
|
|||
auto vmas = proc_mem.Smaps(path);
|
||||
|
||||
ASSERT_FALSE(vmas.empty());
|
||||
// We should get a total of 6 vmas
|
||||
ASSERT_EQ(vmas.size(), 6);
|
||||
|
||||
// Expect values to be equal to what we have in testdata1/smaps_short
|
||||
// Check for sizes first
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
@ -157,14 +158,14 @@ const MemUsage& ProcMemInfo::Wss() {
|
|||
if (!get_wss_) {
|
||||
LOG(WARNING) << "Trying to read process working set for " << pid_
|
||||
<< " using invalid object";
|
||||
return wss_;
|
||||
return usage_;
|
||||
}
|
||||
|
||||
if (maps_.empty() && !ReadMaps(get_wss_)) {
|
||||
LOG(ERROR) << "Failed to get working set for Process " << pid_;
|
||||
}
|
||||
|
||||
return wss_;
|
||||
return usage_;
|
||||
}
|
||||
|
||||
bool ProcMemInfo::ForEachVma(const VmaCallback& callback) {
|
||||
|
@ -172,11 +173,17 @@ 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(uint64_t* pss) const {
|
||||
std::string path = ::android::base::StringPrintf(
|
||||
"/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
|
||||
return SmapsOrRollupPssFromFile(path, pss);
|
||||
}
|
||||
|
||||
const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
|
||||
if (get_wss_) {
|
||||
|
@ -228,11 +235,7 @@ bool ProcMemInfo::ReadMaps(bool get_wss) {
|
|||
maps_.clear();
|
||||
return false;
|
||||
}
|
||||
if (get_wss) {
|
||||
add_mem_usage(&wss_, vma.wss);
|
||||
} else {
|
||||
add_mem_usage(&usage_, vma.usage);
|
||||
}
|
||||
add_mem_usage(&usage_, vma.usage);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -300,31 +303,20 @@ bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
|
|||
// This effectively makes vss = rss for the working set is requested.
|
||||
// The libpagemap implementation returns vss > rss for
|
||||
// working set, which doesn't make sense.
|
||||
vma.wss.vss += pagesz;
|
||||
vma.wss.rss += pagesz;
|
||||
vma.wss.uss += is_private ? pagesz : 0;
|
||||
vma.wss.pss += pagesz / pg_counts[i];
|
||||
if (is_private) {
|
||||
vma.wss.private_dirty += is_dirty ? pagesz : 0;
|
||||
vma.wss.private_clean += is_dirty ? 0 : pagesz;
|
||||
} else {
|
||||
vma.wss.shared_dirty += is_dirty ? pagesz : 0;
|
||||
vma.wss.shared_clean += is_dirty ? 0 : pagesz;
|
||||
}
|
||||
vma.usage.vss += pagesz;
|
||||
}
|
||||
|
||||
vma.usage.rss += pagesz;
|
||||
vma.usage.uss += is_private ? pagesz : 0;
|
||||
vma.usage.pss += pagesz / pg_counts[i];
|
||||
if (is_private) {
|
||||
vma.usage.private_dirty += is_dirty ? pagesz : 0;
|
||||
vma.usage.private_clean += is_dirty ? 0 : pagesz;
|
||||
} else {
|
||||
vma.usage.rss += pagesz;
|
||||
vma.usage.uss += is_private ? pagesz : 0;
|
||||
vma.usage.pss += pagesz / pg_counts[i];
|
||||
if (is_private) {
|
||||
vma.usage.private_dirty += is_dirty ? pagesz : 0;
|
||||
vma.usage.private_clean += is_dirty ? 0 : pagesz;
|
||||
} else {
|
||||
vma.usage.shared_dirty += is_dirty ? pagesz : 0;
|
||||
vma.usage.shared_clean += is_dirty ? 0 : pagesz;
|
||||
}
|
||||
vma.usage.shared_dirty += is_dirty ? pagesz : 0;
|
||||
vma.usage.shared_clean += is_dirty ? 0 : pagesz;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -338,8 +330,9 @@ bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) {
|
|||
char* line = nullptr;
|
||||
bool parsing_vma = false;
|
||||
ssize_t line_len;
|
||||
size_t line_alloc = 0;
|
||||
Vma vma;
|
||||
while ((line_len = getline(&line, 0, fp.get())) > 0) {
|
||||
while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
|
||||
// Make sure the line buffer terminates like a C string for ReadMapFile
|
||||
line[line_len] = '\0';
|
||||
|
||||
|
@ -382,6 +375,31 @@ bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) {
|
|||
return true;
|
||||
}
|
||||
|
||||
enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
|
||||
|
||||
static std::atomic<smaps_rollup_support> 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/<pid>/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<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
|
||||
if (fp == nullptr) {
|
||||
|
@ -426,5 +444,22 @@ bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
|
||||
auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*pss = 0;
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), fp.get()) != nullptr) {
|
||||
uint64_t v;
|
||||
if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
|
||||
*pss += v;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace meminfo
|
||||
} // namespace android
|
||||
|
|
|
@ -98,7 +98,7 @@ static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
|
|||
std::stringstream ss;
|
||||
print_header(ss);
|
||||
for (auto& vma : maps) {
|
||||
const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
|
||||
const MemUsage& vma_stats = vma.usage;
|
||||
if (hide_zeroes && vma_stats.rss == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -116,14 +116,14 @@ static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
|
|||
int main(int argc, char* argv[]) {
|
||||
int opt;
|
||||
auto pss_sort = [](const Vma& a, const Vma& b) {
|
||||
uint64_t pss_a = show_wss ? a.wss.pss : a.usage.pss;
|
||||
uint64_t pss_b = show_wss ? b.wss.pss : b.usage.pss;
|
||||
uint64_t pss_a = a.usage.pss;
|
||||
uint64_t pss_b = b.usage.pss;
|
||||
return pss_a > pss_b;
|
||||
};
|
||||
|
||||
auto uss_sort = [](const Vma& a, const Vma& b) {
|
||||
uint64_t uss_a = show_wss ? a.wss.uss : a.usage.uss;
|
||||
uint64_t uss_b = show_wss ? b.wss.uss : b.usage.uss;
|
||||
uint64_t uss_a = a.usage.uss;
|
||||
uint64_t uss_b = b.usage.uss;
|
||||
return uss_a > uss_b;
|
||||
};
|
||||
|
||||
|
@ -182,7 +182,7 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
ProcMemInfo proc(pid, show_wss);
|
||||
const MemUsage& proc_stats = show_wss ? proc.Wss() : proc.Usage();
|
||||
const MemUsage& proc_stats = proc.Usage();
|
||||
std::vector<Vma> maps(proc.Maps());
|
||||
if (sort_func != nullptr) {
|
||||
std::sort(maps.begin(), maps.end(), sort_func);
|
||||
|
|
|
@ -465,7 +465,7 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
// Skip processes with no memory mappings
|
||||
uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
|
||||
uint64_t vss = proc.Usage().vss;
|
||||
if (vss == 0) return true;
|
||||
|
||||
// collect swap_offset counts from all processes in 1st pass
|
||||
|
|
Loading…
Reference in a new issue