procmem2: Finish implementing all options

In the same time replace iomanipulators with
::android::base::StringPrintf which makes the output much more readable
and the overall program simpler.

Bug: 114325007
Bug: 111694435
Test: procmem 1
Test: procmem -W 1
Test: procmem -u 1
Test: procmem -p 1
Test: procmem -w 1
Test: procmem -w -u 1
Test: procmem -w -p 1

Change-Id: Ibf7ef59b24dfcb851c78a3b2fad672d96a708b98
Signed-off-by: Sandeep Patil <sspatil@google.com>
This commit is contained in:
Sandeep Patil 2018-11-19 17:42:55 -08:00
parent a14119d069
commit 99ed4a0b4e
2 changed files with 78 additions and 54 deletions

View file

@ -22,6 +22,7 @@ cc_binary {
srcs: ["procmem.cpp"],
shared_libs: [
"libbase",
"libmeminfo",
],
}

View file

@ -15,20 +15,33 @@
*/
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <android-base/stringprintf.h>
#include <meminfo/procmeminfo.h>
using Vma = ::android::meminfo::Vma;
using ProcMemInfo = ::android::meminfo::ProcMemInfo;
using MemUsage = ::android::meminfo::MemUsage;
// Global flags to control procmem output
// Set to use page idle bits for working set detection
bool use_pageidle = false;
// hides map entries with zero rss
bool hide_zeroes = false;
// Reset working set and exit
bool reset_wss = false;
// Show working set, mutually exclusive with reset_wss;
bool show_wss = false;
static void usage(const char* cmd) {
fprintf(stderr,
"Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
@ -42,66 +55,56 @@ static void usage(const char* cmd) {
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;
static void print_separator(std::stringstream& ss) {
if (show_wss) {
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
"-------", "-------", "-------", "-------", "-------",
"-------", "");
return;
}
std::cout << std::endl;
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
"-------", "-------", "-------", "-------", "-------",
"-------", "-------", "");
}
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;
static void print_header(std::stringstream& ss) {
if (show_wss) {
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %s\n", "WRss",
"WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
"Name");
} else {
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n", "Vss",
"Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi",
"Name");
}
std::cout << header.back() << std::endl;
show_footer(header.size() - 1, padding);
print_separator(ss);
}
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 void print_stats(std::stringstream& ss, const MemUsage& stats) {
if (!show_wss) {
ss << ::android::base::StringPrintf("%6" PRIu64 "K ", stats.vss / 1024);
}
ss << ::android::base::StringPrintf("%6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64
"K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ",
stats.rss / 1024, stats.pss / 1024, stats.uss / 1024,
stats.shared_clean / 1024, stats.shared_dirty / 1024,
stats.private_clean / 1024, stats.private_dirty / 1024);
}
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 ";
static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
std::stringstream ss;
print_header(ss);
for (auto& vma : maps) {
const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
if (hide_zeroes && vma_stats.rss == 0) {
continue;
}
scan_usage(ss, vma_stats, padding, show_wss);
print_stats(ss, vma_stats);
ss << vma.name << std::endl;
std::cout << ss.str();
}
show_footer(header.size() - 1, spaces);
scan_usage(ss, stats, padding, show_wss);
print_separator(ss);
print_stats(ss, proc_stats);
ss << "TOTAL" << std::endl;
std::cout << ss.str();
@ -109,28 +112,43 @@ static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
}
int main(int argc, char* argv[]) {
bool use_pageidle = false;
bool hide_zeroes = false;
bool wss_reset = false;
bool show_wss = false;
int opt;
auto pss_sort = [](const Vma& a, const Vma& b) {
uint64_t pss_a = show_wss ? a.wss.pss : a.usage.pss;
uint64_t pss_b = show_wss ? b.wss.pss : b.usage.pss;
return pss_a > pss_b;
};
auto uss_sort = [](const Vma& a, const Vma& b) {
uint64_t uss_a = show_wss ? a.wss.uss : a.usage.uss;
uint64_t uss_b = show_wss ? b.wss.uss : b.usage.uss;
return uss_a > uss_b;
};
std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr;
while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
switch (opt) {
case 'h':
hide_zeroes = true;
break;
case 'i':
// TODO: libmeminfo doesn't support the flag to chose
// between idle page tracking vs clear_refs. So for now,
// this flag is unused and the library defaults to using
// /proc/<pid>/clear_refs for finding the working set.
use_pageidle = true;
break;
case 'm':
// this is the default
break;
case 'p':
sort_func = pss_sort;
break;
case 'u':
sort_func = uss_sort;
break;
case 'W':
wss_reset = true;
reset_wss = true;
break;
case 'w':
show_wss = true;
@ -155,8 +173,7 @@ int main(int argc, char* argv[]) {
exit(EXIT_FAILURE);
}
bool need_wss = wss_reset || show_wss;
if (wss_reset) {
if (reset_wss) {
if (!ProcMemInfo::ResetWorkingSet(pid)) {
std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
exit(EXIT_FAILURE);
@ -164,6 +181,12 @@ int main(int argc, char* argv[]) {
return 0;
}
ProcMemInfo proc(pid, need_wss);
return show(proc, hide_zeroes, show_wss);
ProcMemInfo proc(pid, show_wss);
const MemUsage& proc_stats = show_wss ? proc.Wss() : proc.Usage();
std::vector<Vma> maps(proc.Maps());
if (sort_func != nullptr) {
std::sort(maps.begin(), maps.end(), sort_func);
}
return show(proc_stats, maps);
}