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] [-A low,high] 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"
36 "-A limit results to the given range\n"
42 static unsigned KLONG range_low;
43 static unsigned KLONG range_high = ~0ull;
46 static int r_option; // ignored -- for SunOS compatibility
51 static unsigned shm_minor = ~0u;
53 static void discover_shm_minor(void){
58 if(!freopen("/proc/self/maps", "r", stdin)) return;
61 shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
62 if(shmid==-1) return; // failed; oh well
64 addr = shmat(shmid, NULL, SHM_RDONLY);
65 if(addr==(void*)-1) goto out_destroy;
67 while(fgets(mapbuf, sizeof mapbuf, stdin)){
69 char *tmp; // to clean up unprintables
70 unsigned KLONG start, end;
71 unsigned long long file_offset, inode;
72 unsigned dev_major, dev_minor;
73 sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
74 tmp = strchr(mapbuf,'\n');
78 if(!isprint(*tmp)) *tmp='?';
81 if(start > (unsigned long)addr) continue;
82 if(dev_major) continue;
83 if(flags[3] != 's') continue;
84 if(strstr(mapbuf,"/SYSV")){
85 shm_minor = dev_minor;
90 if(shmdt(addr)) perror("shmdt");
93 if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
99 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){
102 if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){
103 static char shmbuf[64];
104 snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode);
108 cp = strrchr(mapbuf,'/');
110 if(showpath) return strchr(mapbuf,'/');
111 return cp[1] ? cp+1 : cp;
114 cp = strchr(mapbuf,'/');
116 if(showpath) return cp;
117 return strrchr(cp,'/') + 1; // it WILL succeed
121 if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]";
125 static int one_proc(proc_t *p){
129 unsigned long total_shared = 0ul;
130 unsigned long total_private_readonly = 0ul;
131 unsigned long total_private_writeable = 0ul;
133 // Overkill, but who knows what is proper? The "w" prog
134 // uses the tty width to determine this.
135 int maxcmd = 0xfffff;
137 sprintf(buf,"/proc/%u/maps",p->tgid);
138 if(!freopen(buf, "r", stdin)) return 1;
140 escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
141 printf("%u: %s\n", p->tgid, cmdbuf);
143 if(!q_option && (x_option|d_option)){
145 if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
146 else printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
149 if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n");
150 else printf("Address Kbytes Mode Offset Device Mapping\n");
154 while(fgets(mapbuf,sizeof mapbuf,stdin)){
156 char *tmp; // to clean up unprintables
157 unsigned KLONG start, end, diff;
158 unsigned long long file_offset, inode;
159 unsigned dev_major, dev_minor;
160 sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
162 if(start > range_high)
167 tmp = strchr(mapbuf,'\n');
171 if(!isprint(*tmp)) *tmp='?';
176 if(flags[3]=='s') total_shared += diff;
179 if(flags[1]=='w') total_private_writeable += diff;
180 else total_private_readonly += diff;
183 // format used by Solaris 9 and procps-3.2.0+
184 // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.)
189 const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
192 ? "%016"KLF"x %7lu - - - %s %s\n"
193 : "%08lx %7lu - - - %s %s\n",
195 (unsigned long)(diff>>10),
201 const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
204 ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n"
205 : "%08lx %7lu %s %016Lx %03x:%05x %s\n",
207 (unsigned long)(diff>>10),
210 dev_major, dev_minor,
214 if(!x_option && !d_option){
215 const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
218 ? "%016"KLF"x %6luK %s %s\n"
219 : "%08lx %6luK %s %s\n",
221 (unsigned long)(diff>>10),
234 if(sizeof(KLONG)==8){
235 printf("---------------- ------ ------ ------ ------\n");
237 "total kB %15ld - - -\n",
238 (total_shared + total_private_writeable + total_private_readonly) >> 10
241 printf("-------- ------- ------- ------- -------\n");
243 "total kB %7ld - - -\n",
244 (total_shared + total_private_writeable + total_private_readonly) >> 10
250 "mapped: %ldK writeable/private: %ldK shared: %ldK\n",
251 (total_shared + total_private_writeable + total_private_readonly) >> 10,
252 total_private_writeable >> 10,
256 if(!x_option && !d_option){
257 if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
258 else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
266 int main(int argc, char *argv[]){
274 pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps
277 if(!strcmp("--version",*argv)){
283 if(!walk[1]) usage();
305 walk += strlen(walk)-1;
311 char *arg2 = strchr(arg1,',');
314 arg2 = arg2 ? arg2++ : arg1;
317 range_low = STRTOUKL(arg1,&arg1,16);
319 range_high = STRTOUKL(arg2,&arg2,16);
324 case 'a': // Sun prints anon/swap reservations
325 case 'F': // Sun forces hostile ptrace-like grab
326 case 'l': // Sun shows unresolved dynamic names
327 case 'L': // Sun shows lgroup info
328 case 's': // Sun shows page sizes
329 case 'S': // Sun shows swap reservations
338 if(!strncmp("/proc/",walk,6)){
340 // user allowed to do: pmap /proc/*
341 if(*walk<'0' || *walk>'9') continue;
343 if(*walk<'0' || *walk>'9') usage();
344 pid = strtoul(walk, &endp, 0);
345 if(pid<1ul || pid>0x7ffffffful || *endp) usage();
346 pidlist[count++] = pid;
350 if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes
352 if(count|x_option|r_option|d_option|q_option) usage();
353 fprintf(stdout, "pmap (%s)\n", procps_version);
356 if(count<1) usage(); // no processes
357 if(d_option && x_option) usage();
359 discover_shm_minor();
361 pidlist[count] = 0; // old libproc interface is zero-terminated
362 PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist);
363 while(readproc(PT, &p)){
365 if(p.cmdline) free((void*)*p.cmdline);
370 if(count) ret |= 42; // didn't find all processes asked for