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