| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2018 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include <errno.h> | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 18 | #include <inttypes.h> | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 19 | #include <stdlib.h> | 
|  | 20 | #include <unistd.h> | 
|  | 21 |  | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 22 | #include <iostream> | 
|  | 23 | #include <sstream> | 
|  | 24 | #include <string> | 
|  | 25 | #include <vector> | 
|  | 26 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 27 | #include <android-base/stringprintf.h> | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 28 | #include <meminfo/procmeminfo.h> | 
|  | 29 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 30 | using Vma = ::android::meminfo::Vma; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 31 | using ProcMemInfo = ::android::meminfo::ProcMemInfo; | 
|  | 32 | using MemUsage = ::android::meminfo::MemUsage; | 
|  | 33 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 34 | // Global flags to control procmem output | 
|  | 35 |  | 
|  | 36 | // Set to use page idle bits for working set detection | 
|  | 37 | bool use_pageidle = false; | 
|  | 38 | // hides map entries with zero rss | 
|  | 39 | bool hide_zeroes = false; | 
|  | 40 | // Reset working set and exit | 
|  | 41 | bool reset_wss = false; | 
|  | 42 | // Show working set, mutually exclusive with reset_wss; | 
|  | 43 | bool show_wss = false; | 
|  | 44 |  | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 45 | static void usage(const char* cmd) { | 
|  | 46 | fprintf(stderr, | 
|  | 47 | "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n" | 
|  | 48 | "    -i  Uses idle page tracking for working set statistics.\n" | 
|  | 49 | "    -w  Displays statistics for the working set only.\n" | 
|  | 50 | "    -W  Resets the working set of the process.\n" | 
|  | 51 | "    -p  Sort by PSS.\n" | 
|  | 52 | "    -u  Sort by USS.\n" | 
|  | 53 | "    -m  Sort by mapping order (as read from /proc).\n" | 
|  | 54 | "    -h  Hide maps with no RSS.\n", | 
|  | 55 | cmd); | 
|  | 56 | } | 
|  | 57 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 58 | static void print_separator(std::stringstream& ss) { | 
|  | 59 | if (show_wss) { | 
|  | 60 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------", | 
|  | 61 | "-------", "-------", "-------", "-------", "-------", | 
|  | 62 | "-------", ""); | 
|  | 63 | return; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 64 | } | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 65 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------", | 
|  | 66 | "-------", "-------", "-------", "-------", "-------", | 
|  | 67 | "-------", "-------", ""); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 68 | } | 
|  | 69 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 70 | static void print_header(std::stringstream& ss) { | 
|  | 71 | if (show_wss) { | 
|  | 72 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss", | 
|  | 73 | "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi", | 
|  | 74 | "Name"); | 
|  | 75 | } else { | 
|  | 76 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "Vss", | 
|  | 77 | "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi", | 
|  | 78 | "Name"); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 79 | } | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 80 | print_separator(ss); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 81 | } | 
|  | 82 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 83 | static void print_stats(std::stringstream& ss, const MemUsage& stats) { | 
|  | 84 | if (!show_wss) { | 
|  | 85 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss / 1024); | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 | 
|  | 89 | "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", | 
|  | 90 | stats.rss / 1024, stats.pss / 1024, stats.uss / 1024, | 
|  | 91 | stats.shared_clean / 1024, stats.shared_dirty / 1024, | 
|  | 92 | stats.private_clean / 1024, stats.private_dirty / 1024); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 93 | } | 
|  | 94 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 95 | static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) { | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 96 | std::stringstream ss; | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 97 | print_header(ss); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 98 | for (auto& vma : maps) { | 
|  | 99 | const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage; | 
|  | 100 | if (hide_zeroes && vma_stats.rss == 0) { | 
|  | 101 | continue; | 
|  | 102 | } | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 103 | print_stats(ss, vma_stats); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 104 | ss << vma.name << std::endl; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 105 | } | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 106 | print_separator(ss); | 
|  | 107 | print_stats(ss, proc_stats); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 108 | ss << "TOTAL" << std::endl; | 
|  | 109 | std::cout << ss.str(); | 
|  | 110 |  | 
|  | 111 | return 0; | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | int main(int argc, char* argv[]) { | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 115 | int opt; | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 116 | auto pss_sort = [](const Vma& a, const Vma& b) { | 
|  | 117 | uint64_t pss_a = show_wss ? a.wss.pss : a.usage.pss; | 
|  | 118 | uint64_t pss_b = show_wss ? b.wss.pss : b.usage.pss; | 
|  | 119 | return pss_a > pss_b; | 
|  | 120 | }; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 121 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 122 | auto uss_sort = [](const Vma& a, const Vma& b) { | 
|  | 123 | uint64_t uss_a = show_wss ? a.wss.uss : a.usage.uss; | 
|  | 124 | uint64_t uss_b = show_wss ? b.wss.uss : b.usage.uss; | 
|  | 125 | return uss_a > uss_b; | 
|  | 126 | }; | 
|  | 127 |  | 
|  | 128 | std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 129 | while ((opt = getopt(argc, argv, "himpuWw")) != -1) { | 
|  | 130 | switch (opt) { | 
|  | 131 | case 'h': | 
|  | 132 | hide_zeroes = true; | 
|  | 133 | break; | 
|  | 134 | case 'i': | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 135 | // TODO: libmeminfo doesn't support the flag to chose | 
|  | 136 | // between idle page tracking vs clear_refs. So for now, | 
|  | 137 | // this flag is unused and the library defaults to using | 
|  | 138 | // /proc/<pid>/clear_refs for finding the working set. | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 139 | use_pageidle = true; | 
|  | 140 | break; | 
|  | 141 | case 'm': | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 142 | // this is the default | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 143 | break; | 
|  | 144 | case 'p': | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 145 | sort_func = pss_sort; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 146 | break; | 
|  | 147 | case 'u': | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 148 | sort_func = uss_sort; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 149 | break; | 
|  | 150 | case 'W': | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 151 | reset_wss = true; | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 152 | break; | 
|  | 153 | case 'w': | 
|  | 154 | show_wss = true; | 
|  | 155 | break; | 
|  | 156 | case '?': | 
|  | 157 | usage(argv[0]); | 
|  | 158 | return 0; | 
|  | 159 | default: | 
|  | 160 | abort(); | 
|  | 161 | } | 
|  | 162 | } | 
|  | 163 |  | 
|  | 164 | if (optind != (argc - 1)) { | 
|  | 165 | fprintf(stderr, "Need exactly one pid at the end\n"); | 
|  | 166 | usage(argv[0]); | 
|  | 167 | exit(EXIT_FAILURE); | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | pid_t pid = atoi(argv[optind]); | 
|  | 171 | if (pid == 0) { | 
|  | 172 | std::cerr << "Invalid process id" << std::endl; | 
|  | 173 | exit(EXIT_FAILURE); | 
|  | 174 | } | 
|  | 175 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 176 | if (reset_wss) { | 
| Sandeep Patil | f129199 | 2018-11-19 15:25:18 -0800 | [diff] [blame] | 177 | if (!ProcMemInfo::ResetWorkingSet(pid)) { | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 178 | std::cerr << "Failed to reset working set of pid : " << pid << std::endl; | 
|  | 179 | exit(EXIT_FAILURE); | 
|  | 180 | } | 
|  | 181 | return 0; | 
|  | 182 | } | 
|  | 183 |  | 
| Sandeep Patil | 99ed4a0 | 2018-11-19 17:42:55 -0800 | [diff] [blame] | 184 | ProcMemInfo proc(pid, show_wss); | 
|  | 185 | const MemUsage& proc_stats = show_wss ? proc.Wss() : proc.Usage(); | 
|  | 186 | std::vector<Vma> maps(proc.Maps()); | 
|  | 187 | if (sort_func != nullptr) { | 
|  | 188 | std::sort(maps.begin(), maps.end(), sort_func); | 
|  | 189 | } | 
|  | 190 |  | 
|  | 191 | return show(proc_stats, maps); | 
| Sandeep Patil | 54d8721 | 2018-08-29 17:10:47 -0700 | [diff] [blame] | 192 | } |