2 * Copyright 2002 by Albert Cahalan; all rights reserved.
3 * This file may be used subject to the terms and conditions of the
4 * GNU Library General Public License Version 2, or any later version
5 * at your option, as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU Library General Public License for more details.
15 #include <sys/types.h>
24 #include "proc/readproc.h"
25 #include "proc/version.h"
26 #include "proc/escape.h"
28 static void usage(void) NORETURN;
29 static void usage(void){
31 "Usage: pmap [-x | -d] [-q] pid...\n"
33 "-d show offset and device number\n"
34 "-q quiet; less header/footer info\n"
35 "-V show the version number\n"
42 static int r_option; // ignored -- for SunOS compatibility
48 static unsigned shm_minor = ~0u;
50 static void discover_shm_minor(void){
55 if(!freopen("/proc/self/maps", "r", stdin)) return;
58 shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
59 if(shmid==-1) return; // failed; oh well
61 addr = shmat(shmid, NULL, SHM_RDONLY);
62 if(addr==(void*)-1) goto out_destroy;
64 while(fgets(mapbuf, sizeof mapbuf, stdin)){
66 char *tmp; // to clean up unprintables
67 unsigned KLONG start, end;
68 unsigned long long file_offset, inode;
69 unsigned dev_major, dev_minor;
70 sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
71 tmp = strchr(mapbuf,'\n');
75 if(!isprint(*tmp)) *tmp='?';
78 if(start > (unsigned long)addr) continue;
79 if(dev_major) continue;
80 if(flags[3] != 's') continue;
81 if(strstr(mapbuf,"/SYSV")){
82 shm_minor = dev_minor;
87 if(shmdt(addr)) perror("shmdt");
90 if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
96 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){
99 if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){
100 static char shmbuf[64];
101 snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode);
105 cp = strrchr(mapbuf,'/');
107 if(showpath) return strchr(mapbuf,'/');
108 return cp[1] ? cp+1 : cp;
111 cp = strchr(mapbuf,'/');
113 if(showpath) return cp;
114 return strrchr(cp,'/') + 1; // it WILL succeed
118 if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]";
122 static int one_proc(proc_t *p){
126 unsigned long total_shared = 0ul;
127 unsigned long total_private_readonly = 0ul;
128 unsigned long total_private_writeable = 0ul;
130 // Overkill, but who knows what is proper? The "w" prog
131 // uses the tty width to determine this.
132 int maxcmd = 0xfffff;
134 sprintf(buf,"/proc/%u/maps",p->tgid);
135 if(!freopen(buf, "r", stdin)) return 1;
137 escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
138 printf("%u: %s\n", p->tgid, cmdbuf);
140 if(!q_option && (x_option|d_option)){
142 if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
143 else printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
146 if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n");
147 else printf("Address Kbytes Mode Offset Device Mapping\n");
151 while(fgets(mapbuf,sizeof mapbuf,stdin)){
153 char *tmp; // to clean up unprintables
154 unsigned KLONG start, end, diff;
155 unsigned long long file_offset, inode;
156 unsigned dev_major, dev_minor;
157 sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
158 tmp = strchr(mapbuf,'\n');
162 if(!isprint(*tmp)) *tmp='?';
167 if(flags[3]=='s') total_shared += diff;
170 if(flags[1]=='w') total_private_writeable += diff;
171 else total_private_readonly += diff;
174 // format used by Solaris 9 and procps-3.2.0+
175 // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.)
180 const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
183 ? "%016"KLF"x %7lu - - - %s %s\n"
184 : "%08lx %7lu - - - %s %s\n",
186 (unsigned long)(diff>>10),
192 const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
195 ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n"
196 : "%08lx %7lu %s %016Lx %03x:%05x %s\n",
198 (unsigned long)(diff>>10),
201 dev_major, dev_minor,
205 if(!x_option && !d_option){
206 const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
209 ? "%016"KLF"x %6luK %s %s\n"
210 : "%08lx %6luK %s %s\n",
212 (unsigned long)(diff>>10),
222 if(sizeof(KLONG)==8){
223 printf("---------------- ------ ------ ------ ------\n");
225 "total kB %15ld - - -\n",
226 (total_shared + total_private_writeable + total_private_readonly) >> 10
229 printf("-------- ------- ------- ------- -------\n");
231 "total kB %7ld - - -\n",
232 (total_shared + total_private_writeable + total_private_readonly) >> 10
238 "mapped: %ldK writeable/private: %ldK shared: %ldK\n",
239 (total_shared + total_private_writeable + total_private_readonly) >> 10,
240 total_private_writeable >> 10,
244 if(!x_option && !d_option){
245 if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
246 else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
254 int main(int argc, char *argv[]){
262 pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps
265 if(!strcmp("--version",*argv)){
271 if(!walk[1]) usage();
297 if(!strncmp("/proc/",walk,6)){
299 // user allowed to do: pmap /proc/*
300 if(*walk<'0' || *walk>'9') continue;
302 if(*walk<'0' || *walk>'9') usage();
303 pid = strtoul(walk, &endp, 0);
304 if(pid<1ul || pid>0x7ffffffful || *endp) usage();
305 pidlist[count++] = pid;
309 if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes
311 if(count|x_option|r_option|d_option|q_option) usage();
312 fprintf(stdout, "pmap (%s)\n", procps_version);
315 if(count<1) usage(); // no processes
316 if(d_option && x_option) usage();
318 discover_shm_minor();
320 pidlist[count] = 0; // old libproc interface is zero-terminated
321 PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist);
322 while(readproc(PT, &p)){
324 if(p.cmdline) free((void*)*p.cmdline);
329 if(count) ret |= 42; // didn't find all processes asked for