12 typedef struct mapinfo mapinfo;
21 unsigned shared_clean;
22 unsigned shared_dirty;
23 unsigned private_clean;
24 unsigned private_dirty;
31 static int is_library(const char *name) {
32 int len = strlen(name);
33 return len >= 4 && name[0] == '/'
34 && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
37 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
38 // 012345678901234567890123456789012345678901234567890123456789
41 static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
48 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
53 while (isspace(line[name_pos])) {
58 strlcpy(name, line + name_pos, sizeof(name));
60 if (prev && start == prev->end && is_library(prev->name)) {
61 // anonymous mappings immediately adjacent to shared libraries
62 // usually correspond to the library BSS segment, so we use the
64 strlcpy(name, prev->name, sizeof(name));
67 strlcpy(name, "[anon]", sizeof(name));
71 const int name_size = strlen(name) + 1;
72 struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
74 fprintf(stderr, "out of memory\n");
80 info->is_bss = is_bss;
82 strlcpy(info->name, name, name_size);
88 static int parse_field(mapinfo* mi, const char* line) {
92 if (sscanf(line, "%63s %n", field, &len) == 1
93 && *field && field[strlen(field) - 1] == ':') {
95 if (sscanf(line + len, "%d kB", &size) == 1) {
96 if (!strcmp(field, "Size:")) {
98 } else if (!strcmp(field, "Rss:")) {
100 } else if (!strcmp(field, "Pss:")) {
102 } else if (!strcmp(field, "Shared_Clean:")) {
103 mi->shared_clean = size;
104 } else if (!strcmp(field, "Shared_Dirty:")) {
105 mi->shared_dirty = size;
106 } else if (!strcmp(field, "Private_Clean:")) {
107 mi->private_clean = size;
108 } else if (!strcmp(field, "Private_Dirty:")) {
109 mi->private_dirty = size;
110 } else if (!strcmp(field, "Swap:")) {
119 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
120 if (sort_by_address) {
121 return a->start < b->start
122 || (a->start == b->start && a->end < b->end);
124 return strcmp(a->name, b->name) < 0;
128 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
129 mapinfo *prev = NULL;
130 mapinfo *current = *head;
137 if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
138 current->size += map->size;
139 current->rss += map->rss;
140 current->pss += map->pss;
141 current->shared_clean += map->shared_clean;
142 current->shared_dirty += map->shared_dirty;
143 current->private_clean += map->private_clean;
144 current->private_dirty += map->private_dirty;
145 current->swap += map->swap;
146 current->is_bss &= map->is_bss;
152 if (!current || order_before(map, current, sort_by_address)) {
163 current = current->next;
167 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
172 mapinfo *head = NULL;
173 mapinfo *current = NULL;
176 snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
179 fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
183 while (fgets(line, sizeof(line), fp) != 0) {
185 if (line[len - 1] == '\n') {
189 if (current != NULL && !parse_field(current, line)) {
194 if (!parse_header(line, current, &next)) {
195 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
200 fprintf(stderr, "warning: could not parse map info line: %s\n", line);
203 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
208 fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
215 static int verbose = 0;
216 static int terse = 0;
217 static int addresses = 0;
219 static void print_header()
222 printf(" start end ");
224 printf(" virtual shared shared private private\n");
227 printf(" addr addr ");
229 printf(" size RSS PSS clean dirty clean dirty swap ");
230 if (!verbose && !addresses) {
236 static void print_divider()
239 printf("-------- -------- ");
241 printf("-------- -------- -------- -------- -------- -------- -------- -------- ");
242 if (!verbose && !addresses) {
245 printf("------------------------------\n");
248 static int show_map(int pid)
252 unsigned shared_dirty = 0;
253 unsigned shared_clean = 0;
254 unsigned private_dirty = 0;
255 unsigned private_clean = 0;
262 milist = load_maps(pid, addresses, !verbose && !addresses);
263 if (milist == NULL) {
270 for (mi = milist; mi;) {
273 shared_clean += mi->shared_clean;
274 shared_dirty += mi->shared_dirty;
275 private_clean += mi->private_clean;
276 private_dirty += mi->private_dirty;
283 if (terse && !mi->private_dirty) {
288 printf("%08x %08x ", mi->start, mi->end);
290 printf("%8d %8d %8d %8d %8d %8d %8d %8d", mi->size,
293 mi->shared_clean, mi->shared_dirty,
294 mi->private_clean, mi->private_dirty, mi->swap);
295 if (!verbose && !addresses) {
296 printf("%4d ", mi->count);
298 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
312 printf("%8d %8d %8d %8d %8d %8d %8d %8d", size,
314 shared_clean, shared_dirty,
315 private_clean, private_dirty, swap);
316 if (!verbose && !addresses) {
317 printf("%4d ", count);
324 int main(int argc, char *argv[])
332 signal(SIGPIPE, SIG_IGN);
333 for (argc--, argv++; argc > 0; argc--, argv++) {
335 if (!strcmp(arg,"-v")) {
339 if (!strcmp(arg,"-t")) {
343 if (!strcmp(arg,"-a")) {
348 fprintf(stderr, "too many arguments\n");
351 pid = strtol(arg, &argend, 10);
352 if (*arg && !*argend) {
359 fprintf(stderr, "unrecognized argument: %s\n", arg);
365 "showmap [-t] [-v] [-c] <pid>\n"
366 " -t = terse (show only items with private pages)\n"
367 " -v = verbose (don't coalesce maps with the same name)\n"
368 " -a = addresses (show virtual memory map)\n"