sync with master branch
[external/procps.git] / pmap.c
1 /*
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.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include <sys/ipc.h>
22 #include <sys/shm.h>
23
24 #include "proc/readproc.h"
25 #include "proc/version.h"
26 #include "proc/escape.h"
27
28 static void usage(void) NORETURN;
29 static void usage(void){
30   fprintf(stderr,
31     "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n"
32     "-x  show details\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"
37   );
38   exit(1);
39 }
40
41
42 static unsigned KLONG range_low;
43 static unsigned KLONG range_high = ~0ull;
44
45 static int V_option;
46 static int r_option;  // ignored -- for SunOS compatibility
47 static int x_option;
48 static int d_option;
49 static int q_option;
50
51 static unsigned shm_minor = ~0u;
52
53 static void discover_shm_minor(void){
54   void *addr;
55   int shmid;
56   char mapbuf[256];
57
58   if(!freopen("/proc/self/maps", "r", stdin)) return;
59
60   // create
61   shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
62   if(shmid==-1) return; // failed; oh well
63   // attach
64   addr = shmat(shmid, NULL, SHM_RDONLY);
65   if(addr==(void*)-1) goto out_destroy;
66
67   while(fgets(mapbuf, sizeof mapbuf, stdin)){
68     char flags[32];
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');
75     if(tmp) *tmp='\0';
76     tmp = mapbuf;
77     while(*tmp){
78       if(!isprint(*tmp)) *tmp='?';
79       tmp++;
80     }
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;
86       break;
87     }
88   }
89
90   if(shmdt(addr)) perror("shmdt");
91
92 out_destroy:
93   if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
94
95   return;
96 }
97
98
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){
100   const char *cp;
101
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);
105     return shmbuf;
106   }
107
108   cp = strrchr(mapbuf,'/');
109   if(cp){
110     if(showpath) return strchr(mapbuf,'/');
111     return cp[1] ? cp+1 : cp;
112   }
113
114   cp = strchr(mapbuf,'/');
115   if(cp){
116     if(showpath) return cp;
117     return strrchr(cp,'/') + 1;  // it WILL succeed
118   }
119
120   cp = "  [ anon ]";
121   if( (p->start_stack >= addr) && (p->start_stack <= addr+len) )  cp = "  [ stack ]";
122   return cp;
123 }
124
125 static int one_proc(proc_t *p){
126   char buf[32];
127   char mapbuf[9600];
128   char cmdbuf[512];
129   unsigned long total_shared = 0ul;
130   unsigned long total_private_readonly = 0ul;
131   unsigned long total_private_writeable = 0ul;
132
133   // Overkill, but who knows what is proper? The "w" prog
134   // uses the tty width to determine this.
135   int maxcmd = 0xfffff;
136
137   sprintf(buf,"/proc/%u/maps",p->tgid);
138   if(!freopen(buf, "r", stdin)) return 1;
139
140   escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
141   printf("%u:   %s\n", p->tgid, cmdbuf);
142
143   if(!q_option && (x_option|d_option)){
144     if(x_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");
147     }
148     if(d_option){
149       if(sizeof(KLONG)==4) printf("Address   Kbytes Mode  Offset           Device    Mapping\n");
150       else         printf("Address           Kbytes Mode  Offset           Device    Mapping\n");
151     }
152   }
153
154   while(fgets(mapbuf,sizeof mapbuf,stdin)){
155     char flags[32];
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);
161
162     if(start > range_high)
163       break;
164     if(end < range_low)
165       continue;
166
167     tmp = strchr(mapbuf,'\n');
168     if(tmp) *tmp='\0';
169     tmp = mapbuf;
170     while(*tmp){
171       if(!isprint(*tmp)) *tmp='?';
172       tmp++;
173     }
174     
175     diff = end-start;
176     if(flags[3]=='s') total_shared  += diff;
177     if(flags[3]=='p'){
178       flags[3] = '-';
179       if(flags[1]=='w') total_private_writeable += diff;
180       else              total_private_readonly  += diff;
181     }
182
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.)
185     flags[4] = '-';
186     flags[5] = '\0';
187
188     if(x_option){
189       const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
190       printf(
191         (sizeof(KLONG)==8)
192           ? "%016"KLF"x %7lu       -       -       - %s  %s\n"
193           :      "%08lx %7lu       -       -       - %s  %s\n",
194         start,
195         (unsigned long)(diff>>10),
196         flags,
197         cp
198       );
199     }
200     if(d_option){
201       const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
202       printf(
203         (sizeof(KLONG)==8)
204           ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n"
205           :      "%08lx %7lu %s %016Lx %03x:%05x %s\n",
206         start,
207         (unsigned long)(diff>>10),
208         flags,
209         file_offset,
210         dev_major, dev_minor,
211         cp
212       );
213     }
214     if(!x_option && !d_option){
215       const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
216       printf(
217         (sizeof(KLONG)==8)
218           ? "%016"KLF"x %6luK %s  %s\n"
219           :      "%08lx %6luK %s  %s\n",
220         start,
221         (unsigned long)(diff>>10),
222         flags,
223         cp
224       );
225     }
226     
227   }
228
229
230
231
232   if(!q_option){
233     if(x_option){
234       if(sizeof(KLONG)==8){
235         printf("----------------  ------  ------  ------  ------\n");
236         printf(
237           "total kB %15ld       -       -       -\n",
238           (total_shared + total_private_writeable + total_private_readonly) >> 10
239         );
240       }else{
241         printf("-------- ------- ------- ------- -------\n");
242         printf(
243           "total kB %7ld       -       -       -\n",
244           (total_shared + total_private_writeable + total_private_readonly) >> 10
245         );
246       }
247     }
248     if(d_option){
249         printf(
250           "mapped: %ldK    writeable/private: %ldK    shared: %ldK\n",
251           (total_shared + total_private_writeable + total_private_readonly) >> 10,
252           total_private_writeable >> 10,
253           total_shared >> 10
254         );
255     }
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);
259     }
260   }
261
262   return 0;
263 }
264
265
266 int main(int argc, char *argv[]){
267   unsigned *pidlist;
268   unsigned count = 0;
269   PROCTAB* PT;
270   proc_t p;
271   int ret = 0;
272
273   if(argc<2) usage();
274   pidlist = malloc(sizeof(unsigned)*argc);  // a bit more than needed perhaps
275
276   while(*++argv){
277     if(!strcmp("--version",*argv)){
278       V_option++;
279       continue;
280     }
281     if(**argv=='-'){
282       char *walk = *argv;
283       if(!walk[1]) usage();
284       while(*++walk){
285         switch(*walk){
286         case 'V':
287           V_option++;
288           break;
289         case 'x':
290           x_option++;
291           break;
292         case 'r':
293           r_option++;
294           break;
295         case 'd':
296           d_option++;
297           break;
298         case 'q':
299           q_option++;
300           break;
301         case 'A':{
302             char *arg1;
303             if(walk[1]){
304               arg1 = walk+1;
305               walk += strlen(walk)-1;
306             }else{
307               arg1 = *++argv;
308               if(!arg1)
309                 usage();
310             }
311             char *arg2 = strchr(arg1,',');
312             if(arg2)
313               *arg2 = '\0';
314             arg2 = arg2 ? arg2++ : arg1;
315             
316             if(*arg1)
317               range_low = STRTOUKL(arg1,&arg1,16);
318             if(*arg2)
319               range_high = STRTOUKL(arg2,&arg2,16);
320             if(*arg1 || *arg2)
321                 usage();
322           }
323           break;
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
330         default:
331           usage();
332         }
333       }
334     }else{
335       char *walk = *argv;
336       char *endp;
337       unsigned long pid;
338       if(!strncmp("/proc/",walk,6)){
339         walk += 6;
340         // user allowed to do: pmap /proc/*
341         if(*walk<'0' || *walk>'9') continue;
342       }
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;
347     }
348   }
349
350   if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes
351   if(V_option){
352     if(count|x_option|r_option|d_option|q_option) usage();
353     fprintf(stdout, "pmap (%s)\n", procps_version);
354     return 0;
355   }
356   if(count<1) usage();   // no processes
357   if(d_option && x_option) usage();
358
359   discover_shm_minor();
360
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)){
364     ret |= one_proc(&p);
365     if(p.cmdline) free((void*)*p.cmdline);
366     count--;
367   }
368   closeproc(PT);
369
370   if(count) ret |= 42;  // didn't find all processes asked for
371   return ret;
372 }