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 size; 90 91 if (sscanf(line, "%63s %d kB", field, &size) != 2) { 92 return -1; 93 } 94 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 114 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) { 115 if (sort_by_address) { 116 return a->start < b->start 117 || (a->start == b->start && a->end < b->end); 118 } else { 119 return strcmp(a->name, b->name) < 0; 120 } 121 } 122 123 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) { 124 mapinfo *prev = NULL; 125 mapinfo *current = *head; 126 127 if (!map) { 128 return; 129 } 130 131 for (;;) { 132 if (current && coalesce_by_name && !strcmp(map->name, current->name)) { 133 current->size += map->size; 134 current->rss += map->rss; 135 current->pss += map->pss; 136 current->shared_clean += map->shared_clean; 137 current->shared_dirty += map->shared_dirty; 138 current->private_clean += map->private_clean; 139 current->private_dirty += map->private_dirty; 140 current->is_bss &= map->is_bss; 141 current->count++; 142 free(map); 143 break; 144 } 145 146 if (!current || order_before(map, current, sort_by_address)) { 147 if (prev) { 148 prev->next = map; 149 } else { 150 *head = map; 151 } 152 map->next = current; 153 break; 154 } 155 156 prev = current; 157 current = current->next; 158 } 159 } 160 161 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name) 162 { 163 char fn[128]; 164 FILE *fp; 165 char line[1024]; 166 mapinfo *head = NULL; 167 mapinfo *current = NULL; 168 int len; 169 170 snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid); 171 fp = fopen(fn, "r"); 172 if (fp == 0) { 173 fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno)); 174 return NULL; 175 } 176 177 while (fgets(line, sizeof(line), fp) != 0) { 178 len = strlen(line); 179 if (line[len - 1] == '\n') { 180 line[--len] = 0; 181 } 182 183 if (current != NULL && !parse_field(current, line)) { 184 continue; 185 } 186 187 mapinfo *next; 188 if (!parse_header(line, current, &next)) { 189 enqueue_map(&head, current, sort_by_address, coalesce_by_name); 190 current = next; 191 continue; 192 } 193 194 fprintf(stderr, "warning: could not parse map info line: %s\n", line); 195 } 196 197 enqueue_map(&head, current, sort_by_address, coalesce_by_name); 198 199 fclose(fp); 200 201 if (!head) { 202 fprintf(stderr, "could not read /proc/%d/smaps\n", pid); 203 return NULL; 204 } 205 206 return head; 207 } 208 209 static int verbose = 0; 210 static int terse = 0; 211 static int addresses = 0; 212 213 static void print_header() 214 { 215 if (addresses) { 216 printf(" start end "); 217 } 218 printf(" virtual shared shared private private\n"); 219 220 if (addresses) { 221 printf(" addr addr "); 222 } 223 printf(" size RSS PSS clean dirty clean dirty "); 224 if (!verbose && !addresses) { 225 printf(" # "); 226 } 227 printf("object\n"); 228 } 229 230 static void print_divider() 231 { 232 if (addresses) { 233 printf("-------- -------- "); 234 } 235 printf("-------- -------- -------- -------- -------- -------- -------- "); 236 if (!verbose && !addresses) { 237 printf("---- "); 238 } 239 printf("------------------------------\n"); 240 } 241 242 static int show_map(int pid) 243 { 244 mapinfo *milist; 245 mapinfo *mi; 246 unsigned shared_dirty = 0; 247 unsigned shared_clean = 0; 248 unsigned private_dirty = 0; 249 unsigned private_clean = 0; 250 unsigned rss = 0; 251 unsigned pss = 0; 252 unsigned size = 0; 253 unsigned count = 0; 254 255 milist = load_maps(pid, addresses, !verbose && !addresses); 256 if (milist == NULL) { 257 return 1; 258 } 259 260 print_header(); 261 print_divider(); 262 263 for (mi = milist; mi;) { 264 mapinfo* last = mi; 265 266 shared_clean += mi->shared_clean; 267 shared_dirty += mi->shared_dirty; 268 private_clean += mi->private_clean; 269 private_dirty += mi->private_dirty; 270 rss += mi->rss; 271 pss += mi->pss; 272 size += mi->size; 273 count += mi->count; 274 275 if (terse && !mi->private_dirty) { 276 goto out; 277 } 278 279 if (addresses) { 280 printf("%08x %08x ", mi->start, mi->end); 281 } 282 printf("%8d %8d %8d %8d %8d %8d %8d ", mi->size, 283 mi->rss, 284 mi->pss, 285 mi->shared_clean, mi->shared_dirty, 286 mi->private_clean, mi->private_dirty); 287 if (!verbose && !addresses) { 288 printf("%4d ", mi->count); 289 } 290 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : ""); 291 292 out: 293 mi = mi->next; 294 free(last); 295 } 296 297 print_divider(); 298 print_header(); 299 print_divider(); 300 301 if (addresses) { 302 printf(" "); 303 } 304 printf("%8d %8d %8d %8d %8d %8d %8d ", size, 305 rss, pss, 306 shared_clean, shared_dirty, 307 private_clean, private_dirty); 308 if (!verbose && !addresses) { 309 printf("%4d ", count); 310 } 311 printf("TOTAL\n"); 312 313 return 0; 314 } 315 316 int main(int argc, char *argv[]) 317 { 318 int usage = 1; 319 int result = 0; 320 int pid; 321 char *arg; 322 char *argend; 323 324 signal(SIGPIPE, SIG_IGN); 325 for (argc--, argv++; argc > 0; argc--, argv++) { 326 arg = argv[0]; 327 if (!strcmp(arg,"-v")) { 328 verbose = 1; 329 continue; 330 } 331 if (!strcmp(arg,"-t")) { 332 terse = 1; 333 continue; 334 } 335 if (!strcmp(arg,"-a")) { 336 addresses = 1; 337 continue; 338 } 339 if (argc != 1) { 340 fprintf(stderr, "too many arguments\n"); 341 break; 342 } 343 pid = strtol(arg, &argend, 10); 344 if (*arg && !*argend) { 345 usage = 0; 346 if (show_map(pid)) { 347 result = 1; 348 } 349 break; 350 } 351 fprintf(stderr, "unrecognized argument: %s\n", arg); 352 break; 353 } 354 355 if (usage) { 356 fprintf(stderr, 357 "showmap [-t] [-v] [-c] <pid>\n" 358 " -t = terse (show only items with private pages)\n" 359 " -v = verbose (don't coalesce maps with the same name)\n" 360 " -a = addresses (show virtual memory map)\n" 361 ); 362 result = 1; 363 } 364 365 return result; 366 } 367