Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa2d8d55 authored by Sandeep Patil's avatar Sandeep Patil
Browse files

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: default avatarSandeep Patil <sspatil@google.com>
parent c24f1e3c
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@ struct MemUsage {
    uint64_t uss;
    uint64_t uss;


    uint64_t swap;
    uint64_t swap;
    uint64_t swap_pss;


    uint64_t private_clean;
    uint64_t private_clean;
    uint64_t private_dirty;
    uint64_t private_dirty;
@@ -44,6 +45,7 @@ struct MemUsage {
          pss(0),
          pss(0),
          uss(0),
          uss(0),
          swap(0),
          swap(0),
          swap_pss(0),
          private_clean(0),
          private_clean(0),
          private_dirty(0),
          private_dirty(0),
          shared_clean(0),
          shared_clean(0),
@@ -52,7 +54,7 @@ struct MemUsage {
    ~MemUsage() = default;
    ~MemUsage() = default;


    void clear() {
    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;
        private_clean = private_dirty = shared_clean = shared_dirty = 0;
    }
    }
};
};
+21 −0
Original line number Original line Diff line number Diff line
@@ -37,6 +37,22 @@ class ProcMemInfo final {
    const std::vector<Vma>& Maps();
    const std::vector<Vma>& Maps();
    const MemUsage& Usage();
    const MemUsage& Usage();
    const MemUsage& Wss();
    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();
    const std::vector<uint16_t>& SwapOffsets();


    ~ProcMemInfo() = default;
    ~ProcMemInfo() = default;
@@ -57,5 +73,10 @@ class ProcMemInfo final {
    std::vector<uint16_t> swap_offsets_;
    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 meminfo
}  // namespace android
}  // namespace android
+85 −0
Original line number Original line Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
#include <meminfo/sysmeminfo.h>


#include <fcntl.h>
#include <fcntl.h>
@@ -31,6 +32,9 @@


#include <benchmark/benchmark.h>
#include <benchmark/benchmark.h>


using ::android::meminfo::MemUsage;
using ::android::meminfo::ProcMemInfo;
using ::android::meminfo::SmapsOrRollupFromFile;
using ::android::meminfo::SysMemInfo;
using ::android::meminfo::SysMemInfo;


enum {
enum {
@@ -457,4 +461,85 @@ static void BM_VmallocInfo_new(benchmark::State& state) {
}
}
BENCHMARK(BM_VmallocInfo_new);
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();
BENCHMARK_MAIN();
+85 −4
Original line number Original line Diff line number Diff line
@@ -246,13 +246,13 @@ TEST_F(ValidatePageAcct, TestPageIdle) {
    }
    }
}
}


TEST(TestProcMemInfo, TestMapsEmpty) {
TEST(TestProcMemInfo, MapsEmpty) {
    ProcMemInfo proc_mem(pid);
    ProcMemInfo proc_mem(pid);
    const std::vector<Vma>& maps = proc_mem.Maps();
    const std::vector<Vma>& maps = proc_mem.Maps();
    EXPECT_GT(maps.size(), 0);
    EXPECT_GT(maps.size(), 0);
}
}


TEST(TestProcMemInfo, TestUsageEmpty) {
TEST(TestProcMemInfo, UsageEmpty) {
    // If we created the object for getting working set,
    // If we created the object for getting working set,
    // the usage must be empty
    // the usage must be empty
    ProcMemInfo proc_mem(pid, true);
    ProcMemInfo proc_mem(pid, true);
@@ -264,7 +264,7 @@ TEST(TestProcMemInfo, TestUsageEmpty) {
    EXPECT_EQ(usage.swap, 0);
    EXPECT_EQ(usage.swap, 0);
}
}


TEST(TestProcMemInfoWssReset, TestWssEmpty) {
TEST(TestProcMemInfo, WssEmpty) {
    // If we created the object for getting usage,
    // If we created the object for getting usage,
    // the working set must be empty
    // the working set must be empty
    ProcMemInfo proc_mem(pid, false);
    ProcMemInfo proc_mem(pid, false);
@@ -276,7 +276,7 @@ TEST(TestProcMemInfoWssReset, TestWssEmpty) {
    EXPECT_EQ(wss.swap, 0);
    EXPECT_EQ(wss.swap, 0);
}
}


TEST(TestProcMemInfoWssReset, TestSwapOffsetsEmpty) {
TEST(TestProcMemInfo, SwapOffsetsEmpty) {
    // If we created the object for getting working set,
    // If we created the object for getting working set,
    // the swap offsets must be empty
    // the swap offsets must be empty
    ProcMemInfo proc_mem(pid, true);
    ProcMemInfo proc_mem(pid, true);
@@ -284,6 +284,87 @@ TEST(TestProcMemInfoWssReset, TestSwapOffsetsEmpty) {
    EXPECT_EQ(swap_offsets.size(), 0);
    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) {
TEST(ValidateProcMemInfoFlags, TestPageFlags1) {
    // Create proc object using libpagemap
    // Create proc object using libpagemap
    pm_kernel_t* ker;
    pm_kernel_t* ker;
+52 −0
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android-base/unique_fd.h>
#include <procinfo/process_map.h>
#include <procinfo/process_map.h>


@@ -102,6 +103,12 @@ const MemUsage& ProcMemInfo::Wss() {
    return 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() {
const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
    if (get_wss_) {
    if (get_wss_) {
        LOG(WARNING) << "Trying to read process swap offsets for " << pid_
        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;
    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 meminfo
}  // namespace android
}  // namespace android
Loading