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