minor optimizations
[util-vserver.git] / util-vserver / src / vserver-stat.c
1 // $Id$
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vserver-stat.cc by Guillaum Dallaire and Jacques Gelinas
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10 //  
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //  
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "vserver.h"
25 #include "util.h"
26 #include "internal.h"
27
28 #include <ensc_vector/vector.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <syscall.h>
41 #include <time.h>
42 #include <stdbool.h>
43 #include <sys/param.h>
44
45 #define ENSC_WRAPPERS_DIRENT    1
46 #define ENSC_WRAPPERS_VSERVER   1
47 #define ENSC_WRAPPERS_FCNTL     1
48 #define ENSC_WRAPPERS_UNISTD    1
49 #include "wrappers.h"
50
51 #define PROC_DIR_NAME "/proc"
52 #define CTX_DIR_NAME "/var/run/vservers/"
53 #define CTX_NAME_MAX_LEN 50
54
55 int     wrapper_exit_code = 1;
56
57 #ifndef AT_CLKTCK
58 #define AT_CLKTCK       17    /* frequency of times() */
59 #endif
60
61 static unsigned long    hertz=0x42;
62
63 struct XidData
64 {
65     xid_t       xid;
66     int         process_count;
67     int         VmSize_total;
68     int         VmRSS_total;
69     uint64_t    start_time_oldest;
70     uint64_t    stime_total, utime_total;
71 };
72
73 struct process_info
74 {
75         long            VmSize;         // number of pages of virtual memory
76         long            VmRSS;          // resident set size from /proc/#/stat
77         uint64_t        start_time;     // start time of process -- milliseconds since 1-1-70
78         uint64_t        stime, utime;   // kernel & user-mode CPU time accumulated by process
79         uint64_t        cstime, cutime; // cumulative time of process and reaped children
80         xid_t           s_context;
81 };
82
83 struct ArgInfo {
84     enum { tpUNSET, tpCTX, tpPID }      type;
85     xid_t               ctx;
86     pid_t               pid;
87     unsigned int        interval;
88     bool                shutdown;
89     bool                omit_init;
90     size_t              argc;
91     char * const *      argv;
92 };
93
94 static void
95 showHelp(int fd, char const *cmd, int res)
96 {
97   WRITE_MSG(fd, "Usage:  ");
98   WRITE_STR(fd, cmd);
99   WRITE_MSG(fd,
100             "\n"
101             "Show informations about all the active context.\n\n"
102             "   CTX#            Context number\n"
103             "                   #0 = root context\n"
104             "                   #1 = monitoring context\n"
105             "   PROC QTY        Quantity of processes in each context\n"
106             "   VSZ             Number of pages of virtual memory\n"
107             "   RSS             Resident set size\n"
108             "   userTIME        User-mode CPU time accumulated\n"
109             "   sysTIME         Kernel-mode CPU time accumulated\n"
110             "   UPTIME          Uptime/context\n"
111             "   NAME            Virtual server name\n"
112             "\n");
113   exit(res);
114 }
115
116 static void
117 showVersion()
118 {
119   WRITE_MSG(1,
120             "vserver-stat " VERSION " -- show virtual context statistics\n"
121             "This program is part of " PACKAGE_STRING "\n\n"
122             "Copyright (C) 2003 Enrico Scholz\n"
123             VERSION_COPYRIGHT_DISCLAIMER);
124   exit(0);
125 }
126
127
128 // return uptime (in ms) from /proc/uptime
129 static uint64_t
130 getUptime()
131 {
132   int           fd;
133   char          buffer[64];
134   char *        errptr;
135   size_t        len;
136   uint64_t      secs;
137   uint32_t      msecs=0;
138
139     // open the /proc/uptime file
140   fd  = EopenD("/proc/uptime", O_RDONLY, 0);
141   len = Eread(fd, buffer, sizeof buffer);
142
143   if (len==sizeof(buffer)) {
144     WRITE_MSG(2, "Too much data in /proc/uptime; aborting...\n");
145     exit(1);
146   }
147   Eclose(fd);
148
149   while (len>0 && buffer[len-1]=='\n') --len;
150   buffer[len] = '\0';
151
152   secs = strtol(buffer, &errptr, 10);
153   if (*errptr!='.') errptr = buffer;
154   else {
155     unsigned int        mult;
156     switch (strlen(errptr+1)) {
157       case 0    :  mult = 1000; break;
158       case 1    :  mult = 100;  break;
159       case 2    :  mult = 10;   break;
160       case 3    :  mult = 1;    break;
161       default   :  mult = 0;    break;
162     }
163     msecs = strtol(errptr+1, &errptr, 10) * mult;
164   }
165
166   if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
167     WRITE_MSG(2, "Bad data in /proc/uptime\n");
168     exit(1);
169   }
170
171   return secs*1000 + msecs;
172 }
173
174 static int
175 cmpData(void const *xid_v, void const *map_v)
176 {
177   xid_t const * const                   xid = xid_v;
178   struct XidData const * const          map = map_v;
179   int   res = *xid - map->xid;
180
181   return res;
182 }
183
184 static void
185 registerXid(struct Vector *vec, struct process_info *process)
186 {
187   struct XidData        *res;
188
189   res = Vector_search(vec, &process->s_context, cmpData);
190   if (res==0) {
191     res = Vector_insert(vec, &process->s_context, cmpData);
192     res->xid           = process->s_context;
193     res->process_count = 0;
194     res->VmSize_total  = 0;
195     res->VmRSS_total   = 0;
196     res->utime_total   = 0;
197     res->stime_total   = 0;
198     res->start_time_oldest = process->start_time;
199   }
200
201   ++res->process_count;
202   res->VmSize_total += process->VmSize;
203   res->VmRSS_total  += process->VmRSS;
204   res->utime_total  += process->utime + process->cutime;
205   res->stime_total  += process->stime + process->cstime;
206
207   res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
208 }
209
210 static inline uint64_t
211 toMsec(uint64_t v)
212 {
213   return v*1000llu/hertz;
214 }
215
216
217 // shamelessly stolen from procps...
218 static unsigned long
219 find_elf_note(unsigned long findme){
220   unsigned long *ep = (unsigned long *)environ;
221   while(*ep++);
222   while(*ep){
223     if(ep[0]==findme) return ep[1];
224     ep+=2;
225   }
226   return (unsigned long)(-1);
227 }
228
229 static void initHertz() __attribute__((__constructor__));
230
231 static void
232 initHertz()
233 {
234   hertz = find_elf_note(AT_CLKTCK);
235   if (hertz==(unsigned long)(-1))
236     hertz = sysconf(_SC_CLK_TCK);
237 }
238
239 // open the process's status file to get the ctx number, and other stat
240 struct process_info *
241 get_process_info(char *pid)
242 {
243   int                           fd;
244   char                          buffer[1024];
245   char                          *p;
246   size_t                        idx, l=strlen(pid);
247   static struct process_info    process;
248
249 #if 1
250   process.s_context = vc_get_task_xid(atoi(pid));
251 #else
252 #  warning Compiling in debug-code
253   process.s_context = random()%6;
254 #endif
255
256   if (process.s_context==VC_NOCTX) {
257     int         err=errno;
258     WRITE_MSG(2, "vc_get_task_xid(");
259     WRITE_STR(2, pid);
260     WRITE_MSG(2, "): ");
261     WRITE_STR(2, strerror(err));
262     WRITE_MSG(2, "\n");
263
264     return 0;
265   }
266   
267   memcpy(buffer,     "/proc/", 6); idx  = 6;
268   memcpy(buffer+idx, pid,      l); idx += l;
269   memcpy(buffer+idx, "/stat",  6);
270         
271     // open the /proc/#/stat file
272   if ((fd = open(buffer, O_RDONLY, 0)) == -1)
273     return NULL;
274     // put the file in a buffer
275   if (read(fd, buffer, sizeof(buffer)) < 1)
276     return NULL;
277
278   close(fd);
279
280   p   = strchr(buffer, ')');            // go after the PID (process_name)
281   for (idx = 0; idx<12 && *p!='\0'; ++p)
282     if ((*p)==' ') ++idx;
283
284   process.utime  = toMsec(strtol(p,   &p, 10));
285   process.stime  = toMsec(strtol(p+1, &p, 10));
286   process.cutime = toMsec(strtol(p+1, &p, 10));
287   process.cstime = toMsec(strtol(p+1, &p, 10));
288
289   for (idx = 0; idx<5 && *p!='\0'; ++p)
290     if ((*p)==' ') ++idx;
291
292   process.start_time = toMsec(strtol(p,   &p, 10));
293   process.VmSize     = strtol(p+1, &p, 10);
294   process.VmRSS      = strtol(p+1, &p, 10);
295
296   //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
297   return &process;
298 }
299
300 static size_t
301 fillUintZero(char *buf, unsigned long val, size_t cnt)
302 {
303   size_t        l;
304   
305   l = utilvserver_fmt_ulong(buf, val);
306   if (l<cnt) {
307     memmove(buf+cnt-l, buf, l);
308     memset(buf, '0', cnt-l);
309   }
310   buf[cnt] = '\0';
311
312   return cnt;
313 }
314
315 static void
316 shortenMem(char *buf, unsigned long val)
317 {
318   char const *  SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
319   char          tmp[16];
320   char const *  suffix = "+";
321   size_t        i, l;
322   unsigned int  mod = 0;
323
324   for (i=0; i<6; ++i) {
325     if (val<1000) {
326       suffix = SUFFIXES[i];
327       break;
328     }
329     mod   = 10*(val & 1023)/1024;
330     val >>= 10;
331   }
332
333   if (val >9999) val=9999;
334   if (val>=1000) mod=0;
335
336   l = utilvserver_fmt_ulong(tmp, val);
337   if (mod!=0) {
338     tmp[l++] = '.';
339     l += utilvserver_fmt_ulong(tmp+l, mod);
340   }
341   i = 7-l-strlen(suffix);
342   
343   memcpy(buf+i,   tmp, l);
344   memcpy(buf+i+l, suffix, strlen(suffix));
345 }
346
347 static void
348 shortenTime(char *buf, uint64_t t)
349 {
350   char          tmp[32];
351   char          *ptr = tmp;
352
353   unsigned long hh, mm, ss, ms;
354
355   ms = t % 1000;
356   t /= 1000;
357
358   ss = t%60;
359   t /= 60;
360   mm = t%60;
361   t /= 60;
362   hh = t%60;
363   t /= 24;
364
365   if (t>999*999) {
366     memcpy(ptr, "INVALID", 7);
367     ptr   += 7;
368   }
369   else if (t>999) {
370     ptr   += utilvserver_fmt_ulong(ptr, t/365);
371     *ptr++ = 'y';
372     ptr   += fillUintZero(ptr, t%365, 2);
373     *ptr++ = 'd';
374     ptr   += fillUintZero(ptr, hh, 2);
375   }    
376   else if (t>0) {
377     ptr   += utilvserver_fmt_ulong(ptr, t);
378     *ptr++ = 'd';
379     ptr   += fillUintZero(ptr, hh, 2);
380     *ptr++ = 'h';
381     ptr   += fillUintZero(ptr, mm, 2);
382   }
383   else if (hh>0) {
384     ptr   += utilvserver_fmt_ulong(ptr, hh);
385     *ptr++ = 'h';
386     ptr   += fillUintZero(ptr, mm, 2);
387     *ptr++ = 'm';
388     ptr   += fillUintZero(ptr, ss, 2);    
389   }
390   else {
391     ptr   += utilvserver_fmt_ulong(ptr, mm);
392     *ptr++ = 'm';
393     ptr   += fillUintZero(ptr, ss, 2);
394     *ptr++ = 's';
395     ptr   += fillUintZero(ptr, ms, 2);
396   }
397
398   *ptr = ' ';
399   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
400 }
401
402 static void
403 showContexts(struct Vector *vec)
404 {
405   uint64_t                      uptime  = getUptime();
406   struct XidData const *        ptr     = Vector_begin_const(vec);
407   struct XidData const * const  end_ptr = Vector_end_const(vec);
408   
409
410   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
411   for (; ptr<end_ptr; ++ptr) {
412     char        buf[sizeof(xid_t)*3 + 512];
413     char        tmp[sizeof(int)*3 + 2];
414     size_t      l;
415
416     memset(buf, ' ', sizeof(buf));
417     l = utilvserver_fmt_long(buf, ptr->xid);
418     l = utilvserver_fmt_long(tmp, ptr->process_count);
419     memcpy(buf+10-l, tmp, l);
420
421     shortenMem (buf+10, ptr->VmSize_total);
422     shortenMem (buf+17, ptr->VmRSS_total);
423     shortenTime(buf+24, ptr->utime_total);
424     shortenTime(buf+34, ptr->stime_total);
425     //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
426     shortenTime(buf+44, uptime - ptr->start_time_oldest);
427
428     switch (ptr->xid) {
429       case 0            :  strcpy(buf+55, "root server"      ); break;
430       case 1            :  strcpy(buf+55, "monitoring server"); break;
431       default           : {
432         char *          name     = 0;
433         char *          cfgpath  = 0;
434         vcCfgStyle      cfgstyle = vcCFG_AUTO;
435
436         if ((cfgpath = vc_getVserverByCtx(ptr->xid, &cfgstyle, 0))==0 ||
437             (name    = vc_getVserverName(cfgpath, cfgstyle))==0) {
438           name     = strdup("");
439           cfgstyle = vcCFG_NONE;
440         }
441         
442         switch (cfgstyle) {
443           case vcCFG_LEGACY     : {
444             size_t      len = MIN(strlen(name), 18);
445             buf[55]     = '[';
446             memcpy(buf+56,     name, len);
447             memcpy(buf+56+len, "]",  2);
448             break;
449           }
450           default               : {
451             size_t      len = MIN(strlen(name), 20);
452             memcpy(buf+55, name, len);
453             buf[55+len] = '\0';
454             break;
455           }
456         }
457         
458         free(name);
459         free(cfgpath);
460       }
461     }
462
463     write(1, buf, strlen(buf));
464     write(1, "\n", 1);
465   }
466 }
467
468 int main(int argc, char **argv)
469 {
470   DIR *                 proc_dir;
471   struct dirent*        dir_entry;
472   pid_t                 my_pid;
473   struct Vector         xid_data;
474   char const *          errptr;
475
476   if (argc==2) {
477     if (strcmp(argv[1], "--help")   ==0) showHelp(1, argv[0], 0);
478     if (strcmp(argv[1], "--version")==0) showVersion();
479   }
480   if (argc!=1) {
481     WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
482     return EXIT_FAILURE;
483   }
484
485   if (hertz==0x42) initHertz();
486   
487   //printf("hertz=%lu\n", hertz);
488     // do not include own stat
489   my_pid = getpid();
490
491   if (!switchToWatchXid(&errptr)) {
492     perror(errptr);
493     exit(1);
494   }
495
496   Vector_init(&xid_data, sizeof(struct XidData));
497
498   Echdir(PROC_DIR_NAME);
499   proc_dir = Eopendir(".");
500   while ((dir_entry = readdir(proc_dir)) != NULL)
501   {
502       // select only process file
503     if (!isdigit(*dir_entry->d_name))
504       continue;
505
506     if (atoi(dir_entry->d_name) != my_pid) {
507       struct process_info *     info = get_process_info(dir_entry->d_name);
508       if (info)
509         registerXid(&xid_data, info);
510     }
511   }
512   closedir(proc_dir);
513
514     // output the ctx_list      
515   showContexts(&xid_data);
516
517 #ifndef NDEBUG
518   Vector_free(&xid_data);
519 #endif
520   
521   return 0;
522 }