326 lines
8.8 KiB
C
326 lines
8.8 KiB
C
/*
|
|
* Copyright 2002 by Albert Cahalan; all rights reserved.
|
|
* This file may be used subject to the terms and conditions of the
|
|
* GNU Library General Public License Version 2, or any later version
|
|
* at your option, as published by the Free Software Foundation.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Library General Public License for more details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.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] 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"
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
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 unsigned shm_minor = ~0u;
|
|
|
|
static void discover_shm_minor(void){
|
|
void *addr;
|
|
int shmid;
|
|
char mapbuf[256];
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(shmdt(addr)) perror("shmdt");
|
|
|
|
out_destroy:
|
|
if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned advance, 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;
|
|
}
|
|
|
|
cp = strrchr(mapbuf,'/');
|
|
if(cp){
|
|
if(cp[1]) cp += advance;
|
|
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];
|
|
unsigned long total_shared = 0ul;
|
|
unsigned long total_private_readonly = 0ul;
|
|
unsigned long total_private_writeable = 0ul;
|
|
|
|
// 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(!freopen(buf, "r", stdin)) return 1;
|
|
|
|
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 Anon Locked Mode Mapping\n");
|
|
else printf("Address Kbytes RSS Anon Locked 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,stdin)){
|
|
char flags[32];
|
|
char *tmp; // to clean up unprintables
|
|
unsigned KLONG start, end, diff;
|
|
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++;
|
|
}
|
|
|
|
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){
|
|
const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
|
|
printf(
|
|
(sizeof(KLONG)==8)
|
|
? "%016"KLF"x %7lu - - - %s %s\n"
|
|
: "%08lx %7lu - - - %s %s\n",
|
|
start,
|
|
(unsigned long)(diff>>10),
|
|
flags,
|
|
cp
|
|
);
|
|
}
|
|
if(d_option){
|
|
const char *cp = mapping_name(p, start, diff, mapbuf, 1, 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, 0, 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 - - -\n",
|
|
(total_shared + total_private_writeable + total_private_readonly) >> 10
|
|
);
|
|
}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;
|
|
|
|
if(argc<2) usage();
|
|
pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps
|
|
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
discover_shm_minor();
|
|
|
|
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);
|
|
if(p.cmdline) free((void*)*p.cmdline);
|
|
count--;
|
|
}
|
|
closeproc(PT);
|
|
|
|
if(count) ret |= 42; // didn't find all processes asked for
|
|
return ret;
|
|
}
|