-more datacache integration work
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cpustatus.c
index 7692a13898e214e9b267f3c11285657b4790e728..44d1c7624a158843e00a85d629a81a7fef0f13b1 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
-     (C) 2001, 2002, 2003, 2005, 2006, 2013 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2008--2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -30,6 +30,7 @@
 
 #include "platform.h"
 #include "gnunet_util_lib.h"
+#include "gnunet-service-testbed_meminfo.h"
 
 #if SOLARIS
 #if HAVE_KSTAT_H
@@ -53,6 +54,9 @@
 
 static processor_cpu_load_info_t prev_cpu_load;
 #endif
+#ifdef WINDOWS
+#include <winternl.h>
+#endif
 
 #define DEBUG_STATUSCALLS GNUNET_NO
 
@@ -75,6 +79,15 @@ static int currentIOLoad;
 
 static double agedIOLoad = -1;
 
+
+/**
+ * hanlde to the file to write the load statistics to
+ */
+struct GNUNET_BIO_WriteHandle *bw;
+
+struct GNUNET_SCHEDULER_Task * sample_load_task_id;
+
+
 #ifdef OSX
 static int
 initMachCpuStats ()
@@ -92,9 +105,7 @@ initMachCpuStats ()
                               &cpu_msg_count);
   if (kret != KERN_SUCCESS)
     {
-      GNUNET_GE_LOG (NULL,
-                     GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_ADMIN |
-                     GNUNET_GE_BULK, "host_processor_info failed.");
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
       return GNUNET_SYSERR;
     }
   prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
@@ -113,7 +124,7 @@ initMachCpuStats ()
 #endif
 
 /**
- * Update the currentCPU and currentIO load values.
+ * Update the currentCPU and currentIO load (and on Linux, memory) values.
  *
  * Before its first invocation the method initStatusCalls() must be called.
  * If there is an error the method returns -1.
@@ -286,9 +297,7 @@ updateUsage ()
       }
     else
       {
-        GNUNET_GE_LOG (NULL,
-                       GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_ADMIN |
-                       GNUNET_GE_BULK, "host_processor_info failed.");
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
         return GNUNET_SYSERR;
       }
   }
@@ -312,10 +321,7 @@ updateUsage ()
     kc = kstat_open ();
     if (kc == NULL)
       {
-        GNUNET_GE_LOG_STRERROR (NULL,
-                                GNUNET_GE_ERROR | GNUNET_GE_USER |
-                                GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                                "kstat_open");
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
         goto ABORT_KSTAT;
       }
 
@@ -340,9 +346,7 @@ updateUsage ()
           }
       }
     if (0 != kstat_close (kc))
-      GNUNET_GE_LOG_STRERROR (NULL,
-                              GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
-                              GNUNET_GE_USER | GNUNET_GE_BULK, "kstat_close");
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
     if ((idlecount == 0) && (totalcount == 0))
       goto ABORT_KSTAT;         /* no stats found => abort */
     deltaidle = idlecount - last_idlecount;
@@ -446,10 +450,8 @@ updateUsage ()
           if (once == 0)
             {
               once = 1;
-              GNUNET_GE_LOG (NULL,
-                             GNUNET_GE_ERROR | GNUNET_GE_USER |
-                             GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                             _("Cannot query the CPU usage (Windows NT).\n"));
+              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                          "Cannot query the CPU usage (Windows NT).\n");
             }
           return GNUNET_SYSERR;
         }
@@ -469,10 +471,8 @@ updateUsage ()
           if (once == 0)
             {
               once = 1;
-              GNUNET_GE_LOG (NULL,
-                             GNUNET_GE_USER | GNUNET_GE_ADMIN |
-                             GNUNET_GE_ERROR | GNUNET_GE_BULK,
-                             _("Cannot query the CPU usage (Win 9x)\n"));
+              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                          "Cannot query the CPU usage (Win 9x)\n");
             }
         }
 
@@ -530,7 +530,7 @@ updateAgedLoad ()
 
   age = GNUNET_TIME_absolute_get_duration (lastCall);
   if ( (agedCPULoad == -1)
-       || (age.rel_value > 500) )
+       || (age.rel_value_us > 500000) )
     {
       /* use smoothing, but do NOT update lastRet at frequencies higher
          than 500ms; this makes the smoothing (mostly) independent from
@@ -580,8 +580,8 @@ updateAgedLoad ()
  * @return the CPU load as a percentage of allowed
  *        (100 is equivalent to full load)
  */
-int
-GST_cpu_get_load ()
+static int
+cpu_get_load ()
 {
   updateAgedLoad ();
   return (int) agedCPULoad;
@@ -593,21 +593,147 @@ GST_cpu_get_load ()
  * @return the CPU load as a percentage of allowed
  *        (100 is equivalent to full load)
  */
-int
-GST_disk_get_load ()
+static int
+disk_get_load ()
 {
   updateAgedLoad ();
   return (int) agedIOLoad;
 }
 
 /**
- * The following method is called in order to initialize the status calls
- * routines.  After that it is safe to call each of the status calls separately
- * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit).
+ * Get the percentage of memory used
+ *
+ * @return the percentage of memory used
+ */
+static unsigned int
+mem_get_usage ()
+{
+  double percentage;
+
+  meminfo ();
+  percentage = ( ((double) kb_main_used) / ((double) kb_main_total) * 100.0 );
+  return (unsigned int) percentage;
+}
+
+
+#ifdef LINUX
+#include <dirent.h>
+/**
+ * Returns the number of processes
+ *
+ * @return the number of processes
+ */
+static unsigned int
+get_nproc ()
+{
+  DIR *dir;
+  struct dirent *ent;
+  unsigned int nproc;
+
+  dir = opendir ("/proc");
+  if (NULL == dir)
+    return 0;
+  nproc = 0;
+  while (NULL != (ent = readdir (dir)))
+  {
+    if((*ent->d_name > '0') && (*ent->d_name <= '9'))
+      nproc++;
+  }
+  closedir (dir);
+  return nproc;
+}
+#endif
+
+
+static void
+sample_load_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_TIME_Absolute now;
+  char *str;
+  int nbs;
+  int ld_cpu;
+  int ld_disk;
+  unsigned int mem_usage;
+  unsigned int nproc;
+
+  sample_load_task_id = NULL;
+  if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
+    return;
+  ld_cpu = cpu_get_load ();
+  ld_disk = disk_get_load ();
+  if ( (-1 == ld_cpu) || (-1 == ld_disk) )
+    goto reschedule;
+  mem_usage = mem_get_usage ();
+#ifdef LINUX
+  nproc = get_nproc ();
+#else
+  nproc = 0;
+#endif
+  now = GNUNET_TIME_absolute_get ();
+  nbs = GNUNET_asprintf (&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL / 1000LL,
+                         ld_cpu, ld_disk, mem_usage, nproc);
+  if (0 < nbs)
+  {
+    GNUNET_BIO_write (bw, str, nbs);
+  }
+  else
+    GNUNET_break (0);
+  GNUNET_free (str);
+
+ reschedule:
+  sample_load_task_id =
+      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+                                    &sample_load_task, NULL);
+}
+
+
+/**
+ * Initialize logging CPU and IO statisticfs.  Checks the configuration for
+ * "STATS_DIR" and logs to a file in that directory.  The file is name is
+ * generated from the hostname and the process's PID.
  */
 void
-GST_stats_init ()
+GST_stats_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
+  char *hostname;
+  char *stats_dir;
+  char *fn;
+  size_t len;
+
+#if MINGW
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Load statistics logging now available for windows\n");
+  return;                       /* No logging on windows for now :( */
+#endif
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg, "testbed",
+                                               "STATS_DIR", &stats_dir))
+    return;
+  len = GNUNET_OS_get_hostname_max_length ();
+  hostname = GNUNET_malloc (len);
+  if (0 != gethostname  (hostname, len))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
+    GNUNET_free (stats_dir);
+    GNUNET_free (hostname);
+    return;
+  }
+  fn = NULL;
+  (void) GNUNET_asprintf (&fn, "%s/%.*s-%jd.dat", stats_dir, len,
+                          hostname, (intmax_t) getpid());
+  GNUNET_free (stats_dir);
+  GNUNET_free (hostname);
+  if (NULL == (bw = GNUNET_BIO_write_open (fn)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Cannot open %s for writing load statistics.  "
+                  "Not logging load statistics\n"), fn);
+    GNUNET_free (fn);
+    return;
+  }
+  GNUNET_free (fn);
+  sample_load_task_id = GNUNET_SCHEDULER_add_now (&sample_load_task, NULL);
 #ifdef LINUX
   proc_stat = fopen ("/proc/stat", "r");
   if (NULL == proc_stat)
@@ -615,18 +741,23 @@ GST_stats_init ()
                               "fopen", "/proc/stat");
 #elif OSX
   initMachCpuStats ();
-#elif MINGW
-  InitWinEnv (NULL);
 #endif
   updateUsage ();               /* initialize */
+
 }
 
+
 /**
  * Shutdown the status calls module.
  */
 void
 GST_stats_destroy ()
 {
+#if MINGW
+  return;
+#endif
+  if (NULL == bw)
+    return;
 #ifdef LINUX
   if (proc_stat != NULL)
     {
@@ -635,11 +766,14 @@ GST_stats_destroy ()
     }
 #elif OSX
   GNUNET_free_non_null (prev_cpu_load);
-#elif MINGW
-  ShutdownWinEnv ();
 #endif
+  if (NULL != sample_load_task_id)
+  {
+    GNUNET_SCHEDULER_cancel (sample_load_task_id);
+    sample_load_task_id = NULL;
+  }
+  GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bw));
+  bw = NULL;
 }
 
-char *GST_stats_dir;
-
 /* end of cpustatus.c */