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 } 220