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