#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <stddef.h>
typedef struct mapinfo mapinfo;
struct mapinfo {
mapinfo *next;
unsigned start;
unsigned end;
unsigned size;
unsigned rss;
unsigned pss;
unsigned shared_clean;
unsigned shared_dirty;
unsigned private_clean;
unsigned private_dirty;
char name[1];
};
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
// 012345678901234567890123456789012345678901234567890123456789
// 0 1 2 3 4 5
mapinfo *read_mapinfo(FILE *fp)
{
char line[1024];
mapinfo *mi;
int len;
int skip;
again:
skip = 0;
if(fgets(line, 1024, fp) == 0) return 0;
len = strlen(line);
if(len < 1) return 0;
line[--len] = 0;
mi = calloc(1, sizeof(mapinfo) + len + 16);
if(mi == 0) return 0;
mi->start = strtoul(line, 0, 16);
mi->end = strtoul(line + 9, 0, 16);
if(len < 50) {
if((mi->start >= 0x10000000) && (mi->start < 0x40000000)) {
strcpy(mi->name, "[stack]");
} else if(mi->start > 0x50000000) {
strcpy(mi->name, "[lib_bss]");
} else {
strcpy(mi->name, "[anon]");
}
} else {
strcpy(mi->name, line + 49);
}
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Size: %d kB", &mi->size) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Rss: %d kB", &mi->rss) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Pss: %d kB", &mi->pss) == 1)
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops;
if(sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) goto oops;
if(fgets(line, 1024, fp) == 0) goto oops; // Referenced
if(fgets(line, 1024, fp) == 0) goto oops; // Swap
if(fgets(line, 1024, fp) == 0) goto oops; // KernelPageSize
if(fgets(line, 1024, fp) == 0) goto oops; // MMUPageSize
if(skip) {
free(mi);
goto again;
}
return mi;
oops:
fprintf(stderr, "WARNING: Format of /proc/<pid>/smaps has changed!\n");
free(mi);
return 0;
}
mapinfo *load_maps(int pid, int verbose)
{
char tmp[128];
FILE *fp;
mapinfo *milist = 0;
mapinfo *mi;
sprintf(tmp, "/proc/%d/smaps", pid);
fp = fopen(tmp, "r");
if(fp == 0) return 0;
while((mi = read_mapinfo(fp)) != 0) {
/* if not verbose, coalesce mappings from the same entity */
if(!verbose && milist) {
if((!strcmp(mi->name, milist->name) && (mi->name[0] != '['))
|| !strcmp(mi->name,"[lib_bss]")) {
milist->size += mi->size;
milist->rss += mi->rss;
milist->pss += mi->pss;
milist->shared_clean += mi->shared_clean;
milist->shared_dirty += mi->shared_dirty;
milist->private_clean += mi->private_clean;
milist->private_dirty += mi->private_dirty;
milist->end = mi->end;
free(mi);
continue;
}
}
mi->next = milist;
milist = mi;
}
fclose(fp);
return milist;
}
static int verbose = 0;
static int terse = 0;
static int addresses = 0;
int show_map(int pid)
{
mapinfo *milist;
mapinfo *mi;
unsigned shared_dirty = 0;
unsigned shared_clean = 0;
unsigned private_dirty = 0;
unsigned private_clean = 0;
unsigned rss = 0;
unsigned pss = 0;
unsigned size = 0;
milist = load_maps(pid, verbose);
if(milist == 0) {
fprintf(stderr,"cannot get /proc/smaps for pid %d\n", pid);
return 1;
}
if(addresses) {
printf("start end shared private object\n");
printf("-------- -------- -------- -------- ------------------------------\n");
} else {
printf("virtual shared shared private private\n");
printf("size RSS PSS clean dirty clean dirty object\n");
printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
}
for(mi = milist; mi; mi = mi->next){
shared_clean += mi->shared_clean;
shared_dirty += mi->shared_dirty;
private_clean += mi->private_clean;
private_dirty += mi->private_dirty;
rss += mi->rss;
pss += mi->pss;
size += mi->size;
if(terse && !mi->private_dirty) continue;
if(addresses) {
printf("%08x %08x %8d %8d %s\n", mi->start, mi->end,
mi->shared_clean + mi->shared_dirty,
mi->private_clean + mi->private_dirty,
mi->name);
} else {
printf("%8d %8d %8d %8d %8d %8d %8d %s\n", mi->size,
mi->rss,
mi->pss,
mi->shared_clean, mi->shared_dirty,
mi->private_clean, mi->private_dirty,
mi->name);
}
}
if(addresses) {
printf("-------- -------- -------- -------- ------------------------------\n");
printf(" %8d %8d TOTAL\n",
shared_dirty + shared_clean,
private_dirty + private_clean);
} else {
printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size,
rss, pss,
shared_clean, shared_dirty,
private_clean, private_dirty);
}
return 0;
}
int main(int argc, char *argv[])
{
int usage = 1;
for(argc--, argv++; argc > 0; argc--, argv++) {
if(!strcmp(argv[0],"-v")) {
verbose = 1;
continue;
}
if(!strcmp(argv[0],"-t")) {
terse = 1;
continue;
}
if(!strcmp(argv[0],"-a")) {
addresses = 1;
continue;
}
show_map(atoi(argv[0]));
usage = 0;
}
if(usage) {
fprintf(stderr,
"showmap [-t] [-v] [-c] <pid>\n"
" -t = terse (show only items with private pages)\n"
" -v = verbose (don't coalesce adjacant maps)\n"
" -a = addresses (show virtual memory map)\n"
);
}
return 0;
}