| 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 |  | 
 | 145 | static void usage(const char* myname) { | 
 | 146 |     std::cerr << "Usage: " << myname << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" << std::endl | 
 | 147 |               << "    -v  Sort by VSS." << std::endl | 
 | 148 |               << "    -r  Sort by RSS." << std::endl | 
 | 149 |               << "    -p  Sort by PSS." << std::endl | 
 | 150 |               << "    -u  Sort by USS." << std::endl | 
 | 151 |               << "    -s  Sort by swap." << std::endl | 
 | 152 |               << "        (Default sort order is PSS.)" << std::endl | 
 | 153 |               << "    -R  Reverse sort order (default is descending)." << std::endl | 
 | 154 |               << "    -c  Only show cached (storage backed) pages" << std::endl | 
 | 155 |               << "    -C  Only show non-cached (ram/swap backed) pages" << std::endl | 
 | 156 |               << "    -k  Only show pages collapsed by KSM" << std::endl | 
 | 157 |               << "    -w  Display statistics for working set only." << std::endl | 
 | 158 |               << "    -W  Reset working set of all processes." << std::endl | 
 | 159 |               << "    -o  Show and sort by oom score against lowmemorykiller thresholds." | 
 | 160 |               << std::endl | 
 | 161 |               << "    -h  Display this help screen." << std::endl; | 
 | 162 | } | 
 | 163 |  | 
 | 164 | static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) { | 
 | 165 |     pids->clear(); | 
 | 166 |     std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir); | 
 | 167 |     if (!procdir) return false; | 
 | 168 |  | 
 | 169 |     struct dirent* dir; | 
 | 170 |     pid_t pid; | 
 | 171 |     while ((dir = readdir(procdir.get()))) { | 
 | 172 |         if (!::android::base::ParseInt(dir->d_name, &pid)) continue; | 
 | 173 |         if (!for_each_pid(pid)) return false; | 
 | 174 |         pids->push_back(pid); | 
 | 175 |     } | 
 | 176 |  | 
 | 177 |     return true; | 
 | 178 | } | 
 | 179 |  | 
 | 180 | static bool count_swap_offsets(const ProcessRecord& proc, uint16_t* swap_offset_array, | 
 | 181 |                                uint32_t size) { | 
 | 182 |     const std::vector<uint16_t>& swp_offs = proc.SwapOffsets(); | 
 | 183 |     for (auto& off : swp_offs) { | 
 | 184 |         if (off >= size) { | 
 | 185 |             std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid() | 
 | 186 |                       << std::endl; | 
 | 187 |             return false; | 
 | 188 |         } | 
 | 189 |  | 
 | 190 |         if (swap_offset_array[off] == USHRT_MAX) { | 
 | 191 |             std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid() | 
 | 192 |                       << std::endl; | 
 | 193 |             return false; | 
 | 194 |         } | 
 | 195 |  | 
 | 196 |         swap_offset_array[off]++; | 
 | 197 |     } | 
 | 198 |  | 
 | 199 |     return true; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static void print_header(std::stringstream& ss) { | 
 | 203 |     ss.str(""); | 
 | 204 |     ss << ::android::base::StringPrintf("%5s  ", "PID"); | 
 | 205 |     if (show_oomadj) { | 
 | 206 |         ss << ::android::base::StringPrintf("%5s  ", "oom"); | 
 | 207 |     } | 
 | 208 |  | 
 | 209 |     if (show_wss) { | 
 | 210 |         ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss"); | 
 | 211 |         // now swap statistics here, working set pages by definition shouldn't end up in swap. | 
 | 212 |     } else { | 
 | 213 |         ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss"); | 
 | 214 |         if (has_swap) { | 
 | 215 |             ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap"); | 
 | 216 |             if (has_zram) { | 
 | 217 |                 ss << ::android::base::StringPrintf("%7s  ", "ZSwap"); | 
 | 218 |             } | 
 | 219 |         } | 
 | 220 |     } | 
 | 221 |  | 
 | 222 |     ss << "cmdline"; | 
 | 223 | } | 
 | 224 |  | 
 | 225 | static void print_process_record(std::stringstream& ss, ProcessRecord& proc) { | 
 | 226 |     ss << ::android::base::StringPrintf("%5d  ", proc.pid()); | 
 | 227 |     if (show_oomadj) { | 
 | 228 |         ss << ::android::base::StringPrintf("%5d  ", proc.oomadj()); | 
 | 229 |     } | 
 | 230 |  | 
 | 231 |     if (show_wss) { | 
 | 232 |         ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", | 
 | 233 |                                             proc.Wss().rss / 1024, proc.Wss().pss / 1024, | 
 | 234 |                                             proc.Wss().uss / 1024); | 
 | 235 |     } else { | 
 | 236 |         ss << ::android::base::StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 | 
 | 237 |                                             "K  ", | 
 | 238 |                                             proc.Usage().vss / 1024, proc.Usage().rss / 1024, | 
 | 239 |                                             proc.Usage().pss / 1024, proc.Usage().uss / 1024); | 
 | 240 |         if (has_swap) { | 
 | 241 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.Usage().swap / 1024); | 
 | 242 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap() / 1024); | 
 | 243 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.unique_swap() / 1024); | 
 | 244 |             if (has_zram) { | 
 | 245 |                 ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", (proc.zswap() / 1024)); | 
 | 246 |             } | 
 | 247 |         } | 
 | 248 |     } | 
 | 249 | } | 
 | 250 |  | 
 | 251 | static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs, | 
 | 252 |                             uint16_t* swap_offset_array) { | 
 | 253 |     for (auto& proc : procs) { | 
 | 254 |         total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss; | 
 | 255 |         total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss; | 
 | 256 |         if (!show_wss && has_swap) { | 
 | 257 |             proc.CalculateSwap(swap_offset_array, zram_compression_ratio); | 
 | 258 |             total_swap += proc.Usage().swap; | 
 | 259 |             total_pswap += proc.proportional_swap(); | 
 | 260 |             total_uswap += proc.unique_swap(); | 
 | 261 |             if (has_zram) { | 
 | 262 |                 total_zswap += proc.zswap(); | 
 | 263 |             } | 
 | 264 |         } | 
 | 265 |  | 
 | 266 |         print_process_record(ss, proc); | 
 | 267 |         ss << proc.cmdline() << std::endl; | 
 | 268 |     } | 
 | 269 | } | 
 | 270 |  | 
 | 271 | static void print_separator(std::stringstream& ss) { | 
 | 272 |     ss << ::android::base::StringPrintf("%5s  ", ""); | 
 | 273 |     if (show_oomadj) { | 
 | 274 |         ss << ::android::base::StringPrintf("%5s  ", ""); | 
 | 275 |     } | 
 | 276 |  | 
 | 277 |     if (show_wss) { | 
 | 278 |         ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "", "------", "------"); | 
 | 279 |     } else { | 
 | 280 |         ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------"); | 
 | 281 |         if (has_swap) { | 
 | 282 |             ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "------", "------", "------"); | 
 | 283 |             if (has_zram) { | 
 | 284 |                 ss << ::android::base::StringPrintf("%7s  ", "------"); | 
 | 285 |             } | 
 | 286 |         } | 
 | 287 |     } | 
 | 288 |  | 
 | 289 |     ss << ::android::base::StringPrintf("%s", "------"); | 
 | 290 | } | 
 | 291 |  | 
 | 292 | static void print_totals(std::stringstream& ss) { | 
 | 293 |     ss << ::android::base::StringPrintf("%5s  ", ""); | 
 | 294 |     if (show_oomadj) { | 
 | 295 |         ss << ::android::base::StringPrintf("%5s  ", ""); | 
 | 296 |     } | 
 | 297 |  | 
 | 298 |     if (show_wss) { | 
 | 299 |         ss << ::android::base::StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", | 
 | 300 |                                             total_pss / 1024, total_uss / 1024); | 
 | 301 |     } else { | 
 | 302 |         ss << ::android::base::StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "", | 
 | 303 |                                             total_pss / 1024, total_uss / 1024); | 
 | 304 |         if (has_swap) { | 
 | 305 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_swap / 1024); | 
 | 306 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_pswap / 1024); | 
 | 307 |             ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_uswap / 1024); | 
 | 308 |             if (has_zram) { | 
 | 309 |                 ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_zswap / 1024); | 
 | 310 |             } | 
 | 311 |         } | 
 | 312 |     } | 
 | 313 |     ss << "TOTAL"; | 
 | 314 | } | 
 | 315 |  | 
 | 316 | static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) { | 
 | 317 |     if (has_swap) { | 
 | 318 |         ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 | 
 | 319 |                                             "K in swap " | 
 | 320 |                                             "(%" PRIu64 "K total swap)", | 
 | 321 |                                             smi.mem_zram_kb(), | 
 | 322 |                                             (smi.mem_swap_kb() - smi.mem_swap_free_kb()), | 
 | 323 |                                             smi.mem_swap_kb()) | 
 | 324 |            << std::endl; | 
 | 325 |     } | 
 | 326 |  | 
 | 327 |     ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 | 
 | 328 |                                         "K buffers, " | 
 | 329 |                                         "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 | 
 | 330 |                                         "K slab", | 
 | 331 |                                         smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(), | 
 | 332 |                                         smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb()); | 
 | 333 | } | 
 | 334 |  | 
 | 335 | int main(int argc, char* argv[]) { | 
 | 336 |     auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 337 |         MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
 | 338 |         MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
 | 339 |         return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss; | 
 | 340 |     }; | 
 | 341 |  | 
 | 342 |     auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 343 |         MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
 | 344 |         MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
 | 345 |         return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss; | 
 | 346 |     }; | 
 | 347 |  | 
 | 348 |     auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 349 |         MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
 | 350 |         MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
 | 351 |         return reverse_sort ? stats_a.rss < stats_b.pss : stats_a.pss > stats_b.pss; | 
 | 352 |     }; | 
 | 353 |  | 
 | 354 |     auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 355 |         MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
 | 356 |         MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
 | 357 |         return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss; | 
 | 358 |     }; | 
 | 359 |  | 
 | 360 |     auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 361 |         MemUsage stats_a = show_wss ? a.Wss() : a.Usage(); | 
 | 362 |         MemUsage stats_b = show_wss ? b.Wss() : b.Usage(); | 
 | 363 |         return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap; | 
 | 364 |     }; | 
 | 365 |  | 
 | 366 |     auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) { | 
 | 367 |         return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj(); | 
 | 368 |     }; | 
 | 369 |  | 
 | 370 |     // default PSS sort | 
 | 371 |     std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort; | 
 | 372 |  | 
 | 373 |     // count all pages by default | 
 | 374 |     uint64_t pgflags = 0; | 
 | 375 |     uint64_t pgflags_mask = 0; | 
 | 376 |  | 
 | 377 |     int opt; | 
 | 378 |     while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) { | 
 | 379 |         switch (opt) { | 
 | 380 |             case 'c': | 
 | 381 |                 pgflags = 0; | 
 | 382 |                 pgflags_mask = (1 << KPF_SWAPBACKED); | 
 | 383 |                 break; | 
 | 384 |             case 'C': | 
 | 385 |                 pgflags = (1 << KPF_SWAPBACKED); | 
 | 386 |                 pgflags_mask = (1 << KPF_SWAPBACKED); | 
 | 387 |                 break; | 
 | 388 |             case 'h': | 
 | 389 |                 usage(argv[0]); | 
 | 390 |                 return 0; | 
 | 391 |                 break; | 
 | 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: | 
 | 425 |                 abort(); | 
 | 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 | } |