/* * 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 #include #include #include #include #include #include #include #include #include #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 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; } 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; return strrchr(cp,'/') + 1; // it WILL succeed } 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, 0, 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, 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 - - -\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; }