| Sandeep Patil | a14119d | 2018-11-16 09:18:57 -0800 | [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 <dirent.h> | 
|  | 18 | #include <errno.h> | 
|  | 19 | #include <inttypes.h> | 
|  | 20 | #include <linux/kernel-page-flags.h> | 
|  | 21 | #include <linux/oom.h> | 
|  | 22 | #include <stdio.h> | 
|  | 23 | #include <stdlib.h> | 
|  | 24 | #include <sys/types.h> | 
|  | 25 | #include <unistd.h> | 
|  | 26 |  | 
|  | 27 | #include <iostream> | 
|  | 28 | #include <memory> | 
|  | 29 | #include <sstream> | 
|  | 30 | #include <vector> | 
|  | 31 |  | 
|  | 32 | #include <android-base/file.h> | 
|  | 33 | #include <android-base/parseint.h> | 
|  | 34 | #include <android-base/stringprintf.h> | 
|  | 35 | #include <android-base/strings.h> | 
|  | 36 |  | 
|  | 37 | #include <meminfo/procmeminfo.h> | 
|  | 38 | #include <meminfo/sysmeminfo.h> | 
|  | 39 |  | 
|  | 40 | using ::android::meminfo::MemUsage; | 
|  | 41 | using ::android::meminfo::ProcMemInfo; | 
|  | 42 |  | 
|  | 43 | struct ProcessRecord { | 
|  | 44 | public: | 
|  | 45 | ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0) | 
|  | 46 | : pid_(-1), | 
|  | 47 | procmem_(nullptr), | 
|  | 48 | oomadj_(OOM_SCORE_ADJ_MAX + 1), | 
|  | 49 | cmdline_(""), | 
|  | 50 | proportional_swap_(0), | 
|  | 51 | unique_swap_(0), | 
|  | 52 | zswap_(0) { | 
|  | 53 | std::unique_ptr<ProcMemInfo> procmem = | 
|  | 54 | std::make_unique<ProcMemInfo>(pid, get_wss, pgflags, pgflags_mask); | 
|  | 55 | if (procmem == nullptr) { | 
|  | 56 | std::cerr << "Failed to create ProcMemInfo for: " << pid << std::endl; | 
|  | 57 | return; | 
|  | 58 | } | 
|  | 59 |  | 
|  | 60 | std::string fname = ::android::base::StringPrintf("/proc/%d/oom_score_adj", pid); | 
|  | 61 | auto oomscore_fp = | 
|  | 62 | std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname.c_str(), "re"), fclose}; | 
|  | 63 | if (oomscore_fp == nullptr) { | 
|  | 64 | std::cerr << "Failed to open oom_score_adj file: " << fname << std::endl; | 
|  | 65 | return; | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | if (fscanf(oomscore_fp.get(), "%d\n", &oomadj_) != 1) { | 
|  | 69 | std::cerr << "Failed to read oomadj from: " << fname << std::endl; | 
|  | 70 | return; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid); | 
|  | 74 | if (!::android::base::ReadFileToString(fname, &cmdline_)) { | 
|  | 75 | std::cerr << "Failed to read cmdline from: " << fname << std::endl; | 
|  | 76 | cmdline_ = "<unknown>"; | 
|  | 77 | } | 
|  | 78 | // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_' | 
|  | 79 | // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00" | 
|  | 80 | // e.g. xtra-daemon, lowi-server | 
|  | 81 | // The .c_str() assignment below then takes care of trimming the cmdline at the first | 
|  | 82 | // 0x00. This is how original procrank worked (luckily) | 
|  | 83 | cmdline_.resize(strlen(cmdline_.c_str())); | 
|  | 84 | procmem_ = std::move(procmem); | 
|  | 85 | pid_ = pid; | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | bool valid() const { return pid_ != -1; } | 
|  | 89 |  | 
|  | 90 | void CalculateSwap(const uint16_t* swap_offset_array, float zram_compression_ratio) { | 
|  | 91 | const std::vector<uint16_t>& swp_offs = procmem_->SwapOffsets(); | 
|  | 92 | for (auto& off : swp_offs) { | 
|  | 93 | proportional_swap_ += getpagesize() / swap_offset_array[off]; | 
|  | 94 | unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0; | 
|  | 95 | zswap_ = proportional_swap_ * zram_compression_ratio; | 
|  | 96 | } | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | // Getters | 
|  | 100 | pid_t pid() const { return pid_; } | 
|  | 101 | const std::string& cmdline() const { return cmdline_; } | 
|  | 102 | int32_t oomadj() const { return oomadj_; } | 
|  | 103 | uint64_t proportional_swap() const { return proportional_swap_; } | 
|  | 104 | uint64_t unique_swap() const { return unique_swap_; } | 
|  | 105 | uint64_t zswap() const { return zswap_; } | 
|  | 106 |  | 
|  | 107 | // Wrappers to ProcMemInfo | 
|  | 108 | const std::vector<uint16_t>& SwapOffsets() const { return procmem_->SwapOffsets(); } | 
|  | 109 | const MemUsage& Usage() const { return procmem_->Usage(); } | 
|  | 110 | const MemUsage& Wss() const { return procmem_->Wss(); } | 
|  | 111 |  | 
|  | 112 | private: | 
|  | 113 | pid_t pid_; | 
|  | 114 | std::unique_ptr<ProcMemInfo> procmem_; | 
|  | 115 | int32_t oomadj_; | 
|  | 116 | std::string cmdline_; | 
|  | 117 | uint64_t proportional_swap_; | 
|  | 118 | uint64_t unique_swap_; | 
|  | 119 | uint64_t zswap_; | 
|  | 120 | }; | 
|  | 121 |  | 
|  | 122 | // Show working set instead of memory consumption | 
|  | 123 | bool show_wss = false; | 
|  | 124 | // Reset working set of each process | 
|  | 125 | bool reset_wss = false; | 
|  | 126 | // Show per-process oom_score_adj column | 
|  | 127 | bool show_oomadj = false; | 
|  | 128 | // True if the device has swap enabled | 
|  | 129 | bool has_swap = false; | 
|  | 130 | // True, if device has zram enabled | 
|  | 131 | bool has_zram = false; | 
|  | 132 | // If zram is enabled, the compression ratio is zram used / swap used. | 
|  | 133 | float zram_compression_ratio = 0.0; | 
|  | 134 | // Sort process in reverse, default is descending | 
|  | 135 | bool reverse_sort = false; | 
|  | 136 |  | 
|  | 137 | // Calculated total memory usage across all processes in the system | 
|  | 138 | uint64_t total_pss = 0; | 
|  | 139 | uint64_t total_uss = 0; | 
|  | 140 | uint64_t total_swap = 0; | 
|  | 141 | uint64_t total_pswap = 0; | 
|  | 142 | uint64_t total_uswap = 0; | 
|  | 143 | uint64_t total_zswap = 0; | 
|  | 144 |  | 
| Sandeep Patil | 7b20fb6 | 2018-12-30 13:28:55 -0800 | [diff] [blame] | 145 | [[noreturn]] static void usage(int exit_status) { | 
|  | 146 | std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" | 
|  | 147 | << std::endl | 
| Sandeep Patil | a14119d | 2018-11-16 09:18:57 -0800 | [diff] [blame] | 148 | << "    -v  Sort by VSS." << std::endl | 
|  | 149 | << "    -r  Sort by RSS." << std::endl | 
|  | 150 | << "    -p  Sort by PSS." << std::endl | 
|  | 151 | << "    -u  Sort by USS." << std::endl | 
|  | 152 | << "    -s  Sort by swap." << std::endl | 
|  | 153 | << "        (Default sort order is PSS.)" << std::endl | 
|  | 154 | << "    -R  Reverse sort order (default is descending)." << std::endl | 
|  | 155 | << "    -c  Only show cached (storage backed) pages" << std::endl | 
|  | 156 | << "    -C  Only show non-cached (ram/swap backed) pages" << std::endl | 
|  | 157 | << "    -k  Only show pages collapsed by KSM" << std::endl | 
|  | 158 | << "    -w  Display statistics for working set only." << std::endl | 
|  | 159 | << "    -W  Reset working set of all processes." << std::endl | 
|  | 160 | << "    -o  Show and sort by oom score against lowmemorykiller thresholds." | 
|  | 161 | << std::endl | 
|  | 162 | << "    -h  Display this help screen." << std::endl; | 
| Sandeep Patil | 7b20fb6 | 2018-12-30 13:28:55 -0800 | [diff] [blame] | 163 | exit(exit_status); | 
| Sandeep Patil | a14119d | 2018-11-16 09:18:57 -0800 | [diff] [blame] | 164 | } | 
|  | 165 |  | 
|  | 166 | static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) { | 
|  | 167 | pids->clear(); | 
|  | 168 | std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir); | 
|  | 169 | if (!procdir) return false; | 
|  | 170 |  | 
|  | 171 | struct dirent* dir; | 
|  | 172 | pid_t pid; | 
|  | 173 | while ((dir = readdir(procdir.get()))) { | 
|  | 174 | if (!::android::base::ParseInt(dir->d_name, &pid)) continue; | 
|  | 175 | if (!for_each_pid(pid)) return false; | 
|  | 176 | pids->push_back(pid); | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | return true; | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | static bool count_swap_offsets(const ProcessRecord& proc, uint16_t* swap_offset_array, | 
|  | 183 | uint32_t size) { | 
|  | 184 | const std::vector<uint16_t>& swp_offs = proc.SwapOffsets(); | 
|  | 185 | for (auto& off : swp_offs) { | 
|  | 186 | if (off >= size) { | 
|  | 187 | std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid() | 
|  | 188 | << std::endl; | 
|  | 189 | return false; | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | if (swap_offset_array[off] == USHRT_MAX) { | 
|  | 193 | std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid() | 
|  | 194 | << std::endl; | 
|  | 195 | return false; | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | swap_offset_array[off]++; | 
|  | 199 | } | 
|  | 200 |  | 
|  | 201 | return true; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | static void print_header(std::stringstream& ss) { | 
|  | 205 | ss.str(""); | 
|  | 206 | ss << ::android::base::StringPrintf("%5s  ", "PID"); | 
|  | 207 | if (show_oomadj) { | 
|  | 208 | ss << ::android::base::StringPrintf("%5s  ", "oom"); | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | if (show_wss) { | 
|  | 212 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss"); | 
|  | 213 | // now swap statistics here, working set pages by definition shouldn't end up in swap. | 
|  | 214 | } else { | 
|  | 215 | ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss"); | 
|  | 216 | if (has_swap) { | 
|  | 217 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap"); | 
|  | 218 | if (has_zram) { | 
|  | 219 | ss << ::android::base::StringPrintf("%7s  ", "ZSwap"); | 
|  | 220 | } | 
|  | 221 | } | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | ss << "cmdline"; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | static void print_process_record(std::stringstream& ss, ProcessRecord& proc) { | 
|  | 228 | ss << ::android::base::StringPrintf("%5d  ", proc.pid()); | 
|  | 229 | if (show_oomadj) { | 
|  | 230 | ss << ::android::base::StringPrintf("%5d  ", proc.oomadj()); | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | if (show_wss) { | 
|  | 234 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", | 
|  | 235 | proc.Wss().rss / 1024, proc.Wss().pss / 1024, | 
|  | 236 | proc.Wss().uss / 1024); | 
|  | 237 | } else { | 
|  | 238 | ss << ::android::base::StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 | 
|  | 239 | "K  ", | 
|  | 240 | proc.Usage().vss / 1024, proc.Usage().rss / 1024, | 
|  | 241 | proc.Usage().pss / 1024, proc.Usage().uss / 1024); | 
|  | 242 | if (has_swap) { | 
|  | 243 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.Usage().swap / 1024); | 
|  | 244 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap() / 1024); | 
|  | 245 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.unique_swap() / 1024); | 
|  | 246 | if (has_zram) { | 
|  | 247 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", (proc.zswap() / 1024)); | 
|  | 248 | } | 
|  | 249 | } | 
|  | 250 | } | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs, | 
|  | 254 | uint16_t* swap_offset_array) { | 
|  | 255 | for (auto& proc : procs) { | 
|  | 256 | total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss; | 
|  | 257 | total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss; | 
|  | 258 | if (!show_wss && has_swap) { | 
|  | 259 | proc.CalculateSwap(swap_offset_array, zram_compression_ratio); | 
|  | 260 | total_swap += proc.Usage().swap; | 
|  | 261 | total_pswap += proc.proportional_swap(); | 
|  | 262 | total_uswap += proc.unique_swap(); | 
|  | 263 | if (has_zram) { | 
|  | 264 | total_zswap += proc.zswap(); | 
|  | 265 | } | 
|  | 266 | } | 
|  | 267 |  | 
|  | 268 | print_process_record(ss, proc); | 
|  | 269 | ss << proc.cmdline() << std::endl; | 
|  | 270 | } | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | static void print_separator(std::stringstream& ss) { | 
|  | 274 | ss << ::android::base::StringPrintf("%5s  ", ""); | 
|  | 275 | if (show_oomadj) { | 
|  | 276 | ss << ::android::base::StringPrintf("%5s  ", ""); | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | if (show_wss) { | 
|  | 280 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "", "------", "------"); | 
|  | 281 | } else { | 
|  | 282 | ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------"); | 
|  | 283 | if (has_swap) { | 
|  | 284 | ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "------", "------", "------"); | 
|  | 285 | if (has_zram) { | 
|  | 286 | ss << ::android::base::StringPrintf("%7s  ", "------"); | 
|  | 287 | } | 
|  | 288 | } | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | ss << ::android::base::StringPrintf("%s", "------"); | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | static void print_totals(std::stringstream& ss) { | 
|  | 295 | ss << ::android::base::StringPrintf("%5s  ", ""); | 
|  | 296 | if (show_oomadj) { | 
|  | 297 | ss << ::android::base::StringPrintf("%5s  ", ""); | 
|  | 298 | } | 
|  | 299 |  | 
|  | 300 | if (show_wss) { | 
|  | 301 | ss << ::android::base::StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", | 
|  | 302 | total_pss / 1024, total_uss / 1024); | 
|  | 303 | } else { | 
|  | 304 | ss << ::android::base::StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "", | 
|  | 305 | total_pss / 1024, total_uss / 1024); | 
|  | 306 | if (has_swap) { | 
|  | 307 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_swap / 1024); | 
|  | 308 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_pswap / 1024); | 
|  | 309 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_uswap / 1024); | 
|  | 310 | if (has_zram) { | 
|  | 311 | ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_zswap / 1024); | 
|  | 312 | } | 
|  | 313 | } | 
|  | 314 | } | 
|  | 315 | ss << "TOTAL"; | 
|  | 316 | } | 
|  | 317 |  | 
|  | 318 | static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) { | 
|  | 319 | if (has_swap) { | 
|  | 320 | ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 | 
|  | 321 | "K in swap " | 
|  | 322 | "(%" PRIu64 "K total swap)", | 
|  | 323 | smi.mem_zram_kb(), | 
|  | 324 | (smi.mem_swap_kb() - smi.mem_swap_free_kb()), | 
|  | 325 | smi.mem_swap_kb()) | 
|  | 326 | << std::endl; | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 | 
|  | 330 | "K buffers, " | 
|  | 331 | "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 | 
|  | 332 | "K slab", | 
|  | 333 | smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(), | 
|  | 334 | smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb()); | 
|  | 335 | } | 
|  | 336 |  | 
|  | 337 | int main(int argc, char* argv[]) { | 
|  | 338 | auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 339 | MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
|  | 340 | MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
|  | 341 | return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss; | 
|  | 342 | }; | 
|  | 343 |  | 
|  | 344 | auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 345 | MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
|  | 346 | MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
|  | 347 | return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss; | 
|  | 348 | }; | 
|  | 349 |  | 
|  | 350 | auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 351 | MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
|  | 352 | MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
|  | 353 | return reverse_sort ? stats_a.rss < stats_b.pss : stats_a.pss > stats_b.pss; | 
|  | 354 | }; | 
|  | 355 |  | 
|  | 356 | auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 357 | MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
|  | 358 | MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
|  | 359 | return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss; | 
|  | 360 | }; | 
|  | 361 |  | 
|  | 362 | auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 363 | MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
|  | 364 | MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
|  | 365 | return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap; | 
|  | 366 | }; | 
|  | 367 |  | 
|  | 368 | auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
|  | 369 | return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj(); | 
|  | 370 | }; | 
|  | 371 |  | 
|  | 372 | // default PSS sort | 
|  | 373 | std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort; | 
|  | 374 |  | 
|  | 375 | // count all pages by default | 
|  | 376 | uint64_t pgflags = 0; | 
|  | 377 | uint64_t pgflags_mask = 0; | 
|  | 378 |  | 
|  | 379 | int opt; | 
|  | 380 | while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) { | 
|  | 381 | switch (opt) { | 
|  | 382 | case 'c': | 
|  | 383 | pgflags = 0; | 
|  | 384 | pgflags_mask = (1 << KPF_SWAPBACKED); | 
|  | 385 | break; | 
|  | 386 | case 'C': | 
|  | 387 | pgflags = (1 << KPF_SWAPBACKED); | 
|  | 388 | pgflags_mask = (1 << KPF_SWAPBACKED); | 
|  | 389 | break; | 
|  | 390 | case 'h': | 
| Sandeep Patil | 7b20fb6 | 2018-12-30 13:28:55 -0800 | [diff] [blame] | 391 | usage(EXIT_SUCCESS); | 
| Sandeep Patil | a14119d | 2018-11-16 09:18:57 -0800 | [diff] [blame] | 392 | case 'k': | 
|  | 393 | pgflags = (1 << KPF_KSM); | 
|  | 394 | pgflags_mask = (1 << KPF_KSM); | 
|  | 395 | break; | 
|  | 396 | case 'o': | 
|  | 397 | proc_sort = oomadj_sort; | 
|  | 398 | show_oomadj = true; | 
|  | 399 | break; | 
|  | 400 | case 'p': | 
|  | 401 | proc_sort = pss_sort; | 
|  | 402 | break; | 
|  | 403 | case 'r': | 
|  | 404 | proc_sort = rss_sort; | 
|  | 405 | break; | 
|  | 406 | case 'R': | 
|  | 407 | reverse_sort = true; | 
|  | 408 | break; | 
|  | 409 | case 's': | 
|  | 410 | proc_sort = swap_sort; | 
|  | 411 | break; | 
|  | 412 | case 'u': | 
|  | 413 | proc_sort = uss_sort; | 
|  | 414 | break; | 
|  | 415 | case 'v': | 
|  | 416 | proc_sort = vss_sort; | 
|  | 417 | break; | 
|  | 418 | case 'w': | 
|  | 419 | show_wss = true; | 
|  | 420 | break; | 
|  | 421 | case 'W': | 
|  | 422 | reset_wss = true; | 
|  | 423 | break; | 
|  | 424 | default: | 
| Sandeep Patil | 7b20fb6 | 2018-12-30 13:28:55 -0800 | [diff] [blame] | 425 | usage(EXIT_FAILURE); | 
| Sandeep Patil | a14119d | 2018-11-16 09:18:57 -0800 | [diff] [blame] | 426 | } | 
|  | 427 | } | 
|  | 428 |  | 
|  | 429 | std::vector<pid_t> pids; | 
|  | 430 | std::vector<ProcessRecord> procs; | 
|  | 431 | if (reset_wss) { | 
|  | 432 | if (!read_all_pids(&pids, | 
|  | 433 | [&](pid_t pid) -> bool { return ProcMemInfo::ResetWorkingSet(pid); })) { | 
|  | 434 | std::cerr << "Failed to reset working set of all processes" << std::endl; | 
|  | 435 | exit(EXIT_FAILURE); | 
|  | 436 | } | 
|  | 437 | // we are done, all other options passed to procrank are ignored in the presence of '-W' | 
|  | 438 | return 0; | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | ::android::meminfo::SysMemInfo smi; | 
|  | 442 | if (!smi.ReadMemInfo()) { | 
|  | 443 | std::cerr << "Failed to get system memory info" << std::endl; | 
|  | 444 | exit(EXIT_FAILURE); | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | // Figure out swap and zram | 
|  | 448 | uint64_t swap_total = smi.mem_swap_kb() * 1024; | 
|  | 449 | has_swap = swap_total > 0; | 
|  | 450 | // Allocate the swap array | 
|  | 451 | auto swap_offset_array = std::make_unique<uint16_t[]>(swap_total / getpagesize()); | 
|  | 452 | if (has_swap) { | 
|  | 453 | has_zram = smi.mem_zram_kb() > 0; | 
|  | 454 | if (has_zram) { | 
|  | 455 | zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) / | 
|  | 456 | (smi.mem_swap_kb() - smi.mem_swap_free_kb()); | 
|  | 457 | } | 
|  | 458 | } | 
|  | 459 |  | 
|  | 460 | auto mark_swap_usage = [&](pid_t pid) -> bool { | 
|  | 461 | ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask); | 
|  | 462 | if (!proc.valid()) { | 
|  | 463 | std::cerr << "Failed to create process record for: " << pid << std::endl; | 
|  | 464 | return false; | 
|  | 465 | } | 
|  | 466 |  | 
|  | 467 | // Skip processes with no memory mappings | 
|  | 468 | uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss; | 
|  | 469 | if (vss == 0) return true; | 
|  | 470 |  | 
|  | 471 | // collect swap_offset counts from all processes in 1st pass | 
|  | 472 | if (!show_wss && has_swap && | 
|  | 473 | !count_swap_offsets(proc, swap_offset_array.get(), swap_total / getpagesize())) { | 
|  | 474 | std::cerr << "Failed to count swap offsets for process: " << pid << std::endl; | 
|  | 475 | return false; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | procs.push_back(std::move(proc)); | 
|  | 479 | return true; | 
|  | 480 | }; | 
|  | 481 |  | 
|  | 482 | // Get a list of all pids currently running in the system in | 
|  | 483 | // 1st pass through all processes. Mark each swap offset used by the process as we find them | 
|  | 484 | // for calculating proportional swap usage later. | 
|  | 485 | if (!read_all_pids(&pids, mark_swap_usage)) { | 
|  | 486 | std::cerr << "Failed to read all pids from the system" << std::endl; | 
|  | 487 | exit(EXIT_FAILURE); | 
|  | 488 | } | 
|  | 489 |  | 
|  | 490 | std::stringstream ss; | 
|  | 491 | if (procs.empty()) { | 
|  | 492 | // This would happen in corner cases where procrank is being run to find KSM usage on a | 
|  | 493 | // system with no KSM and combined with working set determination as follows | 
|  | 494 | //   procrank -w -u -k | 
|  | 495 | //   procrank -w -s -k | 
|  | 496 | //   procrank -w -o -k | 
|  | 497 | ss << "<empty>" << std::endl << std::endl; | 
|  | 498 | print_sysmeminfo(ss, smi); | 
|  | 499 | ss << std::endl; | 
|  | 500 | std::cout << ss.str(); | 
|  | 501 | return 0; | 
|  | 502 | } | 
|  | 503 |  | 
|  | 504 | // Sort all process records, default is PSS descending | 
|  | 505 | std::sort(procs.begin(), procs.end(), proc_sort); | 
|  | 506 |  | 
|  | 507 | // start dumping output in string stream | 
|  | 508 | print_header(ss); | 
|  | 509 | ss << std::endl; | 
|  | 510 |  | 
|  | 511 | // 2nd pass to calculate and get per process stats to add them up | 
|  | 512 | print_processes(ss, procs, swap_offset_array.get()); | 
|  | 513 |  | 
|  | 514 | // Add separator to output | 
|  | 515 | print_separator(ss); | 
|  | 516 | ss << std::endl; | 
|  | 517 |  | 
|  | 518 | // Add totals to output | 
|  | 519 | print_totals(ss); | 
|  | 520 | ss << std::endl << std::endl; | 
|  | 521 |  | 
|  | 522 | // Add system information at the end | 
|  | 523 | print_sysmeminfo(ss, smi); | 
|  | 524 | ss << std::endl; | 
|  | 525 |  | 
|  | 526 | // dump on the screen | 
|  | 527 | std::cout << ss.str(); | 
|  | 528 |  | 
|  | 529 | return 0; | 
|  | 530 | } |