blob: 47881ed5b23b9587ea39d584d59087408421848a [file] [log] [blame]
/*
* 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 <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#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;
[[noreturn]] static void usage(int exit_status) {
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",
getprogname());
exit(exit_status);
}
static void print_separator(std::stringstream& ss) {
if (show_wss) {
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
"-------", "-------", "-------", "-------", "-------",
"-------", "");
return;
}
ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
"-------", "-------", "-------", "-------", "-------",
"-------", "-------", "");
}
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");
}
print_separator(ss);
}
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(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
std::stringstream ss;
print_header(ss);
for (auto& vma : maps) {
const MemUsage& vma_stats = vma.usage;
if (hide_zeroes && vma_stats.rss == 0) {
continue;
}
print_stats(ss, vma_stats);
ss << vma.name << std::endl;
}
print_separator(ss);
print_stats(ss, proc_stats);
ss << "TOTAL" << std::endl;
std::cout << ss.str();
return 0;
}
int main(int argc, char* argv[]) {
int opt;
auto pss_sort = [](const Vma& a, const Vma& b) {
uint64_t pss_a = a.usage.pss;
uint64_t pss_b = b.usage.pss;
return pss_a > pss_b;
};
auto uss_sort = [](const Vma& a, const Vma& b) {
uint64_t uss_a = a.usage.uss;
uint64_t uss_b = 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':
reset_wss = true;
break;
case 'w':
show_wss = true;
break;
case '?':
usage(EXIT_SUCCESS);
default:
usage(EXIT_FAILURE);
}
}
if (optind != (argc - 1)) {
fprintf(stderr, "Need exactly one pid at the end\n");
usage(EXIT_FAILURE);
}
pid_t pid = atoi(argv[optind]);
if (pid == 0) {
std::cerr << "Invalid process id" << std::endl;
exit(EXIT_FAILURE);
}
if (reset_wss) {
if (!ProcMemInfo::ResetWorkingSet(pid)) {
std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
exit(EXIT_FAILURE);
}
return 0;
}
ProcMemInfo proc(pid, show_wss);
const MemUsage& proc_stats = 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);
}