From 99ed4a0b4efbedf52020741b08f0180ea9b53755 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Mon, 19 Nov 2018 17:42:55 -0800 Subject: [PATCH] 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 --- libmeminfo/tools/Android.bp | 1 + libmeminfo/tools/procmem.cpp | 131 ++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 54 deletions(-) diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp index 7c4166023..715d63df5 100644 --- a/libmeminfo/tools/Android.bp +++ b/libmeminfo/tools/Android.bp @@ -22,6 +22,7 @@ cc_binary { srcs: ["procmem.cpp"], shared_libs: [ + "libbase", "libmeminfo", ], } diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp index d30c2f2ba..56de12d67 100644 --- a/libmeminfo/tools/procmem.cpp +++ b/libmeminfo/tools/procmem.cpp @@ -15,20 +15,33 @@ */ #include +#include #include #include -#include #include #include #include #include +#include #include +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& 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 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 main_header = {"Vss", "Rss", "Pss", "Uss", "ShCl", - "ShDi", "PrCl", "PrDi", "Name"}; - const std::vector wss_header = {"WRss", "WPss", "WUss", "WShCl", - "WShDi", "WPrCl", "WPrDi", "Name"}; - const std::vector& 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& 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 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//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 maps(proc.Maps()); + if (sort_func != nullptr) { + std::sort(maps.begin(), maps.end(), sort_func); + } + + return show(proc_stats, maps); }