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

Commit 75e6ec70 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes from topic "libmeminfo"

* changes:
  meminfo: Add Smaps(), showmap and friends.
  libmeminfo: Add SmapsRollup
  meminfo: Add ReadVmallocInfo()
parents 55c08390 82a48b16
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#pragma once

#include <string.h>
#include <sys/types.h>
#include <unistd.h>

@@ -32,6 +33,7 @@ struct MemUsage {
    uint64_t uss;

    uint64_t swap;
    uint64_t swap_pss;

    uint64_t private_clean;
    uint64_t private_dirty;
@@ -44,6 +46,7 @@ struct MemUsage {
          pss(0),
          uss(0),
          swap(0),
          swap_pss(0),
          private_clean(0),
          private_dirty(0),
          shared_clean(0),
@@ -52,7 +55,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;
    }
};
@@ -64,10 +67,16 @@ struct Vma {
    uint16_t flags;
    std::string name;

    Vma() : start(0), end(0), offset(0), flags(0), name("") {}
    Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
        : start(s), end(e), offset(off), flags(f), name(n) {}
    ~Vma() = default;

    void clear() {
        memset(&usage, 0, sizeof(usage));
        memset(&wss, 0, sizeof(wss));
    }

    // Memory usage of this mapping.
    MemUsage usage;
    // Working set within this mapping.
+38 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
namespace android {
namespace meminfo {

using VmaCallback = std::function<void(const Vma&)>;

class ProcMemInfo final {
    // Per-process memory accounting
  public:
@@ -37,6 +39,33 @@ class ProcMemInfo final {
    const std::vector<Vma>& Maps();
    const MemUsage& Usage();
    const MemUsage& Wss();

    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
    // constant reference to the vma vector after the collection is done.
    //
    // Each 'struct Vma' is *fully* populated by this method (unlike SmapsOrRollup).
    const std::vector<Vma>& Smaps(const std::string& path = "");

    // This method reads /proc/<pid>/smaps and calls the callback() for each
    // vma or map that it finds. The map is converted to 'struct Vma' object which is then
    // passed to the callback.
    // Returns 'false' if the file is malformed.
    bool ForEachVma(const VmaCallback& callback);

    // 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 +86,14 @@ class ProcMemInfo final {
    std::vector<uint16_t> swap_offsets_;
};

// Makes callback for each 'vma' or 'map' found in file provided. The file is expected to be in the
// same format as /proc/<pid>/smaps. Returns 'false' if the file is malformed.
bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback);

// 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
+6 −2
Original line number Diff line number Diff line
@@ -50,12 +50,16 @@ class SysMemInfo final {

    // Parse /proc/meminfo and read values that are needed
    bool ReadMemInfo(const std::string& path = "/proc/meminfo");
    bool ReadMemInfo(const std::vector<std::string>& tags,
                     const std::string& path = "/proc/meminfo");
    bool ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
                     const std::string& path = "/proc/meminfo");
    bool ReadMemInfo(std::vector<uint64_t>* out, const std::string& path = "/proc/meminfo");

    // Parse /proc/vmallocinfo and return total physical memory mapped
    // in vmalloc area by the kernel.
    // Note that this deliberately ignores binder buffers. They are _always_
    // mapped in a process and are counted for in each process.
    uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");

    // getters
    uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
    uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
+145 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

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

#include <fcntl.h>
@@ -26,9 +27,14 @@

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>

#include <benchmark/benchmark.h>

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

enum {
@@ -397,4 +403,143 @@ Hugepagesize: 2048 kB)meminfo";
}
BENCHMARK(BM_MemInfoWithZram_new);

// Current implementation is in frameworks/base/core/jni/android_os_Debug.cpp.
// That implementation is still buggy and it skips over vmalloc allocated memory by kernel modules.
// This is the *fixed* version of the same implementation intended for benchmarking against the new
// one.
static uint64_t get_allocated_vmalloc_memory(const std::string& vm_file) {
    char line[1024];

    uint64_t vmalloc_allocated_size = 0;
    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(vm_file.c_str(), "re"), fclose};
    if (fp == nullptr) {
        return 0;
    }

    while (true) {
        if (fgets(line, 1024, fp.get()) == NULL) {
            break;
        }

        // check to see if there are pages mapped in vmalloc area
        if (!strstr(line, "pages=")) {
            continue;
        }

        long nr_pages;
        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
            vmalloc_allocated_size += (nr_pages * getpagesize());
        } else if (sscanf(line, "%*x-%*x %*ld %*s %*s pages=%ld", &nr_pages) == 1) {
            // The second case is for kernel modules. If allocation comes from the module,
            // kernel puts an extra string containing the module name before "pages=" in
            // the line.
            //    See: https://elixir.bootlin.com/linux/latest/source/kernel/kallsyms.c#L373
            vmalloc_allocated_size += (nr_pages * getpagesize());
        }
    }
    return vmalloc_allocated_size;
}

static void BM_VmallocInfo_old_fixed(benchmark::State& state) {
    std::string exec_dir = ::android::base::GetExecutableDirectory();
    std::string vmallocinfo =
            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
    for (auto _ : state) {
        CHECK_EQ(get_allocated_vmalloc_memory(vmallocinfo), 29884416);
    }
}
BENCHMARK(BM_VmallocInfo_old_fixed);

static void BM_VmallocInfo_new(benchmark::State& state) {
    std::string exec_dir = ::android::base::GetExecutableDirectory();
    std::string vmallocinfo =
            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
    for (auto _ : state) {
        SysMemInfo smi;
        CHECK_EQ(smi.ReadVmallocInfo(vmallocinfo), 29884416);
    }
}
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();
+331 −4
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>

using namespace std;
using namespace android::meminfo;
@@ -245,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);
@@ -263,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);
@@ -275,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);
@@ -283,6 +284,272 @@ 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(TestProcMemInfo, ForEachVmaFromFileTest) {
    std::string exec_dir = ::android::base::GetExecutableDirectory();
    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
    ProcMemInfo proc_mem(pid);

    std::vector<Vma> vmas;
    auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
    ASSERT_TRUE(ForEachVmaFromFile(path, collect_vmas));

    // Expect values to be equal to what we have in testdata1/smaps_short
    // Check for sizes first
    ASSERT_EQ(vmas[0].usage.vss, 32768);
    EXPECT_EQ(vmas[1].usage.vss, 11204);
    EXPECT_EQ(vmas[2].usage.vss, 16896);
    EXPECT_EQ(vmas[3].usage.vss, 260);
    EXPECT_EQ(vmas[4].usage.vss, 6060);
    EXPECT_EQ(vmas[5].usage.vss, 4);

    // Check for names
    EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
    EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
    EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
    EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
    EXPECT_EQ(vmas[5].name, "[vsyscall]");

    EXPECT_EQ(vmas[0].usage.rss, 2048);
    EXPECT_EQ(vmas[1].usage.rss, 11188);
    EXPECT_EQ(vmas[2].usage.rss, 15272);
    EXPECT_EQ(vmas[3].usage.rss, 260);
    EXPECT_EQ(vmas[4].usage.rss, 4132);
    EXPECT_EQ(vmas[5].usage.rss, 0);

    EXPECT_EQ(vmas[0].usage.pss, 113);
    EXPECT_EQ(vmas[1].usage.pss, 2200);
    EXPECT_EQ(vmas[2].usage.pss, 15272);
    EXPECT_EQ(vmas[3].usage.pss, 260);
    EXPECT_EQ(vmas[4].usage.pss, 1274);
    EXPECT_EQ(vmas[5].usage.pss, 0);

    EXPECT_EQ(vmas[0].usage.uss, 0);
    EXPECT_EQ(vmas[1].usage.uss, 1660);
    EXPECT_EQ(vmas[2].usage.uss, 15272);
    EXPECT_EQ(vmas[3].usage.uss, 260);
    EXPECT_EQ(vmas[4].usage.uss, 0);
    EXPECT_EQ(vmas[5].usage.uss, 0);

    EXPECT_EQ(vmas[0].usage.private_clean, 0);
    EXPECT_EQ(vmas[1].usage.private_clean, 0);
    EXPECT_EQ(vmas[2].usage.private_clean, 0);
    EXPECT_EQ(vmas[3].usage.private_clean, 260);
    EXPECT_EQ(vmas[4].usage.private_clean, 0);
    EXPECT_EQ(vmas[5].usage.private_clean, 0);

    EXPECT_EQ(vmas[0].usage.private_dirty, 0);
    EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
    EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
    EXPECT_EQ(vmas[3].usage.private_dirty, 0);
    EXPECT_EQ(vmas[4].usage.private_dirty, 0);
    EXPECT_EQ(vmas[5].usage.private_dirty, 0);

    EXPECT_EQ(vmas[0].usage.shared_clean, 0);
    EXPECT_EQ(vmas[1].usage.shared_clean, 80);
    EXPECT_EQ(vmas[2].usage.shared_clean, 0);
    EXPECT_EQ(vmas[3].usage.shared_clean, 0);
    EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
    EXPECT_EQ(vmas[5].usage.shared_clean, 0);

    EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
    EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
    EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[5].usage.shared_dirty, 0);

    EXPECT_EQ(vmas[0].usage.swap, 0);
    EXPECT_EQ(vmas[1].usage.swap, 0);
    EXPECT_EQ(vmas[2].usage.swap, 0);
    EXPECT_EQ(vmas[3].usage.swap, 0);
    EXPECT_EQ(vmas[4].usage.swap, 0);
    EXPECT_EQ(vmas[5].usage.swap, 0);

    EXPECT_EQ(vmas[0].usage.swap_pss, 0);
    EXPECT_EQ(vmas[1].usage.swap_pss, 0);
    EXPECT_EQ(vmas[2].usage.swap_pss, 0);
    EXPECT_EQ(vmas[3].usage.swap_pss, 0);
    EXPECT_EQ(vmas[4].usage.swap_pss, 0);
    EXPECT_EQ(vmas[5].usage.swap_pss, 0);
}

TEST(TestProcMemInfo, SmapsReturnTest) {
    ProcMemInfo proc_mem(pid);
    auto vmas = proc_mem.Smaps();
    EXPECT_FALSE(vmas.empty());
}

TEST(TestProcMemInfo, SmapsTest) {
    std::string exec_dir = ::android::base::GetExecutableDirectory();
    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
    ProcMemInfo proc_mem(pid);
    auto vmas = proc_mem.Smaps(path);

    ASSERT_FALSE(vmas.empty());

    // Expect values to be equal to what we have in testdata1/smaps_short
    // Check for sizes first
    ASSERT_EQ(vmas[0].usage.vss, 32768);
    EXPECT_EQ(vmas[1].usage.vss, 11204);
    EXPECT_EQ(vmas[2].usage.vss, 16896);
    EXPECT_EQ(vmas[3].usage.vss, 260);
    EXPECT_EQ(vmas[4].usage.vss, 6060);
    EXPECT_EQ(vmas[5].usage.vss, 4);

    // Check for names
    EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
    EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
    EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
    EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
    EXPECT_EQ(vmas[5].name, "[vsyscall]");

    EXPECT_EQ(vmas[0].usage.rss, 2048);
    EXPECT_EQ(vmas[1].usage.rss, 11188);
    EXPECT_EQ(vmas[2].usage.rss, 15272);
    EXPECT_EQ(vmas[3].usage.rss, 260);
    EXPECT_EQ(vmas[4].usage.rss, 4132);
    EXPECT_EQ(vmas[5].usage.rss, 0);

    EXPECT_EQ(vmas[0].usage.pss, 113);
    EXPECT_EQ(vmas[1].usage.pss, 2200);
    EXPECT_EQ(vmas[2].usage.pss, 15272);
    EXPECT_EQ(vmas[3].usage.pss, 260);
    EXPECT_EQ(vmas[4].usage.pss, 1274);
    EXPECT_EQ(vmas[5].usage.pss, 0);

    EXPECT_EQ(vmas[0].usage.uss, 0);
    EXPECT_EQ(vmas[1].usage.uss, 1660);
    EXPECT_EQ(vmas[2].usage.uss, 15272);
    EXPECT_EQ(vmas[3].usage.uss, 260);
    EXPECT_EQ(vmas[4].usage.uss, 0);
    EXPECT_EQ(vmas[5].usage.uss, 0);

    EXPECT_EQ(vmas[0].usage.private_clean, 0);
    EXPECT_EQ(vmas[1].usage.private_clean, 0);
    EXPECT_EQ(vmas[2].usage.private_clean, 0);
    EXPECT_EQ(vmas[3].usage.private_clean, 260);
    EXPECT_EQ(vmas[4].usage.private_clean, 0);
    EXPECT_EQ(vmas[5].usage.private_clean, 0);

    EXPECT_EQ(vmas[0].usage.private_dirty, 0);
    EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
    EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
    EXPECT_EQ(vmas[3].usage.private_dirty, 0);
    EXPECT_EQ(vmas[4].usage.private_dirty, 0);
    EXPECT_EQ(vmas[5].usage.private_dirty, 0);

    EXPECT_EQ(vmas[0].usage.shared_clean, 0);
    EXPECT_EQ(vmas[1].usage.shared_clean, 80);
    EXPECT_EQ(vmas[2].usage.shared_clean, 0);
    EXPECT_EQ(vmas[3].usage.shared_clean, 0);
    EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
    EXPECT_EQ(vmas[5].usage.shared_clean, 0);

    EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
    EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
    EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
    EXPECT_EQ(vmas[5].usage.shared_dirty, 0);

    EXPECT_EQ(vmas[0].usage.swap, 0);
    EXPECT_EQ(vmas[1].usage.swap, 0);
    EXPECT_EQ(vmas[2].usage.swap, 0);
    EXPECT_EQ(vmas[3].usage.swap, 0);
    EXPECT_EQ(vmas[4].usage.swap, 0);
    EXPECT_EQ(vmas[5].usage.swap, 0);

    EXPECT_EQ(vmas[0].usage.swap_pss, 0);
    EXPECT_EQ(vmas[1].usage.swap_pss, 0);
    EXPECT_EQ(vmas[2].usage.swap_pss, 0);
    EXPECT_EQ(vmas[3].usage.swap_pss, 0);
    EXPECT_EQ(vmas[4].usage.swap_pss, 0);
    EXPECT_EQ(vmas[5].usage.swap_pss, 0);
}

TEST(ValidateProcMemInfoFlags, TestPageFlags1) {
    // Create proc object using libpagemap
    pm_kernel_t* ker;
@@ -516,6 +783,66 @@ Hugepagesize: 2048 kB)meminfo";
    EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
}

TEST(SysMemInfoParser, TestVmallocInfoNoMemory) {
    std::string vmallocinfo =
            R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap)vmallocinfo";

    TemporaryFile tf;
    ASSERT_TRUE(tf.fd != -1);
    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
    std::string file = std::string(tf.path);

    SysMemInfo smi;
    EXPECT_EQ(smi.ReadVmallocInfo(file), 0);
}

TEST(SysMemInfoParser, TestVmallocInfoKernel) {
    std::string vmallocinfo =
            R"vmallocinfo(0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc)vmallocinfo";

    TemporaryFile tf;
    ASSERT_TRUE(tf.fd != -1);
    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
    std::string file = std::string(tf.path);

    SysMemInfo smi;
    EXPECT_EQ(smi.ReadVmallocInfo(file), getpagesize());
}

TEST(SysMemInfoParser, TestVmallocInfoModule) {
    std::string vmallocinfo =
            R"vmallocinfo(0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";

    TemporaryFile tf;
    ASSERT_TRUE(tf.fd != -1);
    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
    std::string file = std::string(tf.path);

    SysMemInfo smi;
    EXPECT_EQ(smi.ReadVmallocInfo(file), 6 * getpagesize());
}

TEST(SysMemInfoParser, TestVmallocInfoAll) {
    std::string vmallocinfo =
            R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap
0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";

    TemporaryFile tf;
    ASSERT_TRUE(tf.fd != -1);
    ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
    std::string file = std::string(tf.path);

    SysMemInfo smi;
    EXPECT_EQ(smi.ReadVmallocInfo(file), 7 * getpagesize());
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    if (argc <= 1) {
Loading