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/mman.h> 22 #include <sys/signal.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 #include <memory> 27 #include <string> 28 #include <vector> 29 30 #include <android-base/stringprintf.h> 31 #include <android-base/strings.h> 32 #include <meminfo/procmeminfo.h> 33 34 using ::android::meminfo::Vma; 35 36 struct VmaInfo { 37 Vma vma; 38 bool is_bss; 39 uint32_t count; 40 41 VmaInfo() = default; 42 VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {} 43 VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {} 44 VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) { 45 vma.name = name; 46 } 47 }; 48 49 // Global options 50 static std::string g_filename = ""; 51 static bool g_merge_by_names = false; 52 static bool g_terse = false; 53 static bool g_verbose = false; 54 static bool g_show_addr = false; 55 static bool g_quiet = false; 56 static pid_t g_pid = -1; 57 58 static VmaInfo g_total; 59 static std::vector<VmaInfo> g_vmas; 60 61 [[noreturn]] static void usage(const char* progname, int exit_status) { 62 fprintf(stderr, 63 "%s [-aqtv] [-f FILE] PID\n" 64 "-a\taddresses (show virtual memory map)\n" 65 "-q\tquiet (don't show error if map could not be read)\n" 66 "-t\tterse (show only items with private pages)\n" 67 "-v\tverbose (don't coalesce maps with the same name)\n" 68 "-f\tFILE (read from input from FILE instead of PID)\n", 69 progname); 70 71 exit(exit_status); 72 } 73 74 static bool is_library(const std::string& name) { 75 return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so"); 76 } 77 78 static bool insert_before(const VmaInfo& a, const VmaInfo& b) { 79 if (g_show_addr) { 80 return (a.vma.start < b.vma.start || (a.vma.start == b.vma.start && a.vma.end < b.vma.end)); 81 } 82 83 return strcmp(a.vma.name.c_str(), b.vma.name.c_str()) < 0; 84 } 85 86 static void collect_vma(const Vma& vma) { 87 if (g_vmas.empty()) { 88 g_vmas.emplace_back(vma); 89 return; 90 } 91 92 VmaInfo current(vma); 93 VmaInfo& last = g_vmas.back(); 94 // determine if this is bss; 95 if (vma.name.empty()) { 96 if (last.vma.end == current.vma.start && is_library(last.vma.name)) { 97 current.vma.name = last.vma.name; 98 current.is_bss = true; 99 } else { 100 current.vma.name = "[anon]"; 101 } 102 } 103 104 std::vector<VmaInfo>::iterator it; 105 for (it = g_vmas.begin(); it != g_vmas.end(); it++) { 106 if (g_merge_by_names && (it->vma.name == current.vma.name)) { 107 it->vma.usage.vss += current.vma.usage.vss; 108 it->vma.usage.rss += current.vma.usage.rss; 109 it->vma.usage.pss += current.vma.usage.pss; 110 111 it->vma.usage.shared_clean += current.vma.usage.shared_clean; 112 it->vma.usage.shared_dirty += current.vma.usage.shared_dirty; 113 it->vma.usage.private_clean += current.vma.usage.private_clean; 114 it->vma.usage.private_dirty += current.vma.usage.private_dirty; 115 it->vma.usage.swap += current.vma.usage.swap; 116 it->vma.usage.swap_pss += current.vma.usage.swap_pss; 117 it->is_bss &= current.is_bss; 118 it->count++; 119 break; 120 } 121 122 if (insert_before(current, *it)) { 123 g_vmas.insert(it, current); 124 break; 125 } 126 } 127 128 if (it == g_vmas.end()) { 129 g_vmas.emplace_back(current); 130 } 131 } 132 133 static void print_header() { 134 const char* addr1 = g_show_addr ? " start end " : ""; 135 const char* addr2 = g_show_addr ? " addr addr " : ""; 136 137 printf("%s virtual shared shared private private\n", addr1); 138 printf("%s size RSS PSS clean dirty clean dirty swap swapPSS", 139 addr2); 140 if (!g_verbose && !g_show_addr) { 141 printf(" # "); 142 } 143 if (g_verbose) { 144 printf(" flags "); 145 } 146 printf(" object\n"); 147 } 148 149 static void print_divider() { 150 if (g_show_addr) { 151 printf("-------- -------- "); 152 } 153 printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- "); 154 if (!g_verbose && !g_show_addr) { 155 printf("---- "); 156 } 157 if (g_verbose) { 158 printf("------ "); 159 } 160 printf("------------------------------\n"); 161 } 162 163 static void print_vmainfo(const VmaInfo& v, bool total) { 164 if (g_show_addr) { 165 if (total) { 166 printf(" "); 167 } else { 168 printf("%16" PRIx64 " %16" PRIx64 " ", v.vma.start, v.vma.end); 169 } 170 } 171 printf("%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 172 " %8" PRIu64 " %8" PRIu64 " ", 173 v.vma.usage.vss, v.vma.usage.rss, v.vma.usage.pss, v.vma.usage.shared_clean, 174 v.vma.usage.shared_dirty, v.vma.usage.private_clean, v.vma.usage.private_dirty, 175 v.vma.usage.swap, v.vma.usage.swap_pss); 176 if (!g_verbose && !g_show_addr) { 177 printf("%4" PRIu32 " ", v.count); 178 } 179 if (g_verbose) { 180 if (total) { 181 printf(" "); 182 } else { 183 std::string flags_str("---"); 184 if (v.vma.flags & PROT_READ) flags_str[0] = 'r'; 185 if (v.vma.flags & PROT_WRITE) flags_str[1] = 'w'; 186 if (v.vma.flags & PROT_EXEC) flags_str[2] = 'x'; 187 188 printf("%6s ", flags_str.c_str()); 189 } 190 } 191 } 192 193 static int showmap(void) { 194 if (!::android::meminfo::ForEachVmaFromFile(g_filename, collect_vma)) { 195 if (!g_quiet) { 196 fprintf(stderr, "Failed to parse file %s\n", g_filename.c_str()); 197 } 198 return 1; 199 } 200 201 print_header(); 202 print_divider(); 203 204 for (const auto& v : g_vmas) { 205 g_total.vma.usage.vss += v.vma.usage.vss; 206 g_total.vma.usage.rss += v.vma.usage.rss; 207 g_total.vma.usage.pss += v.vma.usage.pss; 208 209 g_total.vma.usage.private_clean += v.vma.usage.private_clean; 210 g_total.vma.usage.private_dirty += v.vma.usage.private_dirty; 211 g_total.vma.usage.shared_clean += v.vma.usage.shared_clean; 212 g_total.vma.usage.shared_dirty += v.vma.usage.shared_dirty; 213 214 g_total.vma.usage.swap += v.vma.usage.swap; 215 g_total.vma.usage.swap_pss += v.vma.usage.swap_pss; 216 g_total.count += v.count; 217 218 if (g_terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) { 219 continue; 220 } 221 222 print_vmainfo(v, false); 223 printf("%s%s\n", v.vma.name.c_str(), v.is_bss ? " [bss]" : ""); 224 } 225 226 print_divider(); 227 print_header(); 228 print_divider(); 229 230 print_vmainfo(g_total, true); 231 printf("TOTAL\n"); 232 233 return 0; 234 } 235 236 int main(int argc, char* argv[]) { 237 signal(SIGPIPE, SIG_IGN); 238 struct option longopts[] = { 239 {"help", no_argument, nullptr, 'h'}, 240 {0, 0, nullptr, 0}, 241 }; 242 243 int opt; 244 while ((opt = getopt_long(argc, argv, "tvaqf:h", longopts, nullptr)) != -1) { 245 switch (opt) { 246 case 't': 247 g_terse = true; 248 break; 249 case 'a': 250 g_show_addr = true; 251 break; 252 case 'v': 253 g_verbose = true; 254 break; 255 case 'q': 256 g_quiet = true; 257 break; 258 case 'f': 259 g_filename = optarg; 260 break; 261 case 'h': 262 usage(argv[0], EXIT_SUCCESS); 263 default: 264 usage(argv[0], EXIT_FAILURE); 265 } 266 } 267 268 if (g_filename.empty()) { 269 if ((argc - 1) < optind) { 270 fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n"); 271 usage(argv[0], EXIT_FAILURE); 272 } 273 274 g_pid = atoi(argv[optind]); 275 if (g_pid <= 0) { 276 fprintf(stderr, "Invalid process id %s\n", argv[optind]); 277 usage(argv[0], EXIT_FAILURE); 278 } 279 280 g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid); 281 } 282 283 g_merge_by_names = !g_verbose && !g_show_addr; 284 return showmap(); 285 } 286