libmeminfo: Add libmeminfo to gather global and per-process memory stats

The library is expected to be a unified place for all components to read
both global and per-process memory accounting form kernel including
getting the working set. This change adds the PageInfo, MemInfo and
ProcMemInfo classes and verifies the implementation against libpagemap
for correctness.

Adds a procmem2 tool show the usage.

TODO: Plumbing in os_debug, add vmastats, zoneinfo etc parsing.

Test: libmeminfo_test 1
Test: procmem2 1
Test: procmem2 -h -W 1
Test: procmem2 -h -w 1
Test: libmeminfo_benchmark
Bug: 111694435
Bug: 114325007

Change-Id: I280440b1dc26a498170686d10fcf63f953a0dcbd
Signed-off-by: Sandeep Patil <sspatil@google.com>
This commit is contained in:
Sandeep Patil 2018-08-29 17:10:47 -07:00
parent e0c3a8d97b
commit 54d8721374
14 changed files with 1572 additions and 0 deletions

1
libmeminfo/.clang-format Symbolic link
View file

@ -0,0 +1 @@
../.clang-format-4

70
libmeminfo/Android.bp Normal file
View file

@ -0,0 +1,70 @@
//
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
cc_defaults {
name: "libmeminfo_defaults",
cflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"libbase",
"liblog",
"libprocinfo",
],
}
cc_library {
name: "libmeminfo",
defaults: ["libmeminfo_defaults"],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libbase"],
srcs: [
"pageacct.cpp",
"procmeminfo.cpp",
"sysmeminfo.cpp",
],
}
cc_test {
name: "libmeminfo_test",
defaults: ["libmeminfo_defaults"],
static_libs: [
"libmeminfo",
"libpagemap",
"libbase",
"liblog",
],
srcs: [
"libmeminfo_test.cpp"
],
}
cc_benchmark {
name: "libmeminfo_benchmark",
srcs: [
"libmeminfo_benchmark.cpp",
],
static_libs : [
"libbase",
"liblog",
"libmeminfo",
"libprocinfo",
],
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
namespace android {
namespace meminfo {
struct MemUsage {
uint64_t vss;
uint64_t rss;
uint64_t pss;
uint64_t uss;
uint64_t private_clean;
uint64_t private_dirty;
uint64_t shared_clean;
uint64_t shared_dirty;
MemUsage()
: vss(0),
rss(0),
pss(0),
uss(0),
private_clean(0),
private_dirty(0),
shared_clean(0),
shared_dirty(0) {}
~MemUsage() = default;
void clear() {
vss = rss = pss = uss = 0;
private_clean = private_dirty = shared_clean = shared_dirty = 0;
}
};
struct Vma {
uint64_t start;
uint64_t end;
uint64_t offset;
uint16_t flags;
std::string 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;
// Memory usage of this mapping.
MemUsage usage;
// Working set within this mapping.
MemUsage wss;
};
} // namespace meminfo
} // namespace android

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <android-base/unique_fd.h>
namespace android {
namespace meminfo {
class PageAcct final {
// Class for per-page accounting by using kernel provided interfaces like
// kpagecount, kpageflags etc.
public:
static bool KernelHasPageIdle() {
return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
}
bool InitPageAcct(bool pageidle_enable = false);
bool PageFlags(uint64_t pfn, uint64_t* flags);
bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
int IsPageIdle(uint64_t pfn);
// The only way to create PageAcct object
static PageAcct& Instance() {
static PageAcct instance;
return instance;
}
~PageAcct() = default;
private:
PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
int MarkPageIdle(uint64_t pfn) const;
int GetPageIdle(uint64_t pfn) const;
// Non-copyable & Non-movable
PageAcct(const PageAcct&) = delete;
PageAcct& operator=(const PageAcct&) = delete;
PageAcct& operator=(PageAcct&&) = delete;
PageAcct(PageAcct&&) = delete;
::android::base::unique_fd kpagecount_fd_;
::android::base::unique_fd kpageflags_fd_;
::android::base::unique_fd pageidle_fd_;
};
} // namespace meminfo
} // namespace android

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <string>
#include <vector>
#include "meminfo.h"
namespace android {
namespace meminfo {
class ProcMemInfo final {
// Per-process memory accounting
public:
ProcMemInfo(pid_t pid, bool get_wss = false);
const std::vector<Vma>& Maps();
const MemUsage& Usage();
const MemUsage& Wss();
bool WssReset();
~ProcMemInfo() = default;
private:
bool ReadMaps(bool get_wss);
bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
pid_t pid_;
bool get_wss_;
std::vector<Vma> maps_;
MemUsage usage_;
MemUsage wss_;
};
} // namespace meminfo
} // namespace android

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <map>
#include <string>
#include <vector>
namespace android {
namespace meminfo {
class SysMemInfo final {
// System or Global memory accounting
public:
static const std::vector<std::string> kDefaultSysMemInfoTags;
SysMemInfo() = default;
// 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");
// getters
uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
private:
std::map<std::string, uint64_t> mem_in_kb_;
};
} // namespace meminfo
} // namespace android

View file

@ -0,0 +1,218 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <meminfo/sysmeminfo.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/test_utils.h>
#include <benchmark/benchmark.h>
enum {
MEMINFO_TOTAL,
MEMINFO_FREE,
MEMINFO_BUFFERS,
MEMINFO_CACHED,
MEMINFO_SHMEM,
MEMINFO_SLAB,
MEMINFO_SLAB_RECLAIMABLE,
MEMINFO_SLAB_UNRECLAIMABLE,
MEMINFO_SWAP_TOTAL,
MEMINFO_SWAP_FREE,
MEMINFO_ZRAM_TOTAL,
MEMINFO_MAPPED,
MEMINFO_VMALLOC_USED,
MEMINFO_PAGE_TABLES,
MEMINFO_KERNEL_STACK,
MEMINFO_COUNT
};
void get_mem_info(uint64_t mem[], const char* file) {
char buffer[4096];
unsigned int numFound = 0;
int fd = open(file, O_RDONLY);
if (fd < 0) {
printf("Unable to open %s: %s\n", file, strerror(errno));
return;
}
const int len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
if (len < 0) {
printf("Empty %s\n", file);
return;
}
buffer[len] = 0;
static const char* const tags[] = {
"MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:", "Slab:",
"SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:", "ZRam:", "Mapped:",
"VmallocUsed:", "PageTables:", "KernelStack:", NULL};
static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
memset(mem, 0, sizeof(uint64_t) * 15);
char* p = buffer;
while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
int i = 0;
while (tags[i]) {
//std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
if (strncmp(p, tags[i], tagsLen[i]) == 0) {
p += tagsLen[i];
while (*p == ' ') p++;
char* num = p;
while (*p >= '0' && *p <= '9') p++;
if (*p != 0) {
*p = 0;
p++;
}
mem[i] = atoll(num);
numFound++;
break;
}
i++;
}
while (*p && *p != '\n') {
p++;
}
if (*p) p++;
}
}
static void BM_ParseSysMemInfo(benchmark::State& state) {
std::string meminfo = R"meminfo(MemTotal: 3019740 kB
MemFree: 1809728 kB
MemAvailable: 2546560 kB
Buffers: 54736 kB
Cached: 776052 kB
SwapCached: 0 kB
Active: 445856 kB
Inactive: 459092 kB
Active(anon): 78492 kB
Inactive(anon): 2240 kB
Active(file): 367364 kB
Inactive(file): 456852 kB
Unevictable: 3096 kB
Mlocked: 3096 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 32 kB
Writeback: 0 kB
AnonPages: 74988 kB
Mapped: 62624 kB
Shmem: 4020 kB
Slab: 86464 kB
SReclaimable: 44432 kB
SUnreclaim: 42032 kB
KernelStack: 4880 kB
PageTables: 2900 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1509868 kB
Committed_AS: 80296 kB
VmallocTotal: 263061440 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
AnonHugePages: 6144 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
CmaTotal: 131072 kB
CmaFree: 130380 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB)meminfo";
TemporaryFile tf;
::android::base::WriteStringToFd(meminfo, tf.fd);
uint64_t mem[MEMINFO_COUNT];
for (auto _ : state) {
get_mem_info(mem, tf.path);
}
}
BENCHMARK(BM_ParseSysMemInfo);
static void BM_ReadMemInfo(benchmark::State& state) {
std::string meminfo = R"meminfo(MemTotal: 3019740 kB
MemFree: 1809728 kB
MemAvailable: 2546560 kB
Buffers: 54736 kB
Cached: 776052 kB
SwapCached: 0 kB
Active: 445856 kB
Inactive: 459092 kB
Active(anon): 78492 kB
Inactive(anon): 2240 kB
Active(file): 367364 kB
Inactive(file): 456852 kB
Unevictable: 3096 kB
Mlocked: 3096 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 32 kB
Writeback: 0 kB
AnonPages: 74988 kB
Mapped: 62624 kB
Shmem: 4020 kB
Slab: 86464 kB
SReclaimable: 44432 kB
SUnreclaim: 42032 kB
KernelStack: 4880 kB
PageTables: 2900 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1509868 kB
Committed_AS: 80296 kB
VmallocTotal: 263061440 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
AnonHugePages: 6144 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
CmaTotal: 131072 kB
CmaFree: 130380 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB)meminfo";
TemporaryFile tf;
android::base::WriteStringToFd(meminfo, tf.fd);
std::string file = std::string(tf.path);
::android::meminfo::SysMemInfo mi;
for (auto _ : state) {
mi.ReadMemInfo(file);
}
}
BENCHMARK(BM_ReadMemInfo);
BENCHMARK_MAIN();

View file

@ -0,0 +1,301 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <meminfo/pageacct.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
#include <pagemap/pagemap.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/test_utils.h>
using namespace std;
using namespace android::meminfo;
pid_t pid = -1;
class ValidateProcMemInfo : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_EQ(0, pm_kernel_create(&ker));
ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
proc_mem = new ProcMemInfo(pid);
ASSERT_NE(proc_mem, nullptr);
}
void TearDown() override {
delete proc_mem;
pm_process_destroy(proc);
pm_kernel_destroy(ker);
}
pm_kernel_t* ker;
pm_process_t* proc;
ProcMemInfo* proc_mem;
};
TEST_F(ValidateProcMemInfo, TestMapsSize) {
const std::vector<Vma>& maps = proc_mem->Maps();
ASSERT_FALSE(maps.empty()) << "Process " << getpid() << " maps are empty";
}
TEST_F(ValidateProcMemInfo, TestMapsEquality) {
const std::vector<Vma>& maps = proc_mem->Maps();
ASSERT_EQ(proc->num_maps, maps.size());
for (size_t i = 0; i < maps.size(); ++i) {
EXPECT_EQ(proc->maps[i]->start, maps[i].start);
EXPECT_EQ(proc->maps[i]->end, maps[i].end);
EXPECT_EQ(proc->maps[i]->offset, maps[i].offset);
EXPECT_EQ(std::string(proc->maps[i]->name), maps[i].name);
}
}
TEST_F(ValidateProcMemInfo, TestMapsUsage) {
const std::vector<Vma>& maps = proc_mem->Maps();
ASSERT_FALSE(maps.empty());
ASSERT_EQ(proc->num_maps, maps.size());
pm_memusage_t map_usage, proc_usage;
pm_memusage_zero(&map_usage);
pm_memusage_zero(&proc_usage);
for (size_t i = 0; i < maps.size(); i++) {
ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
EXPECT_EQ(map_usage.vss, maps[i].usage.vss) << "VSS mismatch for map: " << maps[i].name;
EXPECT_EQ(map_usage.rss, maps[i].usage.rss) << "RSS mismatch for map: " << maps[i].name;
EXPECT_EQ(map_usage.pss, maps[i].usage.pss) << "PSS mismatch for map: " << maps[i].name;
EXPECT_EQ(map_usage.uss, maps[i].usage.uss) << "USS mismatch for map: " << maps[i].name;
pm_memusage_add(&proc_usage, &map_usage);
}
EXPECT_EQ(proc_usage.vss, proc_mem->Usage().vss);
EXPECT_EQ(proc_usage.rss, proc_mem->Usage().rss);
EXPECT_EQ(proc_usage.pss, proc_mem->Usage().pss);
EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
}
class ValidateProcMemInfoWss : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_EQ(0, pm_kernel_create(&ker));
ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
proc_mem = new ProcMemInfo(pid, true);
ASSERT_NE(proc_mem, nullptr);
}
void TearDown() override {
delete proc_mem;
pm_process_destroy(proc);
pm_kernel_destroy(ker);
}
pm_kernel_t* ker;
pm_process_t* proc;
ProcMemInfo* proc_mem;
};
TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
// Expect reset to succeed
EXPECT_TRUE(proc_mem->WssReset());
}
TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
// Read wss using libpagemap
pm_memusage_t wss_pagemap;
EXPECT_EQ(0, pm_process_workingset(proc, &wss_pagemap, 0));
// Read wss using libmeminfo
MemUsage wss = proc_mem->Wss();
// compare
EXPECT_EQ(wss_pagemap.rss, wss.rss);
EXPECT_EQ(wss_pagemap.pss, wss.pss);
EXPECT_EQ(wss_pagemap.uss, wss.uss);
}
class ValidatePageAcct : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_EQ(0, pm_kernel_create(&ker));
ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
}
void TearDown() override {
pm_process_destroy(proc);
pm_kernel_destroy(ker);
}
pm_kernel_t* ker;
pm_process_t* proc;
};
TEST_F(ValidatePageAcct, TestPageFlags) {
PageAcct& pi = PageAcct::Instance();
pi.InitPageAcct(false);
uint64_t* pagemap;
size_t num_pages;
for (size_t i = 0; i < proc->num_maps; i++) {
ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
for (size_t j = 0; j < num_pages; j++) {
if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
uint64_t page_flags_pagemap, page_flags_meminfo;
ASSERT_EQ(0, pm_kernel_flags(ker, pfn, &page_flags_pagemap));
ASSERT_TRUE(pi.PageFlags(pfn, &page_flags_meminfo));
// check if page flags equal
EXPECT_EQ(page_flags_pagemap, page_flags_meminfo);
}
free(pagemap);
}
}
TEST_F(ValidatePageAcct, TestPageCounts) {
PageAcct& pi = PageAcct::Instance();
pi.InitPageAcct(false);
uint64_t* pagemap;
size_t num_pages;
for (size_t i = 0; i < proc->num_maps; i++) {
ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
for (size_t j = 0; j < num_pages; j++) {
uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
uint64_t map_count_pagemap, map_count_meminfo;
ASSERT_EQ(0, pm_kernel_count(ker, pfn, &map_count_pagemap));
ASSERT_TRUE(pi.PageMapCount(pfn, &map_count_meminfo));
// check if map counts are equal
EXPECT_EQ(map_count_pagemap, map_count_meminfo);
}
free(pagemap);
}
}
TEST_F(ValidatePageAcct, TestPageIdle) {
// skip the test if idle page tracking isn't enabled
if (pm_kernel_init_page_idle(ker) != 0) {
return;
}
PageAcct& pi = PageAcct::Instance();
ASSERT_TRUE(pi.InitPageAcct(true));
uint64_t* pagemap;
size_t num_pages;
for (size_t i = 0; i < proc->num_maps; i++) {
ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
for (size_t j = 0; j < num_pages; j++) {
if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
ASSERT_EQ(0, pm_kernel_mark_page_idle(ker, &pfn, 1));
int idle_status_pagemap = pm_kernel_get_page_idle(ker, pfn);
int idle_status_meminfo = pi.IsPageIdle(pfn);
EXPECT_EQ(idle_status_pagemap, idle_status_meminfo);
}
free(pagemap);
}
}
TEST(SysMemInfoParser, TestSysMemInfoFile) {
std::string meminfo = R"meminfo(MemTotal: 3019740 kB
MemFree: 1809728 kB
MemAvailable: 2546560 kB
Buffers: 54736 kB
Cached: 776052 kB
SwapCached: 0 kB
Active: 445856 kB
Inactive: 459092 kB
Active(anon): 78492 kB
Inactive(anon): 2240 kB
Active(file): 367364 kB
Inactive(file): 456852 kB
Unevictable: 3096 kB
Mlocked: 3096 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 32 kB
Writeback: 0 kB
AnonPages: 74988 kB
Mapped: 62624 kB
Shmem: 4020 kB
Slab: 86464 kB
SReclaimable: 44432 kB
SUnreclaim: 42032 kB
KernelStack: 4880 kB
PageTables: 2900 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1509868 kB
Committed_AS: 80296 kB
VmallocTotal: 263061440 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
AnonHugePages: 6144 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
CmaTotal: 131072 kB
CmaFree: 130380 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB)meminfo";
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
SysMemInfo mi;
ASSERT_TRUE(mi.ReadMemInfo(tf.path));
EXPECT_EQ(mi.mem_total_kb(), 3019740);
EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
}
TEST(SysMemInfoParser, TestEmptyFile) {
TemporaryFile tf;
std::string empty_string = "";
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
SysMemInfo mi;
EXPECT_TRUE(mi.ReadMemInfo(tf.path));
EXPECT_EQ(mi.mem_total_kb(), 0);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc <= 1) {
cerr << "Pid of a permanently sleeping process must be provided." << endl;
exit(EXIT_FAILURE);
}
::android::base::InitLogging(argv, android::base::StderrLogger);
pid = std::stoi(std::string(argv[1]));
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/types.h>
#include <unistd.h>
#include <meminfo/meminfo.h>
#include <meminfo/pageacct.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
// Macros to do per-page flag manipulation
#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
#define PAGE_PFN(x) (_BITS(x, 0, 55))
#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))

142
libmeminfo/pageacct.cpp Normal file
View file

@ -0,0 +1,142 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include "meminfo_private.h"
using unique_fd = ::android::base::unique_fd;
namespace android {
namespace meminfo {
static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
return static_cast<off64_t>((pfn >> 6) << 3);
}
uint64_t pagesize(void) {
static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
return pagesize;
}
bool PageAcct::InitPageAcct(bool pageidle_enable) {
if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
LOG(ERROR) << "Idle page tracking is not supported by the kernel";
return false;
}
if (kpagecount_fd_ < 0) {
unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
if (count_fd < 0) {
PLOG(ERROR) << "Failed to open /proc/kpagecount";
return false;
}
kpagecount_fd_ = std::move(count_fd);
}
if (kpageflags_fd_ < 0) {
unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
if (flags_fd < 0) {
PLOG(ERROR) << "Failed to open /proc/kpageflags";
return false;
}
kpageflags_fd_ = std::move(flags_fd);
}
if (pageidle_enable && pageidle_fd_ < 0) {
unique_fd idle_fd(
TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
if (idle_fd < 0) {
PLOG(ERROR) << "Failed to open page idle bitmap";
return false;
}
pageidle_fd_ = std::move(idle_fd);
}
return true;
}
bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
if (!flags) return false;
if (kpageflags_fd_ < 0) {
if (!InitPageAcct()) return false;
}
if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
PLOG(ERROR) << "Failed to read page flags for page " << pfn;
return false;
}
return true;
}
bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
if (!mapcount) return false;
if (kpagecount_fd_ < 0) {
if (!InitPageAcct()) return false;
}
if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
PLOG(ERROR) << "Failed to read map count for page " << pfn;
return false;
}
return true;
}
int PageAcct::IsPageIdle(uint64_t pfn) {
if (pageidle_fd_ < 0) {
if (!InitPageAcct(true)) return -EOPNOTSUPP;
}
int idle_status = MarkPageIdle(pfn);
if (idle_status) return idle_status;
return GetPageIdle(pfn);
}
int PageAcct::MarkPageIdle(uint64_t pfn) const {
off64_t offset = pfn_to_idle_bitmap_offset(pfn);
// set the bit corresponding to page frame
uint64_t idle_bits = 1ULL << (pfn % 64);
if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
return -errno;
}
return 0;
}
int PageAcct::GetPageIdle(uint64_t pfn) const {
off64_t offset = pfn_to_idle_bitmap_offset(pfn);
uint64_t idle_bits;
if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
return -errno;
}
return !!(idle_bits & (1ULL << (pfn % 64)));
}
} // namespace meminfo
} // namespace android

218
libmeminfo/procmeminfo.cpp Normal file
View file

@ -0,0 +1,218 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/kernel-page-flags.h>
#include <stdio.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <procinfo/process_map.h>
#include "meminfo_private.h"
namespace android {
namespace meminfo {
static void add_mem_usage(MemUsage* to, const MemUsage& from) {
to->vss += from.vss;
to->rss += from.rss;
to->pss += from.pss;
to->uss += from.uss;
to->private_clean += from.private_clean;
to->private_dirty += from.private_dirty;
to->shared_clean += from.shared_clean;
to->shared_dirty += from.shared_dirty;
}
ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss) : pid_(pid), get_wss_(get_wss) {
if (!ReadMaps(get_wss_)) {
LOG(ERROR) << "Failed to read maps for Process " << pid_;
}
}
const std::vector<Vma>& ProcMemInfo::Maps() {
return maps_;
}
const MemUsage& ProcMemInfo::Usage() {
if (get_wss_) {
LOG(WARNING) << "Trying to read memory usage from working set object";
}
return usage_;
}
const MemUsage& ProcMemInfo::Wss() {
if (!get_wss_) {
LOG(WARNING) << "Trying to read working set when there is none";
}
return wss_;
}
bool ProcMemInfo::WssReset() {
if (!get_wss_) {
LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
return false;
}
std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid_);
if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
PLOG(ERROR) << "Failed to write to " << clear_refs_path;
return false;
}
wss_.clear();
return true;
}
bool ProcMemInfo::ReadMaps(bool get_wss) {
// parse and read /proc/<pid>/maps
std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
if (!::android::procinfo::ReadMapFile(
maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
const char* name) {
maps_.emplace_back(Vma(start, end, pgoff, flags, name));
})) {
LOG(ERROR) << "Failed to parse " << maps_file;
maps_.clear();
return false;
}
std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
::android::base::unique_fd pagemap_fd(
TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
if (pagemap_fd < 0) {
PLOG(ERROR) << "Failed to open " << pagemap_file;
return false;
}
for (auto& vma : maps_) {
if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
<< "-" << vma.end << "]";
maps_.clear();
return false;
}
if (get_wss) {
add_mem_usage(&wss_, vma.wss);
} else {
add_mem_usage(&usage_, vma.usage);
}
}
return true;
}
bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
PageAcct& pinfo = PageAcct::Instance();
uint64_t pagesz = getpagesize();
uint64_t num_pages = (vma.end - vma.start) / pagesz;
std::unique_ptr<uint64_t[]> pg_frames(new uint64_t[num_pages]);
uint64_t first = vma.start / pagesz;
if (pread64(pagemap_fd, pg_frames.get(), num_pages * sizeof(uint64_t),
first * sizeof(uint64_t)) < 0) {
PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
return false;
}
std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
for (uint64_t i = 0; i < num_pages; ++i) {
if (!get_wss) {
vma.usage.vss += pagesz;
}
uint64_t p = pg_frames[i];
if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
if (PAGE_SWAPPED(p)) {
// TODO: do what's needed for swapped pages
continue;
}
uint64_t page_frame = PAGE_PFN(p);
if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
return false;
}
if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
return false;
}
// Page was unmapped between the presence check at the beginning of the loop and here.
if (pg_counts[i] == 0) {
pg_frames[i] = 0;
pg_flags[i] = 0;
continue;
}
bool is_dirty = !!(pg_flags[i] & (1 << KPF_DIRTY));
bool is_private = (pg_counts[i] == 1);
// Working set
if (get_wss) {
bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
if (!is_referenced) {
continue;
}
// 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;
}
} 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;
}
}
}
return true;
}
} // namespace meminfo
} // namespace android

129
libmeminfo/sysmeminfo.cpp Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <cctype>
#include <fstream>
#include <string>
#include <utility>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include "meminfo_private.h"
namespace android {
namespace meminfo {
const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
"MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:",
"Slab:", "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:",
"ZRam:", "Mapped:", "VmallocUsed:", "PageTables:", "KernelStack:",
};
bool SysMemInfo::ReadMemInfo(const std::string& path) {
return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path);
}
// TODO: Delete this function if it can't match up with the c-like implementation below.
// Currently, this added about 50 % extra overhead on hikey.
#if 0
bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
std::string buffer;
if (!::android::base::ReadFileToString(path, &buffer)) {
PLOG(ERROR) << "Failed to read : " << path;
return false;
}
uint32_t total_found = 0;
for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
for (auto& tag : tags) {
if (tag == std::string(s, s + tag.size())) {
s += tag.size();
while (isspace(*s)) s++;
auto num_start = s;
while (std::isdigit(*s)) s++;
std::string number(num_start, num_start + (s - num_start));
if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
LOG(ERROR) << "Failed to parse uint";
return false;
}
total_found++;
break;
}
}
while (s < buffer.end() && *s != '\n') s++;
if (s < buffer.end()) s++;
}
return true;
}
#else
bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
char buffer[4096];
int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
PLOG(ERROR) << "Failed to open file :" << path;
return false;
}
const int len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
if (len < 0) {
return false;
}
buffer[len] = '\0';
char* p = buffer;
uint32_t found = 0;
while (*p && found < tags.size()) {
for (auto& tag : tags) {
if (strncmp(p, tag.c_str(), tag.size()) == 0) {
p += tag.size();
while (*p == ' ') p++;
char* endptr = nullptr;
mem_in_kb_[tag] = strtoull(p, &endptr, 10);
if (p == endptr) {
PLOG(ERROR) << "Failed to parse line in file: " << path;
return false;
}
p = endptr;
found++;
break;
}
}
while (*p && *p != '\n') {
p++;
}
if (*p) p++;
}
return true;
}
#endif
} // namespace meminfo
} // namespace android

View file

@ -0,0 +1,27 @@
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_binary {
name: "procmem2",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
],
srcs: ["procmem.cpp"],
shared_libs: [
"libmeminfo",
],
}

View file

@ -0,0 +1,171 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <meminfo/procmeminfo.h>
using ProcMemInfo = ::android::meminfo::ProcMemInfo;
using MemUsage = ::android::meminfo::MemUsage;
static void usage(const char* cmd) {
fprintf(stderr,
"Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
" -i Uses idle page tracking for working set statistics.\n"
" -w Displays statistics for the working set only.\n"
" -W Resets the working set of the process.\n"
" -p Sort by PSS.\n"
" -u Sort by USS.\n"
" -m Sort by mapping order (as read from /proc).\n"
" -h Hide maps with no RSS.\n",
cmd);
}
static void show_footer(uint32_t nelem, const std::string& padding) {
std::string elem(7, '-');
for (uint32_t i = 0; i < nelem; ++i) {
std::cout << std::setw(7) << elem << padding;
}
std::cout << std::endl;
}
static void show_header(const std::vector<std::string>& header, const std::string& padding) {
if (header.empty()) return;
for (size_t i = 0; i < header.size() - 1; ++i) {
std::cout << std::setw(7) << header[i] << padding;
}
std::cout << header.back() << std::endl;
show_footer(header.size() - 1, padding);
}
static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
bool show_wss) {
// clear string stream first.
ss.str("");
// TODO: use ::android::base::StringPrintf instead of <iomanip> here.
if (!show_wss)
ss << std::setw(6) << usage.vss/1024 << padding;
ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
<< usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
<< std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
<< usage.shared_dirty/1024 << padding << std::setw(6)
<< usage.private_clean/1024 << padding << std::setw(6)
<< usage.private_dirty/1024 << padding;
}
static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
const std::vector<std::string> main_header = {"Vss", "Rss", "Pss", "Uss", "ShCl",
"ShDi", "PrCl", "PrDi", "Name"};
const std::vector<std::string> wss_header = {"WRss", "WPss", "WUss", "WShCl",
"WShDi", "WPrCl", "WPrDi", "Name"};
const std::vector<std::string>& header = show_wss ? wss_header : main_header;
// Read process memory stats
const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
// following retains 'procmem' output so as to not break any scripts
// that rely on it.
std::string spaces = " ";
show_header(header, spaces);
const std::string padding = "K ";
std::stringstream ss;
for (auto& vma : maps) {
const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
if (hide_zeroes && vma_stats.rss == 0) {
continue;
}
scan_usage(ss, vma_stats, padding, show_wss);
ss << vma.name << std::endl;
std::cout << ss.str();
}
show_footer(header.size() - 1, spaces);
scan_usage(ss, stats, padding, show_wss);
ss << "TOTAL" << std::endl;
std::cout << ss.str();
return 0;
}
int main(int argc, char* argv[]) {
bool use_pageidle = false;
bool hide_zeroes = false;
bool wss_reset = false;
bool show_wss = false;
int opt;
while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
switch (opt) {
case 'h':
hide_zeroes = true;
break;
case 'i':
use_pageidle = true;
break;
case 'm':
break;
case 'p':
break;
case 'u':
break;
case 'W':
wss_reset = true;
break;
case 'w':
show_wss = true;
break;
case '?':
usage(argv[0]);
return 0;
default:
abort();
}
}
if (optind != (argc - 1)) {
fprintf(stderr, "Need exactly one pid at the end\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
pid_t pid = atoi(argv[optind]);
if (pid == 0) {
std::cerr << "Invalid process id" << std::endl;
exit(EXIT_FAILURE);
}
bool need_wss = wss_reset || show_wss;
ProcMemInfo proc(pid, need_wss);
if (wss_reset) {
if (!proc.WssReset()) {
std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
exit(EXIT_FAILURE);
}
return 0;
}
return show(proc, hide_zeroes, show_wss);
}