minor optimizations
[util-vserver.git] / util-vserver / src / vserver-stat.c
index d107121..d8ee389 100644 (file)
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
-/*
-       vserver-stat help you to see all the active context currently in the kernel
-       with some useful stat
-
-       Changelog:
-
-       2003-01-08 Jacques Gelinas: Shows vserver description
-       2002-02-28 Jacques Gelinas: Use dynamic system call
-       2002-06-05 Martial Rioux : fix memory output error
-       2002-12-05 Martial Rioux : fix output glitch
-       2001-11-29 added uptime/ctx stat
-       2001-11-20 added vmsize, rss, stime and utime stat
-*/
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
@@ -37,9 +24,8 @@
 #include "vserver.h"
 #include "util.h"
 #include "internal.h"
-#include "wrappers.h"
-#include "wrappers-dirent.h"
-#include "wrappers-vserver.h"
+
+#include <ensc_vector/vector.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <syscall.h>
 #include <time.h>
 #include <stdbool.h>
+#include <sys/param.h>
+
+#define ENSC_WRAPPERS_DIRENT   1
+#define ENSC_WRAPPERS_VSERVER  1
+#define ENSC_WRAPPERS_FCNTL    1
+#define ENSC_WRAPPERS_UNISTD   1
+#include "wrappers.h"
 
 #define PROC_DIR_NAME "/proc"
 #define CTX_DIR_NAME "/var/run/vservers/"
 
 int    wrapper_exit_code = 1;
 
-struct ctx_list
+#ifndef AT_CLKTCK
+#define AT_CLKTCK       17    /* frequency of times() */
+#endif
+
+static unsigned long   hertz=0x42;
+
+struct XidData
 {
-       int ctx;
-       int process_count;
-       int VmSize_total;
-       int VmRSS_total;
-       long start_time_oldest;
-       long stime_total, utime_total;
-       char name[CTX_NAME_MAX_LEN];
-       struct ctx_list *next;
-} *my_ctx_list;
+    xid_t      xid;
+    int                process_count;
+    int                VmSize_total;
+    int                VmRSS_total;
+    uint64_t   start_time_oldest;
+    uint64_t   stime_total, utime_total;
+};
 
 struct process_info
 {
-       long VmSize;            // number of pages of virtual memory
-       long VmRSS;             // resident set size from /proc/#/stat
-       long start_time;        // start time of process -- seconds since 1-1-70
-       long stime, utime;      // kernel & user-mode CPU time accumulated by process
-       long cstime, cutime;    // cumulative time of process and reaped children
-       int s_context;
+       long            VmSize;         // number of pages of virtual memory
+       long            VmRSS;          // resident set size from /proc/#/stat
+       uint64_t        start_time;     // start time of process -- milliseconds since 1-1-70
+        uint64_t       stime, utime;   // kernel & user-mode CPU time accumulated by process
+       uint64_t        cstime, cutime; // cumulative time of process and reaped children
+       xid_t           s_context;
 };
 
-char *process_name;
-
 struct ArgInfo {
     enum { tpUNSET, tpCTX, tpPID }     type;
     xid_t              ctx;
@@ -138,11 +133,11 @@ getUptime()
   char         buffer[64];
   char *       errptr;
   size_t       len;
-  time_t       secs;
+  uint64_t     secs;
   uint32_t     msecs=0;
 
     // open the /proc/uptime file
-  fd  = Eopen("/proc/uptime", O_RDONLY, 0);
+  fd  = EopenD("/proc/uptime", O_RDONLY, 0);
   len = Eread(fd, buffer, sizeof buffer);
 
   if (len==sizeof(buffer)) {
@@ -156,102 +151,94 @@ getUptime()
 
   secs = strtol(buffer, &errptr, 10);
   if (*errptr!='.') errptr = buffer;
-  else              msecs = strtol(errptr+1, &errptr, 10);
+  else {
+    unsigned int       mult;
+    switch (strlen(errptr+1)) {
+      case 0   :  mult = 1000; break;
+      case 1   :  mult = 100;  break;
+      case 2   :  mult = 10;   break;
+      case 3   :  mult = 1;    break;
+      default  :  mult = 0;    break;
+    }
+    msecs = strtol(errptr+1, &errptr, 10) * mult;
+  }
 
   if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
     WRITE_MSG(2, "Bad data in /proc/uptime\n");
     exit(1);
   }
 
-  return secs*100 + msecs;
+  return secs*1000 + msecs;
 }
 
-// insert a new record to the list
-static struct ctx_list *
-insert_ctx(int ctx, struct ctx_list *next)
+static int
+cmpData(void const *xid_v, void const *map_v)
 {
-       struct ctx_list *new;
-
-       new = (struct ctx_list *)malloc(sizeof(struct ctx_list));
-       new->ctx = ctx;
-       new->process_count = 0;
-       new->VmSize_total = 0;
-       new->VmRSS_total = 0;
-       new->utime_total = 0;
-       new->stime_total = 0;
-       new->start_time_oldest = 0;
-       new->next = next;
-       new->name[0] = '\0';
-
-       return new;
+  xid_t const * const                  xid = xid_v;
+  struct XidData const * const         map = map_v;
+  int  res = *xid - map->xid;
+
+  return res;
 }
 
-// compute the process info into the list
 static void
-add_ctx(struct ctx_list *list, struct process_info *process)
+registerXid(struct Vector *vec, struct process_info *process)
 {
-       list->process_count ++;
-       list->VmSize_total += process->VmSize;
-       list->VmRSS_total += process->VmRSS;
-       list->utime_total += process->utime + process->cutime;
-       list->stime_total += process->stime + process->cstime;
-
-       if (list->start_time_oldest == 0) // first entry
-               list->start_time_oldest = process->start_time;
-       else
-               if (list->start_time_oldest > process->start_time)
-                       list->start_time_oldest = process->start_time;
+  struct XidData       *res;
+
+  res = Vector_search(vec, &process->s_context, cmpData);
+  if (res==0) {
+    res = Vector_insert(vec, &process->s_context, cmpData);
+    res->xid           = process->s_context;
+    res->process_count = 0;
+    res->VmSize_total  = 0;
+    res->VmRSS_total   = 0;
+    res->utime_total   = 0;
+    res->stime_total   = 0;
+    res->start_time_oldest = process->start_time;
+  }
+
+  ++res->process_count;
+  res->VmSize_total += process->VmSize;
+  res->VmRSS_total  += process->VmRSS;
+  res->utime_total  += process->utime + process->cutime;
+  res->stime_total  += process->stime + process->cstime;
+
+  res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
 }
 
-// increment the count number in the ctx record using ctx number
-static void
-count_ctx(struct ctx_list *list, struct process_info *process)
+static inline uint64_t
+toMsec(uint64_t v)
 {
-       struct ctx_list *prev = list;
-
-       if (process == NULL) return;
-
-       // search
-       while(list != NULL)
-       {
-               // find
-               if (list->ctx == process->s_context)
-               {
-                       add_ctx(list, process);
-                       return;
-               }
-               // insert between
-               if (list->ctx > process->s_context)
-               {
-                       prev->next = insert_ctx(process->s_context, list);
-                       add_ctx(prev->next, process);
-                       return;
-               }
-               // ++
-               prev = list;
-               list = list->next;
-       }
-       // add at the end
-       prev->next = insert_ctx(process->s_context, NULL);
-       add_ctx(prev->next, process);
+  return v*1000llu/hertz;
 }
 
-// free mem
-static void
-free_ctx(struct ctx_list *list)
-{
-       struct ctx_list *prev;
 
-       for(;list != NULL; list = prev)
-       {
-               prev = list->next;              
-               free(list);
-       }
+// shamelessly stolen from procps...
+static unsigned long
+find_elf_note(unsigned long findme){
+  unsigned long *ep = (unsigned long *)environ;
+  while(*ep++);
+  while(*ep){
+    if(ep[0]==findme) return ep[1];
+    ep+=2;
+  }
+  return (unsigned long)(-1);
 }
 
+static void initHertz()        __attribute__((__constructor__));
+
+static void
+initHertz()
+{
+  hertz = find_elf_note(AT_CLKTCK);
+  if (hertz==(unsigned long)(-1))
+    hertz = sysconf(_SC_CLK_TCK);
+}
 
 // open the process's status file to get the ctx number, and other stat
-struct process_info *get_process_info(char *pid)
+struct process_info *
+get_process_info(char *pid)
 {
   int                          fd;
   char                         buffer[1024];
@@ -259,8 +246,23 @@ struct process_info *get_process_info(char *pid)
   size_t                       idx, l=strlen(pid);
   static struct process_info   process;
 
+#if 1
   process.s_context = vc_get_task_xid(atoi(pid));
+#else
+#  warning Compiling in debug-code
+  process.s_context = random()%6;
+#endif
 
+  if (process.s_context==VC_NOCTX) {
+    int                err=errno;
+    WRITE_MSG(2, "vc_get_task_xid(");
+    WRITE_STR(2, pid);
+    WRITE_MSG(2, "): ");
+    WRITE_STR(2, strerror(err));
+    WRITE_MSG(2, "\n");
+
+    return 0;
+  }
   
   memcpy(buffer,     "/proc/", 6); idx  = 6;
   memcpy(buffer+idx, pid,      l); idx += l;
@@ -279,23 +281,22 @@ struct process_info *get_process_info(char *pid)
   for (idx = 0; idx<12 && *p!='\0'; ++p)
     if ((*p)==' ') ++idx;
 
-  process.utime  = strtol(p,   &p, 10);
-  process.stime  = strtol(p+1, &p, 10);
-  process.cutime = strtol(p+1, &p, 10);
-  process.cstime = strtol(p+1, &p, 10);
+  process.utime  = toMsec(strtol(p,   &p, 10));
+  process.stime  = toMsec(strtol(p+1, &p, 10));
+  process.cutime = toMsec(strtol(p+1, &p, 10));
+  process.cstime = toMsec(strtol(p+1, &p, 10));
 
   for (idx = 0; idx<5 && *p!='\0'; ++p)
     if ((*p)==' ') ++idx;
 
-  process.start_time = strtol(p,   &p, 10);
+  process.start_time = toMsec(strtol(p,   &p, 10));
   process.VmSize     = strtol(p+1, &p, 10);
   process.VmRSS      = strtol(p+1, &p, 10);
-       
+
+  //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
   return &process;
 }
 
-
-
 static size_t
 fillUintZero(char *buf, unsigned long val, size_t cnt)
 {
@@ -351,8 +352,8 @@ shortenTime(char *buf, uint64_t t)
 
   unsigned long        hh, mm, ss, ms;
 
-  ms = t % 100;
-  t /= 100;
+  ms = t % 1000;
+  t /= 1000;
 
   ss = t%60;
   t /= 60;
@@ -361,7 +362,18 @@ shortenTime(char *buf, uint64_t t)
   hh = t%60;
   t /= 24;
 
-  if (t>0) {
+  if (t>999*999) {
+    memcpy(ptr, "INVALID", 7);
+    ptr   += 7;
+  }
+  else if (t>999) {
+    ptr   += utilvserver_fmt_ulong(ptr, t/365);
+    *ptr++ = 'y';
+    ptr   += fillUintZero(ptr, t%365, 2);
+    *ptr++ = 'd';
+    ptr   += fillUintZero(ptr, hh, 2);
+  }    
+  else if (t>0) {
     ptr   += utilvserver_fmt_ulong(ptr, t);
     *ptr++ = 'd';
     ptr   += fillUintZero(ptr, hh, 2);
@@ -387,58 +399,79 @@ shortenTime(char *buf, uint64_t t)
   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
 }
 
-void showContexts(struct ctx_list *list)
+static void
+showContexts(struct Vector *vec)
 {
-  uint64_t     uptime = getUptime();
+  uint64_t                     uptime  = getUptime();
+  struct XidData const *       ptr     = Vector_begin_const(vec);
+  struct XidData const * const end_ptr = Vector_end_const(vec);
+  
 
   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
-  while (list!=0) {
-    char       buf[512];
-    char       tmp[32];
+  for (; ptr<end_ptr; ++ptr) {
+    char       buf[sizeof(xid_t)*3 + 512];
+    char       tmp[sizeof(int)*3 + 2];
     size_t     l;
 
     memset(buf, ' ', sizeof(buf));
-    l = utilvserver_fmt_ulong(buf, list->ctx);
-    l = utilvserver_fmt_ulong(tmp, list->process_count);
+    l = utilvserver_fmt_long(buf, ptr->xid);
+    l = utilvserver_fmt_long(tmp, ptr->process_count);
     memcpy(buf+10-l, tmp, l);
 
-    shortenMem (buf+10, list->VmSize_total);
-    shortenMem (buf+17, list->VmRSS_total);
-    shortenTime(buf+24, list->utime_total);
-    shortenTime(buf+34, list->stime_total);
-    shortenTime(buf+44, uptime - list->start_time_oldest);
+    shortenMem (buf+10, ptr->VmSize_total);
+    shortenMem (buf+17, ptr->VmRSS_total);
+    shortenTime(buf+24, ptr->utime_total);
+    shortenTime(buf+34, ptr->stime_total);
+    //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
+    shortenTime(buf+44, uptime - ptr->start_time_oldest);
 
-    switch (list->ctx) {
-      case 0           :  strncpy(buf+55, "root server",       20); break;
-      case 1           :  strncpy(buf+55, "monitoring server", 20); break;
+    switch (ptr->xid) {
+      case 0           :  strcpy(buf+55, "root server"      ); break;
+      case 1           :  strcpy(buf+55, "monitoring server"); break;
       default          : {
-       char *  name     = 0;
-       char *  cfgpath  = 0;
+       char *          name     = 0;
+       char *          cfgpath  = 0;
        vcCfgStyle      cfgstyle = vcCFG_AUTO;
 
-       if ((cfgpath = vc_getVserverByCtx(list->ctx, &cfgstyle, 0))!=0 &&
-           (name    = vc_getVserverName(cfgpath, cfgstyle))!=0)
-         strncpy(buf+55, name, 20);
+       if ((cfgpath = vc_getVserverByCtx(ptr->xid, &cfgstyle, 0))==0 ||
+           (name    = vc_getVserverName(cfgpath, cfgstyle))==0) {
+         name     = strdup("");
+         cfgstyle = vcCFG_NONE;
+       }
+       
+       switch (cfgstyle) {
+         case vcCFG_LEGACY     : {
+           size_t      len = MIN(strlen(name), 18);
+           buf[55]     = '[';
+           memcpy(buf+56,     name, len);
+           memcpy(buf+56+len, "]",  2);
+           break;
+         }
+         default               : {
+           size_t      len = MIN(strlen(name), 20);
+           memcpy(buf+55, name, len);
+           buf[55+len] = '\0';
+           break;
+         }
+       }
+       
        free(name);
        free(cfgpath);
       }
     }
 
-    write(1, buf, 80);
+    write(1, buf, strlen(buf));
     write(1, "\n", 1);
-
-    list = list->next;
   }
 }
 
 int main(int argc, char **argv)
 {
-  DIR *proc_dir;
-  struct dirent *dir_entry;
-  pid_t my_pid;
-
-    // for error msg
-  process_name = argv[0];
+  DIR *                        proc_dir;
+  struct dirent*       dir_entry;
+  pid_t                        my_pid;
+  struct Vector                xid_data;
+  char const *         errptr;
 
   if (argc==2) {
     if (strcmp(argv[1], "--help")   ==0) showHelp(1, argv[0], 0);
@@ -449,19 +482,18 @@ int main(int argc, char **argv)
     return EXIT_FAILURE;
   }
 
+  if (hertz==0x42) initHertz();
+  
+  //printf("hertz=%lu\n", hertz);
     // do not include own stat
   my_pid = getpid();
 
-#if 1
-    // try to switch in context 1
-  Evc_new_s_context(1, 0,0);
-#endif
-       
-    // create the fist...
-  my_ctx_list = insert_ctx(0, NULL);
-    // init with the default name for the context 0
-  strncpy(my_ctx_list->name, "root server", CTX_NAME_MAX_LEN);
+  if (!switchToWatchXid(&errptr)) {
+    perror(errptr);
+    exit(1);
+  }
 
+  Vector_init(&xid_data, sizeof(struct XidData));
 
   Echdir(PROC_DIR_NAME);
   proc_dir = Eopendir(".");
@@ -471,17 +503,20 @@ int main(int argc, char **argv)
     if (!isdigit(*dir_entry->d_name))
       continue;
 
-    if (atoi(dir_entry->d_name) != my_pid)
-      count_ctx(my_ctx_list, get_process_info(dir_entry->d_name));
-               
+    if (atoi(dir_entry->d_name) != my_pid) {
+      struct process_info *    info = get_process_info(dir_entry->d_name);
+      if (info)
+       registerXid(&xid_data, info);
+    }
   }
   closedir(proc_dir);
 
     // output the ctx_list     
-  showContexts(my_ctx_list);
-
-    // free the ctx_list
-  free_ctx(my_ctx_list);
+  showContexts(&xid_data);
 
+#ifndef NDEBUG
+  Vector_free(&xid_data);
+#endif
+  
   return 0;
 }