pmap: new usage & fix coding style

Coding style fixed and more readable help output.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2011-06-05 18:05:26 +02:00
parent 6dd092412f
commit d50884788d

771
pmap.c
View File

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