/* * 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 #include #include #include #include #include #include #include #include #include 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& 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 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//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 maps(proc.Maps()); if (sort_func != nullptr) { std::sort(maps.begin(), maps.end(), sort_func); } return show(proc_stats, maps); }