diff --git a/pmap.c b/pmap.c index c92737d8..a61dd2e1 100644 --- a/pmap.c +++ b/pmap.c @@ -9,415 +9,480 @@ * GNU Library General Public License for more details. */ +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include #include -#include - #include #include +#include +#include +#include +#include "proc/escape.h" #include "proc/readproc.h" #include "proc/version.h" -#include "proc/escape.h" -static void usage(void) NORETURN; -static void usage(void){ - fprintf(stderr, - "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n" - "-x show details\n" - "-d show offset and device number\n" - "-q quiet; less header/footer info\n" - "-V show the version number\n" - "-A limit results to the given range\n" - ); - exit(1); +static void __attribute__ ((__noreturn__)) + usage(FILE * out) +{ + fprintf(out, + "\nUsage: %s [options] pid [pid ...]\n" + "\nOptions:\n", program_invocation_short_name); + fprintf(out, + " -x, --extended show details\n" + " -d, --device show the device format\n" + " -q, --quiet do not display header and footer\n" + " -A, --range=[,] limit results to the given range\n" + " -h, --help display this help text\n" + " -V, --version display version information and exit\n"); + fprintf(out, "\nFor more information see pmap(1).\n"); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } - static unsigned KLONG range_low; static unsigned KLONG range_high = ~0ull; -static int V_option; -static int r_option; // ignored -- for SunOS compatibility -static int x_option; static int d_option; static int q_option; +static int x_option; static unsigned shm_minor = ~0u; -static void discover_shm_minor(void){ - void *addr; - int shmid; - char mapbuf[256]; +static void discover_shm_minor(void) +{ + void *addr; + int shmid; + char mapbuf[256]; - if(!freopen("/proc/self/maps", "r", stdin)) return; + if (!freopen("/proc/self/maps", "r", stdin)) + return; - // create - shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); - if(shmid==-1) return; // failed; oh well - // attach - addr = shmat(shmid, NULL, SHM_RDONLY); - if(addr==(void*)-1) goto out_destroy; + /* create */ + shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); + if (shmid == -1) + /* failed; oh well */ + return; + /* attach */ + addr = shmat(shmid, NULL, SHM_RDONLY); + if (addr == (void *)-1) + goto out_destroy; - while(fgets(mapbuf, sizeof mapbuf, stdin)){ - char flags[32]; - char *tmp; // to clean up unprintables - unsigned KLONG start, end; - unsigned long long file_offset, inode; - unsigned dev_major, dev_minor; - sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); - tmp = strchr(mapbuf,'\n'); - if(tmp) *tmp='\0'; - tmp = mapbuf; - while(*tmp){ - if(!isprint(*tmp)) *tmp='?'; - tmp++; - } - if(start > (unsigned long)addr) continue; - if(dev_major) continue; - if(flags[3] != 's') continue; - if(strstr(mapbuf,"/SYSV")){ - shm_minor = dev_minor; - break; - } - } + while (fgets(mapbuf, sizeof mapbuf, stdin)) { + char flags[32]; + /* to clean up unprintables */ + char *tmp; + unsigned KLONG start, end; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %Lx %x:%x %Lu", &start, + &end, flags, &file_offset, &dev_major, &dev_minor, + &inode); + tmp = strchr(mapbuf, '\n'); + if (tmp) + *tmp = '\0'; + tmp = mapbuf; + while (*tmp) { + if (!isprint(*tmp)) + *tmp = '?'; + tmp++; + } + if (start > (unsigned long)addr) + continue; + if (dev_major) + continue; + if (flags[3] != 's') + continue; + if (strstr(mapbuf, "/SYSV")) { + shm_minor = dev_minor; + break; + } + } - if(shmdt(addr)) perror("shmdt"); + if (shmdt(addr)) + perror("shmdt"); -out_destroy: - if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID"); + out_destroy: + if (shmctl(shmid, IPC_RMID, NULL)) + perror("IPC_RMID"); - return; + return; } +static const char *mapping_name(proc_t * p, unsigned KLONG addr, + unsigned KLONG len, const char *mapbuf, + unsigned showpath, unsigned dev_major, + unsigned dev_minor, unsigned long long inode) +{ + const char *cp; -static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode){ - const char *cp; + if (!dev_major && dev_minor == shm_minor && strstr(mapbuf, "/SYSV")) { + static char shmbuf[64]; + snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode); + return shmbuf; + } - if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){ - static char shmbuf[64]; - snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode); - return shmbuf; - } + cp = strrchr(mapbuf, '/'); + if (cp) { + if (showpath) + return strchr(mapbuf, '/'); + return cp[1] ? cp + 1 : cp; + } - cp = strrchr(mapbuf,'/'); - if(cp){ - if(showpath) return strchr(mapbuf,'/'); - return cp[1] ? cp+1 : cp; - } + cp = strchr(mapbuf, '/'); + if (cp) { + if (showpath) + return cp; + /* it WILL succeed */ + return strrchr(cp, '/') + 1; + } - cp = strchr(mapbuf,'/'); - if(cp){ - if(showpath) return cp; - return strrchr(cp,'/') + 1; // it WILL succeed - } - - cp = " [ anon ]"; - if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]"; - return cp; + cp = " [ anon ]"; + if ((p->start_stack >= addr) && (p->start_stack <= addr + len)) + cp = " [ stack ]"; + return cp; } -static int one_proc(proc_t *p){ - char buf[32]; - char mapbuf[9600]; - char cmdbuf[512]; - FILE *fp; - unsigned long total_shared = 0ul; - unsigned long total_private_readonly = 0ul; - unsigned long total_private_writeable = 0ul; +static int one_proc(proc_t * p) +{ + char buf[32]; + char mapbuf[9600]; + char cmdbuf[512]; + FILE *fp; + unsigned long total_shared = 0ul; + unsigned long total_private_readonly = 0ul; + unsigned long total_private_writeable = 0ul; - char *cp2=NULL; - unsigned long long rss = 0ull; - unsigned long long private_dirty = 0ull; - unsigned long long shared_dirty = 0ull; - unsigned long long total_rss = 0ull; - unsigned long long total_private_dirty = 0ull; - unsigned long long total_shared_dirty = 0ull; + char *cp2 = NULL; + unsigned long long rss = 0ull; + unsigned long long private_dirty = 0ull; + unsigned long long shared_dirty = 0ull; + unsigned long long total_rss = 0ull; + unsigned long long total_private_dirty = 0ull; + unsigned long long total_shared_dirty = 0ull; - // Overkill, but who knows what is proper? The "w" prog - // uses the tty width to determine this. - int maxcmd = 0xfffff; + /* Overkill, but who knows what is proper? The "w" prog uses + * the tty width to determine this. + */ + int maxcmd = 0xfffff; - sprintf(buf,"/proc/%u/maps",p->tgid); - if ( (fp = fopen(buf, "r")) == NULL) return 1; - if (x_option) { - sprintf(buf,"/proc/%u/smaps",p->tgid); - if ( (fp = freopen(buf, "r", fp)) == NULL) return 1; - } + sprintf(buf, "/proc/%u/maps", p->tgid); + if ((fp = fopen(buf, "r")) == NULL) + return 1; + if (x_option) { + sprintf(buf, "/proc/%u/smaps", p->tgid); + if ((fp = freopen(buf, "r", fp)) == NULL) + return 1; + } - escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS); - printf("%u: %s\n", p->tgid, cmdbuf); + escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, + ESC_ARGS | ESC_BRACKETS); + printf("%u: %s\n", p->tgid, cmdbuf); - if(!q_option && (x_option|d_option)){ - if(x_option){ - if(sizeof(KLONG)==4) printf("Address Kbytes RSS Dirty Mode Mapping\n"); - else printf("Address Kbytes RSS Dirty Mode Mapping\n"); - } - if(d_option){ - if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n"); - else printf("Address Kbytes Mode Offset Device Mapping\n"); - } - } + if (!q_option && (x_option | d_option)) { + if (x_option) { + if (sizeof(KLONG) == 4) + printf + ("Address Kbytes RSS Dirty Mode Mapping\n"); + else + printf + ("Address Kbytes RSS Dirty Mode Mapping\n"); + } + if (d_option) { + if (sizeof(KLONG) == 4) + printf + ("Address Kbytes Mode Offset Device Mapping\n"); + else + printf + ("Address Kbytes Mode Offset Device Mapping\n"); + } + } - while(fgets(mapbuf,sizeof mapbuf,fp)){ - char flags[32]; - char *tmp; // to clean up unprintables - unsigned KLONG start, end, diff=0; - unsigned long long file_offset, inode; - unsigned dev_major, dev_minor; - unsigned long long smap_value; - char smap_key[20]; + while (fgets(mapbuf, sizeof mapbuf, fp)) { + char flags[32]; + /* to clean up unprintables */ + char *tmp; + unsigned KLONG start, end, diff = 0; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + unsigned long long smap_value; + char smap_key[20]; - /* hex values are lower case or numeric, keys are upper */ - if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') { - /* Its a key */ - if (sscanf(mapbuf,"%20[^:]: %llu", smap_key, &smap_value) == 2) { - if (strncmp("Rss", smap_key, 3) == 0) { - rss = smap_value; - total_rss += smap_value; - continue; - } - if (strncmp("Shared_Dirty", smap_key, 12) == 0) { - shared_dirty = smap_value; - total_shared_dirty += smap_value; - continue; - } - if (strncmp("Private_Dirty", smap_key, 13) == 0) { - private_dirty = smap_value; - total_private_dirty += smap_value; - continue; - } - if (strncmp("Swap", smap_key, 4) == 0) { /*doesnt matter as long as last*/ - printf( - (sizeof(KLONG)==8) - ? "%016"KLF"x %7lu %7llu %7llu %s %s\n" - : "%08lx %7lu %7llu %7llu %s %s\n", - start, - (unsigned long)(diff>>10), - rss, - (private_dirty + shared_dirty), - flags, - cp2 - ); - /* reset some counters */ - rss = shared_dirty = private_dirty = 0ull; - continue; - } - /* Other keys */ - continue; - } - } - sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); + /* hex values are lower case or numeric, keys are upper */ + if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') { + /* Its a key */ + if (sscanf + (mapbuf, "%20[^:]: %llu", smap_key, + &smap_value) == 2) { + if (strncmp("Rss", smap_key, 3) == 0) { + rss = smap_value; + total_rss += smap_value; + continue; + } + if (strncmp("Shared_Dirty", smap_key, 12) == 0) { + shared_dirty = smap_value; + total_shared_dirty += smap_value; + continue; + } + if (strncmp("Private_Dirty", smap_key, 13) == 0) { + private_dirty = smap_value; + total_private_dirty += smap_value; + continue; + } + if (strncmp("Swap", smap_key, 4) == 0) { + /*doesnt matter as long as last */ + printf((sizeof(KLONG) == 8) + ? "%016" KLF + "x %7lu %7llu %7llu %s %s\n" : + "%08lx %7lu %7llu %7llu %s %s\n", + start, + (unsigned long)(diff >> 10), rss, + (private_dirty + shared_dirty), + flags, cp2); + /* reset some counters */ + rss = shared_dirty = private_dirty = + 0ull; + continue; + } + /* Other keys */ + continue; + } + } + sscanf(mapbuf, "%" KLF "x-%" KLF "x %31s %Lx %x:%x %Lu", &start, + &end, flags, &file_offset, &dev_major, &dev_minor, + &inode); - if(start > range_high) - break; - if(end < range_low) - continue; + if (start > range_high) + break; + if (end < range_low) + continue; - tmp = strchr(mapbuf,'\n'); - if(tmp) *tmp='\0'; - tmp = mapbuf; - while(*tmp){ - if(!isprint(*tmp)) *tmp='?'; - tmp++; - } - - diff = end-start; - if(flags[3]=='s') total_shared += diff; - if(flags[3]=='p'){ - flags[3] = '-'; - if(flags[1]=='w') total_private_writeable += diff; - else total_private_readonly += diff; - } + tmp = strchr(mapbuf, '\n'); + if (tmp) + *tmp = '\0'; + tmp = mapbuf; + while (*tmp) { + if (!isprint(*tmp)) + *tmp = '?'; + tmp++; + } - // format used by Solaris 9 and procps-3.2.0+ - // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.) - flags[4] = '-'; - flags[5] = '\0'; + diff = end - start; + if (flags[3] == 's') + total_shared += diff; + if (flags[3] == 'p') { + flags[3] = '-'; + if (flags[1] == 'w') + total_private_writeable += diff; + else + total_private_readonly += diff; + } + /* format used by Solaris 9 and procps-3.2.0+ an 'R' + * if swap not reserved (MAP_NORESERVE, SysV ISM + * shared mem, etc.) + */ + flags[4] = '-'; + flags[5] = '\0'; - if(x_option){ - cp2 = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); - /* printed with the keys */ - continue; - } - if(d_option){ - const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); - printf( - (sizeof(KLONG)==8) - ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n" - : "%08lx %7lu %s %016Lx %03x:%05x %s\n", - start, - (unsigned long)(diff>>10), - flags, - file_offset, - dev_major, dev_minor, - cp - ); - } - if(!x_option && !d_option){ - const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode); - printf( - (sizeof(KLONG)==8) - ? "%016"KLF"x %6luK %s %s\n" - : "%08lx %6luK %s %s\n", - start, - (unsigned long)(diff>>10), - flags, - cp - ); - } - - } + if (x_option) { + cp2 = + mapping_name(p, start, diff, mapbuf, 0, dev_major, + dev_minor, inode); + /* printed with the keys */ + continue; + } + if (d_option) { + const char *cp = + mapping_name(p, start, diff, mapbuf, 0, dev_major, + dev_minor, inode); + printf((sizeof(KLONG) == 8) + ? "%016" KLF "x %7lu %s %016Lx %03x:%05x %s\n" + : "%08lx %7lu %s %016Lx %03x:%05x %s\n", + start, + (unsigned long)(diff >> 10), + flags, file_offset, dev_major, dev_minor, cp); + } + if (!x_option && !d_option) { + const char *cp = + mapping_name(p, start, diff, mapbuf, 1, dev_major, + dev_minor, inode); + printf((sizeof(KLONG) == 8) + ? "%016" KLF "x %6luK %s %s\n" + : "%08lx %6luK %s %s\n", + start, (unsigned long)(diff >> 10), flags, cp); + } + } + if (!q_option) { + if (x_option) { + if (sizeof(KLONG) == 8) { + printf + ("---------------- ------ ------ ------\n"); + printf("total kB %15ld %7llu %7llu\n", + (total_shared + total_private_writeable + + total_private_readonly) >> 10, + total_rss, + (total_shared_dirty + + total_private_dirty) + ); + } else { + printf + ("-------- ------- ------- ------- -------\n"); + printf + ("total kB %7ld - - -\n", + (total_shared + total_private_writeable + + total_private_readonly) >> 10); + } + } + if (d_option) { + printf + ("mapped: %ldK writeable/private: %ldK shared: %ldK\n", + (total_shared + total_private_writeable + + total_private_readonly) >> 10, + total_private_writeable >> 10, total_shared >> 10); + } + if (!x_option && !d_option) { + if (sizeof(KLONG) == 8) + printf(" total %16ldK\n", + (total_shared + total_private_writeable + + total_private_readonly) >> 10); + else + printf(" total %8ldK\n", + (total_shared + total_private_writeable + + total_private_readonly) >> 10); + } + } - if(!q_option){ - if(x_option){ - if(sizeof(KLONG)==8){ - printf("---------------- ------ ------ ------\n"); - printf( - "total kB %15ld %7llu %7llu\n", - (total_shared + total_private_writeable + total_private_readonly) >> 10, - total_rss, (total_shared_dirty+total_private_dirty) - - ); - }else{ - printf("-------- ------- ------- ------- -------\n"); - printf( - "total kB %7ld - - -\n", - (total_shared + total_private_writeable + total_private_readonly) >> 10 - ); - } - } - if(d_option){ - printf( - "mapped: %ldK writeable/private: %ldK shared: %ldK\n", - (total_shared + total_private_writeable + total_private_readonly) >> 10, - total_private_writeable >> 10, - total_shared >> 10 - ); - } - if(!x_option && !d_option){ - if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); - else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); - } - } - - return 0; + return 0; } +int main(int argc, char **argv) +{ + unsigned *pidlist; + unsigned count = 0; + PROCTAB *PT; + proc_t p; + int ret = 0, c; -int main(int argc, char *argv[]){ - unsigned *pidlist; - unsigned count = 0; - PROCTAB* PT; - proc_t p; - int ret = 0; + static const struct option longopts[] = { + {"extended", no_argument, NULL, 'x'}, + {"device", no_argument, NULL, 'd'}, + {"quiet", no_argument, NULL, 'q'}, + {"range", required_argument, NULL, 'A'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} + }; - if(argc<2) usage(); - pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps + x_option = d_option = q_option = 0; - while(*++argv){ - if(!strcmp("--version",*argv)){ - V_option++; - continue; - } - if(**argv=='-'){ - char *walk = *argv; - if(!walk[1]) usage(); - while(*++walk){ - switch(*walk){ - case 'V': - V_option++; - break; - case 'x': - x_option++; - break; - case 'r': - r_option++; - break; - case 'd': - d_option++; - break; - case 'q': - q_option++; - break; - case 'A':{ - char *arg1; - if(walk[1]){ - arg1 = walk+1; - walk += strlen(walk)-1; - }else{ - arg1 = *++argv; - if(!arg1) - usage(); - } - char *arg2 = strchr(arg1,','); - if(arg2) - *arg2 = '\0'; - if(arg2) ++arg2; - else arg2 = arg1; - - if(*arg1) - range_low = STRTOUKL(arg1,&arg1,16); - if(*arg2) - range_high = STRTOUKL(arg2,&arg2,16); - if(*arg1 || *arg2) - usage(); - } - break; - case 'a': // Sun prints anon/swap reservations - case 'F': // Sun forces hostile ptrace-like grab - case 'l': // Sun shows unresolved dynamic names - case 'L': // Sun shows lgroup info - case 's': // Sun shows page sizes - case 'S': // Sun shows swap reservations - default: - usage(); - } - } - }else{ - char *walk = *argv; - char *endp; - unsigned long pid; - if(!strncmp("/proc/",walk,6)){ - walk += 6; - // user allowed to do: pmap /proc/* - if(*walk<'0' || *walk>'9') continue; - } - if(*walk<'0' || *walk>'9') usage(); - pid = strtoul(walk, &endp, 0); - if(pid<1ul || pid>0x7ffffffful || *endp) usage(); - pidlist[count++] = pid; - } - } + while ((c = getopt_long(argc, argv, "xrdqA:hV", longopts, NULL)) != -1) + switch (c) { + case 'x': + x_option = 1; + break; + case 'r': + warnx("option -r is ignored as SunOS compatibility"); + break; + case 'd': + d_option = 1; + break; + case 'q': + q_option = 1; + break; + case 'A': + { + /* FIXME: this should be a function. */ + char *walk = optarg; + char *arg1; + char *arg2; + if (walk[1]) { + arg1 = walk + 1; + walk += strlen(walk) - 1; + } else { + arg1 = *++argv; + if (!arg1) + usage(stderr); + } + arg2 = strchr(arg1, ','); + if (arg2) + *arg2 = '\0'; + arg2 = *arg2 ? arg2++ : arg1; - if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes - if(V_option){ - if(count|x_option|r_option|d_option|q_option) usage(); - fprintf(stdout, "pmap (%s)\n", procps_version); - return 0; - } - if(count<1) usage(); // no processes - if(d_option && x_option) usage(); + if (*arg1) + range_low = STRTOUKL(arg1, &arg1, 16); + if (*arg2) + range_high = STRTOUKL(arg2, &arg2, 16); + if (*arg1 || *arg2) + usage(stderr); + } + break; + case 'h': + usage(stdout); + case 'V': + display_version(); + return EXIT_SUCCESS; + case 'a': /* Sun prints anon/swap reservations */ + case 'F': /* Sun forces hostile ptrace-like grab */ + case 'l': /* Sun shows unresolved dynamic names */ + case 'L': /* Sun shows lgroup info */ + case 's': /* Sun shows page sizes */ + case 'S': /* Sun shows swap reservations */ + default: + usage(stderr); + } - discover_shm_minor(); + argc -= optind; + argv += optind; - memset(&p, '\0', sizeof(p)); - pidlist[count] = 0; // old libproc interface is zero-terminated - PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist); - while(readproc(PT, &p)){ - ret |= one_proc(&p); - count--; - } - closeproc(PT); + if (argc < 1) + errx(EXIT_FAILURE, "argument missing"); + if (d_option && x_option) + errx(EXIT_FAILURE, "options -d and -x can not coexist"); - if(count) ret |= 42; // didn't find all processes asked for - return ret; + pidlist = malloc(sizeof(unsigned) * argc); + if (pidlist == NULL) + err(EXIT_FAILURE, "cannot allocate %zu bytes", + sizeof(unsigned) * argc); + + while (*argv) { + char *walk = *argv++; + char *endp; + unsigned long pid; + if (!strncmp("/proc/", walk, 6)) { + walk += 6; + /* user allowed to do: pmap /proc/PID */ + if (*walk < '0' || *walk > '9') + continue; + } + if (*walk < '0' || *walk > '9') + usage(stderr); + pid = strtoul(walk, &endp, 0); + if (pid < 1ul || pid > 0x7ffffffful || *endp) + usage(stderr); + pidlist[count++] = pid; + } + + discover_shm_minor(); + + memset(&p, '\0', sizeof(p)); + /* old libproc interface is zero-terminated */ + pidlist[count] = 0; + PT = openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, pidlist); + while (readproc(PT, &p)) { + ret |= one_proc(&p); + count--; + } + closeproc(PT); + + if (count) + /* didn't find all processes asked for */ + ret |= 42; + return ret; }