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

Commit 99ed4a0b authored by Sandeep Patil's avatar Sandeep Patil
Browse files

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: default avatarSandeep Patil <sspatil@google.com>
parent a14119d0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ cc_binary {

    srcs: ["procmem.cpp"],
    shared_libs: [
        "libbase",
        "libmeminfo",
    ],
}
+78 −55
Original line number Diff line number Diff line
@@ -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);
    }

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();
    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);
}

    // 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);
}