libmeminfo: Add SmapsRollup
This adds the tests and SmapsRollup() parsing function in ProcMemInfo. Adds tests to check the return value as well as the correctness. Bug: 111694435 Test: libmeminfo_test 1 --gtest_filter=TestProcMemInfo.* Test: libmeminfo_benchmark --benchmark_filter=BM_SmapsRollup_ Result: ---------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------- BM_SmapsRollup_old 4751 ns 4730 ns 149458 BM_SmapsRollup_new 4858 ns 4837 ns 144636 ---------------------------------------------------------- Change-Id: Ia051fe53a7622e3091502ff7166efafae35e7935 Signed-off-by: Sandeep Patil <sspatil@google.com>
This commit is contained in:
parent
c24f1e3c63
commit
fa2d8d5541
6 changed files with 56543 additions and 5 deletions
|
|
@ -32,6 +32,7 @@ struct MemUsage {
|
|||
uint64_t uss;
|
||||
|
||||
uint64_t swap;
|
||||
uint64_t swap_pss;
|
||||
|
||||
uint64_t private_clean;
|
||||
uint64_t private_dirty;
|
||||
|
|
@ -44,6 +45,7 @@ struct MemUsage {
|
|||
pss(0),
|
||||
uss(0),
|
||||
swap(0),
|
||||
swap_pss(0),
|
||||
private_clean(0),
|
||||
private_dirty(0),
|
||||
shared_clean(0),
|
||||
|
|
@ -52,7 +54,7 @@ struct MemUsage {
|
|||
~MemUsage() = default;
|
||||
|
||||
void clear() {
|
||||
vss = rss = pss = uss = swap = 0;
|
||||
vss = rss = pss = uss = swap = swap_pss = 0;
|
||||
private_clean = private_dirty = shared_clean = shared_dirty = 0;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,6 +37,22 @@ class ProcMemInfo final {
|
|||
const std::vector<Vma>& Maps();
|
||||
const MemUsage& Usage();
|
||||
const MemUsage& Wss();
|
||||
|
||||
// Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
|
||||
// Pss and Private memory usage in 'stats'. In particular, the method only populates the fields
|
||||
// of the MemUsage structure that are intended to be used by Android's periodic Pss collection.
|
||||
//
|
||||
// The method populates the following statistics in order to be fast an efficient.
|
||||
// Pss
|
||||
// Rss
|
||||
// Uss
|
||||
// private_clean
|
||||
// private_dirty
|
||||
// SwapPss
|
||||
//
|
||||
// All other fields of MemUsage are zeroed.
|
||||
bool SmapsOrRollup(bool use_rollup, MemUsage* stats) const;
|
||||
|
||||
const std::vector<uint16_t>& SwapOffsets();
|
||||
|
||||
~ProcMemInfo() = default;
|
||||
|
|
@ -57,5 +73,10 @@ class ProcMemInfo final {
|
|||
std::vector<uint16_t> swap_offsets_;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
} // namespace meminfo
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <meminfo/procmeminfo.h>
|
||||
#include <meminfo/sysmeminfo.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
|
@ -31,6 +32,9 @@
|
|||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
using ::android::meminfo::MemUsage;
|
||||
using ::android::meminfo::ProcMemInfo;
|
||||
using ::android::meminfo::SmapsOrRollupFromFile;
|
||||
using ::android::meminfo::SysMemInfo;
|
||||
|
||||
enum {
|
||||
|
|
@ -457,4 +461,85 @@ static void BM_VmallocInfo_new(benchmark::State& state) {
|
|||
}
|
||||
BENCHMARK(BM_VmallocInfo_new);
|
||||
|
||||
// This implementation is picked up as-is from frameworks/base/core/jni/android_os_Debug.cpp
|
||||
// and only slightly modified to use std:unique_ptr.
|
||||
static bool get_smaps_rollup(const std::string path, MemUsage* rollup) {
|
||||
char lineBuffer[1024];
|
||||
auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
|
||||
if (fp != nullptr) {
|
||||
char* line;
|
||||
while (true) {
|
||||
if (fgets(lineBuffer, sizeof(lineBuffer), fp.get()) == NULL) {
|
||||
break;
|
||||
}
|
||||
line = lineBuffer;
|
||||
|
||||
switch (line[0]) {
|
||||
case 'P':
|
||||
if (strncmp(line, "Pss:", 4) == 0) {
|
||||
char* c = line + 4;
|
||||
while (*c != 0 && (*c < '0' || *c > '9')) {
|
||||
c++;
|
||||
}
|
||||
rollup->pss += atoi(c);
|
||||
} else if (strncmp(line, "Private_Clean:", 14) == 0 ||
|
||||
strncmp(line, "Private_Dirty:", 14) == 0) {
|
||||
char* c = line + 14;
|
||||
while (*c != 0 && (*c < '0' || *c > '9')) {
|
||||
c++;
|
||||
}
|
||||
rollup->uss += atoi(c);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
if (strncmp(line, "Rss:", 4) == 0) {
|
||||
char* c = line + 4;
|
||||
while (*c != 0 && (*c < '0' || *c > '9')) {
|
||||
c++;
|
||||
}
|
||||
rollup->rss += atoi(c);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (strncmp(line, "SwapPss:", 8) == 0) {
|
||||
char* c = line + 8;
|
||||
long lSwapPss;
|
||||
while (*c != 0 && (*c < '0' || *c > '9')) {
|
||||
c++;
|
||||
}
|
||||
lSwapPss = atoi(c);
|
||||
rollup->swap_pss += lSwapPss;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void BM_SmapsRollup_old(benchmark::State& state) {
|
||||
std::string exec_dir = ::android::base::GetExecutableDirectory();
|
||||
std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
|
||||
for (auto _ : state) {
|
||||
MemUsage stats;
|
||||
CHECK_EQ(get_smaps_rollup(path, &stats), true);
|
||||
CHECK_EQ(stats.pss, 108384);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_SmapsRollup_old);
|
||||
|
||||
static void BM_SmapsRollup_new(benchmark::State& state) {
|
||||
std::string exec_dir = ::android::base::GetExecutableDirectory();
|
||||
std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
|
||||
for (auto _ : state) {
|
||||
MemUsage stats;
|
||||
CHECK_EQ(SmapsOrRollupFromFile(path, &stats), true);
|
||||
CHECK_EQ(stats.pss, 108384);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_SmapsRollup_new);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
|
|
|||
|
|
@ -246,13 +246,13 @@ TEST_F(ValidatePageAcct, TestPageIdle) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, TestMapsEmpty) {
|
||||
TEST(TestProcMemInfo, MapsEmpty) {
|
||||
ProcMemInfo proc_mem(pid);
|
||||
const std::vector<Vma>& maps = proc_mem.Maps();
|
||||
EXPECT_GT(maps.size(), 0);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, TestUsageEmpty) {
|
||||
TEST(TestProcMemInfo, UsageEmpty) {
|
||||
// If we created the object for getting working set,
|
||||
// the usage must be empty
|
||||
ProcMemInfo proc_mem(pid, true);
|
||||
|
|
@ -264,7 +264,7 @@ TEST(TestProcMemInfo, TestUsageEmpty) {
|
|||
EXPECT_EQ(usage.swap, 0);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfoWssReset, TestWssEmpty) {
|
||||
TEST(TestProcMemInfo, WssEmpty) {
|
||||
// If we created the object for getting usage,
|
||||
// the working set must be empty
|
||||
ProcMemInfo proc_mem(pid, false);
|
||||
|
|
@ -276,7 +276,7 @@ TEST(TestProcMemInfoWssReset, TestWssEmpty) {
|
|||
EXPECT_EQ(wss.swap, 0);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfoWssReset, TestSwapOffsetsEmpty) {
|
||||
TEST(TestProcMemInfo, SwapOffsetsEmpty) {
|
||||
// If we created the object for getting working set,
|
||||
// the swap offsets must be empty
|
||||
ProcMemInfo proc_mem(pid, true);
|
||||
|
|
@ -284,6 +284,87 @@ TEST(TestProcMemInfoWssReset, TestSwapOffsetsEmpty) {
|
|||
EXPECT_EQ(swap_offsets.size(), 0);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, SmapsOrRollupTest) {
|
||||
std::string rollup =
|
||||
R"rollup(12c00000-7fe859e000 ---p 00000000 00:00 0 [rollup]
|
||||
Rss: 331908 kB
|
||||
Pss: 202052 kB
|
||||
Shared_Clean: 158492 kB
|
||||
Shared_Dirty: 18928 kB
|
||||
Private_Clean: 90472 kB
|
||||
Private_Dirty: 64016 kB
|
||||
Referenced: 318700 kB
|
||||
Anonymous: 81984 kB
|
||||
AnonHugePages: 0 kB
|
||||
Shared_Hugetlb: 0 kB
|
||||
Private_Hugetlb: 0 kB
|
||||
Swap: 5344 kB
|
||||
SwapPss: 442 kB
|
||||
Locked: 1523537 kB)rollup";
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(::android::base::WriteStringToFd(rollup, tf.fd));
|
||||
|
||||
MemUsage stats;
|
||||
ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
|
||||
EXPECT_EQ(stats.rss, 331908);
|
||||
EXPECT_EQ(stats.pss, 202052);
|
||||
EXPECT_EQ(stats.uss, 154488);
|
||||
EXPECT_EQ(stats.private_clean, 90472);
|
||||
EXPECT_EQ(stats.private_dirty, 64016);
|
||||
EXPECT_EQ(stats.swap_pss, 442);
|
||||
}
|
||||
|
||||
TEST(TestProcMemInfo, SmapsOrRollupSmapsTest) {
|
||||
// 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));
|
||||
|
||||
MemUsage stats;
|
||||
ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
|
||||
EXPECT_EQ(stats.rss, 2652);
|
||||
EXPECT_EQ(stats.pss, 2652);
|
||||
EXPECT_EQ(stats.uss, 2736);
|
||||
EXPECT_EQ(stats.private_clean, 84);
|
||||
EXPECT_EQ(stats.private_dirty, 2652);
|
||||
EXPECT_EQ(stats.swap_pss, 70);
|
||||
}
|
||||
|
||||
TEST(ValidateProcMemInfoFlags, TestPageFlags1) {
|
||||
// Create proc object using libpagemap
|
||||
pm_kernel_t* ker;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
|
|
@ -102,6 +103,12 @@ const MemUsage& ProcMemInfo::Wss() {
|
|||
return wss_;
|
||||
}
|
||||
|
||||
bool ProcMemInfo::SmapsOrRollup(bool use_rollup, MemUsage* stats) const {
|
||||
std::string path = ::android::base::StringPrintf("/proc/%d/%s", pid_,
|
||||
use_rollup ? "smaps_rollup" : "smaps");
|
||||
return SmapsOrRollupFromFile(path, stats);
|
||||
};
|
||||
|
||||
const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
|
||||
if (get_wss_) {
|
||||
LOG(WARNING) << "Trying to read process swap offsets for " << pid_
|
||||
|
|
@ -252,5 +259,50 @@ bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Public APIs
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
stats->clear();
|
||||
while (fgets(line, sizeof(line), fp.get()) != nullptr) {
|
||||
switch (line[0]) {
|
||||
case 'P':
|
||||
if (strncmp(line, "Pss:", 4) == 0) {
|
||||
char* c = line + 4;
|
||||
stats->pss += strtoull(c, nullptr, 10);
|
||||
} else if (strncmp(line, "Private_Clean:", 14) == 0) {
|
||||
char* c = line + 14;
|
||||
uint64_t prcl = strtoull(c, nullptr, 10);
|
||||
stats->private_clean += prcl;
|
||||
stats->uss += prcl;
|
||||
} else if (strncmp(line, "Private_Dirty:", 14) == 0) {
|
||||
char* c = line + 14;
|
||||
uint64_t prdi = strtoull(c, nullptr, 10);
|
||||
stats->private_dirty += prdi;
|
||||
stats->uss += prdi;
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
if (strncmp(line, "Rss:", 4) == 0) {
|
||||
char* c = line + 4;
|
||||
stats->rss += strtoull(c, nullptr, 10);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (strncmp(line, "SwapPss:", 8) == 0) {
|
||||
char* c = line + 8;
|
||||
stats->swap_pss += strtoull(c, nullptr, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace meminfo
|
||||
} // namespace android
|
||||
|
|
|
|||
56297
libmeminfo/testdata1/smaps
Normal file
56297
libmeminfo/testdata1/smaps
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue