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