1 #include <ctype.h> 2 #include <errno.h> 3 #include <fcntl.h> 4 #include <math.h> 5 #include <stddef.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h> 10 11 struct mapinfo { 12 mapinfo *next; 13 unsigned start; 14 unsigned end; 15 unsigned size; 16 unsigned rss; 17 unsigned pss; 18 unsigned shared_clean; 19 unsigned shared_dirty; 20 unsigned private_clean; 21 unsigned private_dirty; 22 unsigned swap; 23 unsigned swap_pss; 24 int is_bss; 25 int count; 26 char name[1]; 27 }; 28 29 static bool verbose = false; 30 static bool terse = false; 31 static bool addresses = false; 32 static bool quiet = false; 33 34 static int is_library(const char *name) { 35 int len = strlen(name); 36 return len >= 4 && name[0] == '/' 37 && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o'; 38 } 39 40 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so 41 // 012345678901234567890123456789012345678901234567890123456789 42 // 0 1 2 3 4 5 43 44 static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) { 45 unsigned long start; 46 unsigned long end; 47 char name[128]; 48 int name_pos; 49 int is_bss = 0; 50 51 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { 52 *mi = NULL; 53 return -1; 54 } 55 56 while (isspace(line[name_pos])) { 57 name_pos += 1; 58 } 59 60 if (line[name_pos]) { 61 strlcpy(name, line + name_pos, sizeof(name)); 62 } else { 63 if (prev && start == prev->end && is_library(prev->name)) { 64 // anonymous mappings immediately adjacent to shared libraries 65 // usually correspond to the library BSS segment, so we use the 66 // library's own name 67 strlcpy(name, prev->name, sizeof(name)); 68 is_bss = 1; 69 } else { 70 strlcpy(name, "[anon]", sizeof(name)); 71 } 72 } 73 74 const int name_size = strlen(name) + 1; 75 struct mapinfo* info = reinterpret_cast<mapinfo*>(calloc(1, sizeof(mapinfo) + name_size)); 76 if (info == NULL) { 77 fprintf(stderr, "out of memory\n"); 78 exit(1); 79 } 80 81 info->start = start; 82 info->end = end; 83 info->is_bss = is_bss; 84 info->count = 1; 85 strlcpy(info->name, name, name_size); 86 87 *mi = info; 88 return 0; 89 } 90 91 static int parse_field(mapinfo* mi, const char* line) { 92 char field[64]; 93 int len; 94 95 if (sscanf(line, "%63s %n", field, &len) == 1 96 && *field && field[strlen(field) - 1] == ':') { 97 int size; 98 if (sscanf(line + len, "%d kB", &size) == 1) { 99 if (!strcmp(field, "Size:")) { 100 mi->size = size; 101 } else if (!strcmp(field, "Rss:")) { 102 mi->rss = size; 103 } else if (!strcmp(field, "Pss:")) { 104 mi->pss = size; 105 } else if (!strcmp(field, "Shared_Clean:")) { 106 mi->shared_clean = size; 107 } else if (!strcmp(field, "Shared_Dirty:")) { 108 mi->shared_dirty = size; 109 } else if (!strcmp(field, "Private_Clean:")) { 110 mi->private_clean = size; 111 } else if (!strcmp(field, "Private_Dirty:")) { 112 mi->private_dirty = size; 113 } else if (!strcmp(field, "Swap:")) { 114 mi->swap = size; 115 } else if (!strcmp(field, "SwapPss:")) { 116 mi->swap_pss = size; 117 } 118 } 119 return 0; 120 } 121 return -1; 122 } 123 124 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) { 125 if (sort_by_address) { 126 return a->start < b->start 127 || (a->start == b->start && a->end < b->end); 128 } else { 129 return strcmp(a->name, b->name) < 0; 130 } 131 } 132 133 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) { 134 mapinfo *prev = NULL; 135 mapinfo *current = *head; 136 137 if (!map) { 138 return; 139 } 140 141 for (;;) { 142 if (current && coalesce_by_name && !strcmp(map->name, current->name)) { 143 current->size += map->size; 144 current->rss += map->rss; 145 current->pss += map->pss; 146 current->shared_clean += map->shared_clean; 147 current->shared_dirty += map->shared_dirty; 148 current->private_clean += map->private_clean; 149 current->private_dirty += map->private_dirty; 150 current->swap += map->swap; 151 current->swap_pss += map->swap_pss; 152 current->is_bss &= map->is_bss; 153 current->count++; 154 free(map); 155 break; 156 } 157 158 if (!current || order_before(map, current, sort_by_address)) { 159 if (prev) { 160 prev->next = map; 161 } else { 162 *head = map; 163 } 164 map->next = current; 165 break; 166 } 167 168 prev = current; 169 current = current->next; 170 } 171 } 172 173 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name) 174 { 175 char fn[128]; 176 FILE *fp; 177 char line[1024]; 178 mapinfo *head = NULL; 179 mapinfo *current = NULL; 180 int len; 181 182 snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid); 183 fp = fopen(fn, "r"); 184 if (fp == 0) { 185 if (!quiet) fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno)); 186 return NULL; 187 } 188 189 while (fgets(line, sizeof(line), fp) != 0) { 190 len = strlen(line); 191 if (line[len - 1] == '\n') { 192 line[--len] = 0; 193 } 194 195 if (current != NULL && !parse_field(current, line)) { 196 continue; 197 } 198 199 mapinfo *next; 200 if (!parse_header(line, current, &next)) { 201 enqueue_map(&head, current, sort_by_address, coalesce_by_name); 202 current = next; 203 continue; 204 } 205 206 fprintf(stderr, "warning: could not parse map info line: %s\n", line); 207 } 208 209 enqueue_map(&head, current, sort_by_address, coalesce_by_name); 210 211 fclose(fp); 212 213 if (!head) { 214 if (!quiet) fprintf(stderr, "could not read /proc/%d/smaps\n", pid); 215 return NULL; 216 } 217 218 return head; 219 } 220 221 static void print_header() 222 { 223 const char *addr1 = addresses ? " start end " : ""; 224 const char *addr2 = addresses ? " addr addr " : ""; 225 226 printf("%s virtual shared shared private private\n", addr1); 227 printf("%s size RSS PSS clean dirty clean dirty swap swapPSS", addr2); 228 if (!verbose && !addresses) { 229 printf(" # "); 230 } 231 printf("object\n"); 232 } 233 234 static void print_divider() 235 { 236 if (addresses) { 237 printf("-------- -------- "); 238 } 239 printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- "); 240 if (!verbose && !addresses) { 241 printf("---- "); 242 } 243 printf("------------------------------\n"); 244 } 245 246 static void print_mi(mapinfo *mi, bool total) 247 { 248 if (addresses) { 249 if (total) { 250 printf(" "); 251 } else { 252 printf("%08x %08x ", mi->start, mi->end); 253 } 254 } 255 printf("%8d %8d %8d %8d %8d %8d %8d %8d %8d ", mi->size, 256 mi->rss, 257 mi->pss, 258 mi->shared_clean, mi->shared_dirty, 259 mi->private_clean, mi->private_dirty, mi->swap, mi->swap_pss); 260 if (!verbose && !addresses) { 261 printf("%4d ", mi->count); 262 } 263 } 264 265 static int show_map(int pid) 266 { 267 mapinfo total; 268 memset(&total, 0, sizeof(total)); 269 270 mapinfo *milist = load_maps(pid, addresses, !verbose && !addresses); 271 if (milist == NULL) { 272 return quiet ? 0 : 1; 273 } 274 275 print_header(); 276 print_divider(); 277 278 for (mapinfo *mi = milist; mi;) { 279 mapinfo* last = mi; 280 281 total.shared_clean += mi->shared_clean; 282 total.shared_dirty += mi->shared_dirty; 283 total.private_clean += mi->private_clean; 284 total.private_dirty += mi->private_dirty; 285 total.swap += mi->swap; 286 total.swap_pss += mi->swap_pss; 287 total.rss += mi->rss; 288 total.pss += mi->pss; 289 total.size += mi->size; 290 total.count += mi->count; 291 292 if (terse && !mi->private_dirty) { 293 goto out; 294 } 295 296 print_mi(mi, false); 297 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : ""); 298 299 out: 300 mi = mi->next; 301 free(last); 302 } 303 304 print_divider(); 305 print_header(); 306 print_divider(); 307 308 print_mi(&total, true); 309 printf("TOTAL\n"); 310 311 return 0; 312 } 313 314 int main(int argc, char *argv[]) 315 { 316 int usage = 1; 317 int result = 0; 318 int pid; 319 char *arg; 320 char *argend; 321 322 signal(SIGPIPE, SIG_IGN); 323 for (argc--, argv++; argc > 0; argc--, argv++) { 324 arg = argv[0]; 325 if (!strcmp(arg,"-v")) { 326 verbose = true; 327 continue; 328 } 329 if (!strcmp(arg,"-t")) { 330 terse = true; 331 continue; 332 } 333 if (!strcmp(arg,"-a")) { 334 addresses = true; 335 continue; 336 } 337 if (!strcmp(arg,"-q")) { 338 quiet = true; 339 continue; 340 } 341 if (argc != 1) { 342 fprintf(stderr, "too many arguments\n"); 343 break; 344 } 345 pid = strtol(arg, &argend, 10); 346 if (*arg && !*argend) { 347 usage = 0; 348 if (show_map(pid)) { 349 result = 1; 350 } 351 break; 352 } 353 fprintf(stderr, "unrecognized argument: %s\n", arg); 354 break; 355 } 356 357 if (usage) { 358 fprintf(stderr, 359 "showmap [-t] [-v] [-c] [-q] <pid>\n" 360 " -t = terse (show only items with private pages)\n" 361 " -v = verbose (don't coalesce maps with the same name)\n" 362 " -a = addresses (show virtual memory map)\n" 363 " -q = quiet (don't show error if map could not be read)\n" 364 ); 365 result = 1; 366 } 367 368 return result; 369 } 370