| Sandeep Patil | 061b713 | 2019-01-19 21:11:01 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2019 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 <getopt.h> | 
|  | 18 | #include <inttypes.h> | 
|  | 19 | #include <stdio.h> | 
|  | 20 | #include <stdlib.h> | 
|  | 21 | #include <sys/types.h> | 
|  | 22 | #include <unistd.h> | 
|  | 23 |  | 
|  | 24 | #include <algorithm> | 
|  | 25 | #include <memory> | 
|  | 26 | #include <string> | 
|  | 27 | #include <vector> | 
|  | 28 |  | 
|  | 29 | #include <meminfo/pageacct.h> | 
|  | 30 | #include <meminfo/procmeminfo.h> | 
|  | 31 |  | 
|  | 32 | using ::android::meminfo::ProcMemInfo; | 
|  | 33 | using ::android::meminfo::Vma; | 
|  | 34 |  | 
|  | 35 | // Global options | 
|  | 36 | static int32_t g_delay = 0; | 
|  | 37 | static int32_t g_total = 2; | 
|  | 38 | static pid_t g_pid = -1; | 
|  | 39 |  | 
|  | 40 | [[noreturn]] static void usage(int exit_status) { | 
|  | 41 | fprintf(stderr, | 
|  | 42 | "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n" | 
|  | 43 | "-d\tdelay between each working set sample (default 0)\n" | 
|  | 44 | "-n\ttotal number of refreshes before we exit (default 2)\n", | 
|  | 45 | getprogname()); | 
|  | 46 |  | 
|  | 47 | exit(exit_status); | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | static void print_header() { | 
|  | 51 | const char* addr1 = "           start              end "; | 
|  | 52 | const char* addr2 = "            addr             addr "; | 
|  | 53 |  | 
|  | 54 | printf("%s  virtual                        shared    shared   private   private\n", addr1); | 
|  | 55 | printf("%s     size       RSS       PSS     clean     dirty     clean     dirty      swap   " | 
|  | 56 | "swapPSS", | 
|  | 57 | addr2); | 
|  | 58 | printf(" object\n"); | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | static void print_divider() { | 
|  | 62 | printf("---------------- ---------------- "); | 
|  | 63 | printf("--------- --------- --------- --------- --------- --------- --------- --------- " | 
|  | 64 | "--------- "); | 
|  | 65 | printf("------------------------------\n"); | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | static void print_vma(const Vma& v) { | 
|  | 69 | printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end); | 
|  | 70 | printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 | 
|  | 71 | "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ", | 
|  | 72 | v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024, | 
|  | 73 | v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024, | 
|  | 74 | v.usage.swap / 1024, v.usage.swap_pss / 1024); | 
|  | 75 | printf("%s\n", v.name.c_str()); | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | static bool same_vma(const Vma& cur, const Vma& last) { | 
|  | 79 | return (cur.start == last.start && cur.end == last.end && cur.name == last.name && | 
|  | 80 | cur.flags == last.flags && cur.offset == last.offset); | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 | static Vma diff_vma_params(const Vma& cur, const Vma& last) { | 
|  | 84 | Vma res; | 
|  | 85 | res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean | 
|  | 86 | ? cur.usage.shared_clean - last.usage.shared_clean | 
|  | 87 | : 0; | 
|  | 88 | res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty | 
|  | 89 | ? cur.usage.shared_dirty - last.usage.shared_dirty | 
|  | 90 | : 0; | 
|  | 91 | res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean | 
|  | 92 | ? cur.usage.private_clean - last.usage.private_clean | 
|  | 93 | : 0; | 
|  | 94 | res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty | 
|  | 95 | ? cur.usage.private_dirty - last.usage.private_dirty | 
|  | 96 | : 0; | 
|  | 97 |  | 
|  | 98 | res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0; | 
|  | 99 | res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0; | 
|  | 100 | res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0; | 
|  | 101 | res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0; | 
|  | 102 | res.usage.swap_pss = | 
|  | 103 | cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0; | 
|  | 104 |  | 
|  | 105 | // set vma properties to the same as the current one. | 
|  | 106 | res.start = cur.start; | 
|  | 107 | res.end = cur.end; | 
|  | 108 | res.offset = cur.offset; | 
|  | 109 | res.flags = cur.flags; | 
|  | 110 | res.name = cur.name; | 
|  | 111 | return res; | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) { | 
|  | 115 | res->clear(); | 
|  | 116 | auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; }; | 
|  | 117 | std::sort(wss.begin(), wss.end(), vma_sorter); | 
|  | 118 | std::sort(old.begin(), old.end(), vma_sorter); | 
|  | 119 | if (old.empty()) { | 
|  | 120 | *res = wss; | 
|  | 121 | return; | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | for (auto& i : wss) { | 
|  | 125 | bool found_same_vma = false; | 
|  | 126 | // TODO: This is highly inefficient, fix it if it takes | 
|  | 127 | // too long. Worst case will be system_server | 
|  | 128 | for (auto& j : old) { | 
|  | 129 | if (same_vma(i, j)) { | 
|  | 130 | res->emplace_back(diff_vma_params(i, j)); | 
|  | 131 | found_same_vma = true; | 
|  | 132 | break; | 
|  | 133 | } | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | if (!found_same_vma) { | 
|  | 137 | res->emplace_back(i); | 
|  | 138 | } | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | std::sort(res->begin(), res->end(), vma_sorter); | 
|  | 142 | return; | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | static int workingset() { | 
|  | 146 | std::vector<Vma> last_wss = {}; | 
|  | 147 | std::vector<Vma> diff_wss = {}; | 
|  | 148 | uint32_t nr_refresh = 0; | 
|  | 149 |  | 
|  | 150 | while (true) { | 
|  | 151 | std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true); | 
|  | 152 | std::vector<Vma> wss = proc_mem->MapsWithPageIdle(); | 
|  | 153 |  | 
|  | 154 | diff_workingset(wss, last_wss, &diff_wss); | 
|  | 155 | diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(), | 
|  | 156 | [](const auto& v) { return v.usage.rss == 0; }), | 
|  | 157 | diff_wss.end()); | 
|  | 158 | if ((nr_refresh % 5) == 0) { | 
|  | 159 | print_header(); | 
|  | 160 | print_divider(); | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | for (const auto& v : diff_wss) { | 
|  | 164 | print_vma(v); | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | nr_refresh++; | 
|  | 168 | if (nr_refresh == g_total) { | 
|  | 169 | break; | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | last_wss = wss; | 
|  | 173 | sleep(g_delay); | 
|  | 174 | print_divider(); | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | return 0; | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | int main(int argc, char* argv[]) { | 
|  | 181 | struct option longopts[] = { | 
|  | 182 | {"help", no_argument, nullptr, 'h'}, | 
|  | 183 | {0, 0, nullptr, 0}, | 
|  | 184 | }; | 
|  | 185 |  | 
|  | 186 | int opt; | 
|  | 187 | while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) { | 
|  | 188 | switch (opt) { | 
|  | 189 | case 'd': | 
|  | 190 | g_delay = atoi(optarg); | 
|  | 191 | break; | 
|  | 192 | case 'n': | 
|  | 193 | g_total = atoi(optarg); | 
|  | 194 | break; | 
|  | 195 | case 'h': | 
|  | 196 | usage(EXIT_SUCCESS); | 
|  | 197 | default: | 
|  | 198 | usage(EXIT_FAILURE); | 
|  | 199 | } | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | if ((argc - 1) < optind) { | 
|  | 203 | fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n"); | 
|  | 204 | usage(EXIT_FAILURE); | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | g_pid = atoi(argv[optind]); | 
|  | 208 | if (g_pid <= 0) { | 
|  | 209 | fprintf(stderr, "Invalid process id %s\n", argv[optind]); | 
|  | 210 | usage(EXIT_FAILURE); | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | if (!::android::meminfo::PageAcct::KernelHasPageIdle()) { | 
|  | 214 | fprintf(stderr, "Missing support for Idle page tracking in the kernel\n"); | 
|  | 215 | return 0; | 
|  | 216 | } | 
|  | 217 |  | 
|  | 218 | return workingset(); | 
|  | 219 | } |