add option to create identity from private key
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cpustatus.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2008--2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file testbed/gnunet-service-testbed_cpustatus.c
23  * @brief calls to determine current CPU load
24  * @author Tzvetan Horozov
25  * @author Christian Grothoff
26  * @author Igor Wronsky
27  * @author Alex Harper (OS X portion)
28  * @author Sree Harsha Totakura
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet-service-testbed_meminfo.h"
34
35 #if SOLARIS
36 #if HAVE_KSTAT_H
37 #include <kstat.h>
38 #endif
39 #if HAVE_SYS_SYSINFO_H
40 #include <sys/sysinfo.h>
41 #endif
42 #if HAVE_KVM_H
43 #include <kvm.h>
44 #endif
45 #endif
46 #ifdef BSD
47 #if HAVE_KVM_H
48 #include <kvm.h>
49 #endif
50 #endif
51
52 #ifdef OSX
53 #include <mach/mach.h>
54
55 static processor_cpu_load_info_t prev_cpu_load;
56 #endif
57
58 #define DEBUG_STATUSCALLS GNUNET_NO
59
60 #ifdef __linux__
61 static FILE *proc_stat;
62 #endif
63
64 /**
65  * Current CPU load, as percentage of CPU cycles not idle or
66  * blocked on IO.
67  */
68 static int currentCPULoad;
69
70 static double agedCPULoad = -1;
71
72 /**
73  * Current IO load, as percentage of CPU cycles blocked on IO.
74  */
75 static int currentIOLoad;
76
77 static double agedIOLoad = -1;
78
79
80 /**
81  * hanlde to the file to write the load statistics to
82  */
83 struct GNUNET_BIO_WriteHandle *bw;
84
85 struct GNUNET_SCHEDULER_Task *sample_load_task_id;
86
87
88 #ifdef OSX
89 static int
90 initMachCpuStats ()
91 {
92   unsigned int cpu_count;
93   processor_cpu_load_info_t cpu_load;
94   mach_msg_type_number_t cpu_msg_count;
95   kern_return_t kret;
96   int i, j;
97
98   kret = host_processor_info (mach_host_self (),
99                               PROCESSOR_CPU_LOAD_INFO,
100                               &cpu_count,
101                               (processor_info_array_t *) &cpu_load,
102                               &cpu_msg_count);
103   if (kret != KERN_SUCCESS)
104   {
105     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
106     return GNUNET_SYSERR;
107   }
108   prev_cpu_load = GNUNET_malloc (cpu_count * sizeof(*prev_cpu_load));
109   for (i = 0; i < cpu_count; i++)
110   {
111     for (j = 0; j < CPU_STATE_MAX; j++)
112     {
113       prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
114     }
115   }
116   vm_deallocate (mach_task_self (),
117                  (vm_address_t) cpu_load,
118                  (vm_size_t) (cpu_msg_count * sizeof(*cpu_load)));
119   return GNUNET_OK;
120 }
121
122
123 #endif
124
125 /**
126  * Update the currentCPU and currentIO load (and on Linux, memory) values.
127  *
128  * Before its first invocation the method initStatusCalls() must be called.
129  * If there is an error the method returns -1.
130  */
131 static int
132 updateUsage ()
133 {
134   currentIOLoad = -1;
135   currentCPULoad = -1;
136 #ifdef __linux__
137   /* under linux, first try %idle/usage using /proc/stat;
138      if that does not work, disable /proc/stat for the future
139      by closing the file and use the next-best method. */
140   if (proc_stat != NULL)
141   {
142     static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
143     static int have_last_cpu = GNUNET_NO;
144     int ret;
145     char line[256];
146     unsigned long long user_read, system_read, nice_read, idle_read,
147                        iowait_read;
148     unsigned long long user, system, nice, idle, iowait;
149     unsigned long long usage_time = 0, total_time = 1;
150
151     /* Get the first line with the data */
152     rewind (proc_stat);
153     fflush (proc_stat);
154     if (NULL == fgets (line, 256, proc_stat))
155     {
156       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
157                                 "fgets", "/proc/stat");
158       proc_stat = NULL;         /* don't try again */
159     }
160     else
161     {
162       iowait_read = 0;
163       ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
164                     &user_read,
165                     &system_read, &nice_read, &idle_read, &iowait_read);
166       if (ret < 4)
167       {
168         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
169                                   "fgets-sscanf", "/proc/stat");
170         fclose (proc_stat);
171         proc_stat = NULL;       /* don't try again */
172         have_last_cpu = GNUNET_NO;
173       }
174       else
175       {
176         /* Store the current usage */
177         user = user_read - last_cpu_results[0];
178         system = system_read - last_cpu_results[1];
179         nice = nice_read - last_cpu_results[2];
180         idle = idle_read - last_cpu_results[3];
181         iowait = iowait_read - last_cpu_results[4];
182         /* Calculate the % usage */
183         usage_time = user + system + nice;
184         total_time = usage_time + idle + iowait;
185         if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
186         {
187           currentCPULoad = (int) (100L * usage_time / total_time);
188           if (ret > 4)
189             currentIOLoad = (int) (100L * iowait / total_time);
190           else
191             currentIOLoad = -1;         /* 2.4 kernel */
192         }
193         /* Store the values for the next calculation */
194         last_cpu_results[0] = user_read;
195         last_cpu_results[1] = system_read;
196         last_cpu_results[2] = nice_read;
197         last_cpu_results[3] = idle_read;
198         last_cpu_results[4] = iowait_read;
199         have_last_cpu = GNUNET_YES;
200         return GNUNET_OK;
201       }
202     }
203   }
204 #endif
205
206 #ifdef OSX
207   {
208     unsigned int cpu_count;
209     processor_cpu_load_info_t cpu_load;
210     mach_msg_type_number_t cpu_msg_count;
211     unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
212     unsigned long long t_idle_all, t_total_all;
213     kern_return_t kret;
214     int i, j;
215
216     t_idle_all = t_total_all = 0;
217     kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
218                                 &cpu_count,
219                                 (processor_info_array_t *) &cpu_load,
220                                 &cpu_msg_count);
221     if (kret == KERN_SUCCESS)
222     {
223       for (i = 0; i < cpu_count; i++)
224       {
225         if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
226             prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
227         {
228           t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
229                   - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
230         }
231         else
232         {
233           t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
234                   + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
235                      + 1);
236         }
237
238         if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
239             prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
240         {
241           t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER]
242                    - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
243         }
244         else
245         {
246           t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER]
247                    + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER]
248                       + 1);
249         }
250
251         if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
252             prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
253         {
254           t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE]
255                    - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
256         }
257         else
258         {
259           t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE]
260                    + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE]
261                       + 1);
262         }
263
264         if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
265             prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
266         {
267           t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
268                    - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
269         }
270         else
271         {
272           t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
273                    + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
274                       + 1);
275         }
276         t_total = t_sys + t_user + t_nice + t_idle;
277         t_idle_all += t_idle;
278         t_total_all += t_total;
279       }
280       for (i = 0; i < cpu_count; i++)
281       {
282         for (j = 0; j < CPU_STATE_MAX; j++)
283         {
284           prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
285         }
286       }
287       if (t_total_all > 0)
288         currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
289       else
290         currentCPULoad = -1;
291       vm_deallocate (mach_task_self (),
292                      (vm_address_t) cpu_load,
293                      (vm_size_t) (cpu_msg_count * sizeof(*cpu_load)));
294       currentIOLoad = -1;       /* FIXME-OSX! */
295       return GNUNET_OK;
296     }
297     else
298     {
299       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
300       return GNUNET_SYSERR;
301     }
302   }
303 #endif
304   /* try kstat (Solaris only) */
305 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
306   {
307     static long long last_idlecount;
308     static long long last_totalcount;
309     static int kstat_once;      /* if open fails, don't keep
310                                    trying */
311     kstat_ctl_t *kc;
312     kstat_t *khelper;
313     long long idlecount;
314     long long totalcount;
315     long long deltaidle;
316     long long deltatotal;
317
318     if (kstat_once == 1)
319       goto ABORT_KSTAT;
320     kc = kstat_open ();
321     if (kc == NULL)
322     {
323       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
324       goto ABORT_KSTAT;
325     }
326
327     idlecount = 0;
328     totalcount = 0;
329     for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
330     {
331       cpu_stat_t stats;
332
333       if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
334         continue;
335       if (khelper->ks_data_size > sizeof(cpu_stat_t))
336         continue;               /* better save then sorry! */
337       if (-1 != kstat_read (kc, khelper, &stats))
338       {
339         idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
340         totalcount
341           += stats.cpu_sysinfo.cpu[CPU_IDLE]
342              + stats.cpu_sysinfo.cpu[CPU_USER]
343              + stats.cpu_sysinfo.cpu[CPU_KERNEL]
344              + stats.cpu_sysinfo.cpu[CPU_WAIT];
345       }
346     }
347     if (0 != kstat_close (kc))
348       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
349     if ((idlecount == 0) && (totalcount == 0))
350       goto ABORT_KSTAT;         /* no stats found => abort */
351     deltaidle = idlecount - last_idlecount;
352     deltatotal = totalcount - last_totalcount;
353     if ((deltatotal > 0) && (last_totalcount > 0))
354     {
355       currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
356       if (currentCPULoad > 100)
357         currentCPULoad = 100;   /* odd */
358       if (currentCPULoad < 0)
359         currentCPULoad = 0;     /* odd */
360       currentCPULoad = 100 - currentCPULoad;    /* computed idle-load before! */
361     }
362     else
363       currentCPULoad = -1;
364     currentIOLoad = -1;         /* FIXME-SOLARIS! */
365     last_idlecount = idlecount;
366     last_totalcount = totalcount;
367     return GNUNET_OK;
368 ABORT_KSTAT:
369     kstat_once = 1;             /* failed, don't try again */
370     return GNUNET_SYSERR;
371   }
372 #endif
373
374   /* insert methods better than getloadavg for
375      other platforms HERE! */
376
377   /* ok, maybe we have getloadavg on this platform */
378 #if HAVE_GETLOADAVG
379   {
380     static int warnOnce = 0;
381     double loadavg;
382     if (1 != getloadavg (&loadavg, 1))
383     {
384       /* only warn once, if there is a problem with
385          getloadavg, we're going to hit it frequently... */
386       if (warnOnce == 0)
387       {
388         warnOnce = 1;
389         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getloadavg");
390       }
391       return GNUNET_SYSERR;
392     }
393     else
394     {
395       /* success with getloadavg */
396       currentCPULoad = (int) (100 * loadavg);
397       currentIOLoad = -1;       /* FIXME */
398       return GNUNET_OK;
399     }
400   }
401 #endif
402
403   /* loadaverage not defined and no platform
404      specific alternative defined
405      => default: error
406    */
407   return GNUNET_SYSERR;
408 }
409
410
411 /**
412  * Update load values (if enough time has expired),
413  * including computation of averages.  Code assumes
414  * that lock has already been obtained.
415  */
416 static void
417 updateAgedLoad ()
418 {
419   static struct GNUNET_TIME_Absolute lastCall;
420   struct GNUNET_TIME_Relative age;
421
422   age = GNUNET_TIME_absolute_get_duration (lastCall);
423   if ((agedCPULoad == -1)
424       || (age.rel_value_us > 500000))
425   {
426     /* use smoothing, but do NOT update lastRet at frequencies higher
427        than 500ms; this makes the smoothing (mostly) independent from
428        the frequency at which getCPULoad is called (and we don't spend
429        more time measuring CPU than actually computing something). */
430     lastCall = GNUNET_TIME_absolute_get ();
431     updateUsage ();
432     if (currentCPULoad == -1)
433     {
434       agedCPULoad = -1;
435     }
436     else
437     {
438       if (agedCPULoad == -1)
439       {
440         agedCPULoad = currentCPULoad;
441       }
442       else
443       {
444         /* for CPU, we don't do the 'fast increase' since CPU is much
445            more jitterish to begin with */
446         agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
447       }
448     }
449     if (currentIOLoad == -1)
450     {
451       agedIOLoad = -1;
452     }
453     else
454     {
455       if (agedIOLoad == -1)
456       {
457         agedIOLoad = currentIOLoad;
458       }
459       else
460       {
461         /* for IO, we don't do the 'fast increase' since IO is much
462            more jitterish to begin with */
463         agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
464       }
465     }
466   }
467 }
468
469
470 /**
471  * Get the load of the CPU relative to what is allowed.
472  * @return the CPU load as a percentage of allowed
473  *        (100 is equivalent to full load)
474  */
475 static int
476 cpu_get_load ()
477 {
478   updateAgedLoad ();
479   return (int) agedCPULoad;
480 }
481
482
483 /**
484  * Get the load of the CPU relative to what is allowed.
485  * @return the CPU load as a percentage of allowed
486  *        (100 is equivalent to full load)
487  */
488 static int
489 disk_get_load ()
490 {
491   updateAgedLoad ();
492   return (int) agedIOLoad;
493 }
494
495
496 /**
497  * Get the percentage of memory used
498  *
499  * @return the percentage of memory used
500  */
501 static unsigned int
502 mem_get_usage ()
503 {
504   double percentage;
505
506   meminfo ();
507   percentage = (((double) kb_main_used) / ((double) kb_main_total) * 100.0);
508   return (unsigned int) percentage;
509 }
510
511
512 #ifdef __linux__
513 #include <dirent.h>
514 /**
515  * Returns the number of processes
516  *
517  * @return the number of processes
518  */
519 static unsigned int
520 get_nproc ()
521 {
522   DIR *dir;
523   struct dirent *ent;
524   unsigned int nproc;
525
526   dir = opendir ("/proc");
527   if (NULL == dir)
528     return 0;
529   nproc = 0;
530   while (NULL != (ent = readdir (dir)))
531   {
532     if ((*ent->d_name > '0') && (*ent->d_name <= '9'))
533       nproc++;
534   }
535   closedir (dir);
536   return nproc;
537 }
538
539
540 #endif
541
542
543 static void
544 sample_load_task (void *cls)
545 {
546   struct GNUNET_TIME_Absolute now;
547   char *str;
548   int nbs;
549   int ld_cpu;
550   int ld_disk;
551   unsigned int mem_usage;
552   unsigned int nproc;
553
554   sample_load_task_id = NULL;
555   ld_cpu = cpu_get_load ();
556   ld_disk = disk_get_load ();
557   if ((-1 == ld_cpu) || (-1 == ld_disk))
558     goto reschedule;
559   mem_usage = mem_get_usage ();
560 #ifdef __linux__
561   nproc = get_nproc ();
562 #else
563   nproc = 0;
564 #endif
565   now = GNUNET_TIME_absolute_get ();
566   nbs = GNUNET_asprintf (&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL
567                          / 1000LL,
568                          ld_cpu, ld_disk, mem_usage, nproc);
569   if (0 < nbs)
570   {
571     GNUNET_BIO_write (bw, "sample load task", str, nbs);
572   }
573   else
574     GNUNET_break (0);
575   GNUNET_free (str);
576
577 reschedule:
578   sample_load_task_id =
579     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
580                                   &sample_load_task, NULL);
581 }
582
583
584 /**
585  * Initialize logging CPU and IO statisticfs.  Checks the configuration for
586  * "STATS_DIR" and logs to a file in that directory.  The file is name is
587  * generated from the hostname and the process's PID.
588  */
589 void
590 GST_stats_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
591 {
592   char *hostname;
593   char *stats_dir;
594   char *fn;
595   size_t len;
596
597   if (GNUNET_OK !=
598       GNUNET_CONFIGURATION_get_value_filename (cfg, "testbed",
599                                                "STATS_DIR", &stats_dir))
600     return;
601   len = GNUNET_OS_get_hostname_max_length ();
602   hostname = GNUNET_malloc (len);
603   if (0 != gethostname (hostname, len))
604   {
605     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
606     GNUNET_free (stats_dir);
607     GNUNET_free (hostname);
608     return;
609   }
610   fn = NULL;
611   (void) GNUNET_asprintf (&fn, "%s/%.*s-%jd.dat", stats_dir, len,
612                           hostname, (intmax_t) getpid ());
613   GNUNET_free (stats_dir);
614   GNUNET_free (hostname);
615   if (NULL == (bw = GNUNET_BIO_write_open_file (fn)))
616   {
617     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
618                 _ ("Cannot open %s for writing load statistics.  "
619                    "Not logging load statistics\n"), fn);
620     GNUNET_free (fn);
621     return;
622   }
623   GNUNET_free (fn);
624   sample_load_task_id = GNUNET_SCHEDULER_add_now (&sample_load_task, NULL);
625 #ifdef __linux__
626   proc_stat = fopen ("/proc/stat", "r");
627   if (NULL == proc_stat)
628     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
629                               "fopen", "/proc/stat");
630 #elif OSX
631   initMachCpuStats ();
632 #endif
633   updateUsage ();                /* initialize */
634 }
635
636
637 /**
638  * Shutdown the status calls module.
639  */
640 void
641 GST_stats_destroy ()
642 {
643   if (NULL == bw)
644     return;
645 #ifdef __linux__
646   if (proc_stat != NULL)
647   {
648     fclose (proc_stat);
649     proc_stat = NULL;
650   }
651 #elif OSX
652   GNUNET_free_non_null (prev_cpu_load);
653 #endif
654   if (NULL != sample_load_task_id)
655   {
656     GNUNET_SCHEDULER_cancel (sample_load_task_id);
657     sample_load_task_id = NULL;
658   }
659   GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bw, NULL));
660   bw = NULL;
661 }
662
663
664 /* end of cpustatus.c */