172 lines
5.5 KiB
C++
172 lines
5.5 KiB
C++
/*
|
|
* 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);
|
|
}
|