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