blob: 3571e41732969dd00433a9b7694daf866290779e [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 <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);
}