GNUnet  0.11.x
gnunet-service-testbed_cpustatus.c
Go to the documentation of this file.
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 
31 #include "platform.h"
32 #include "gnunet_util_lib.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 #if SOMEBSD
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 
68 static int currentCPULoad;
69 
70 static double agedCPULoad = -1;
71 
75 static int currentIOLoad;
76 
77 static double agedIOLoad = -1;
78 
79 
84 
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 
131 static int
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  {
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  {
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  {
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))
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;
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 
416 static void
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  {
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  {
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 
475 static int
477 {
478  updateAgedLoad ();
479  return (int) agedCPULoad;
480 }
481 
482 
488 static int
490 {
491  updateAgedLoad ();
492  return (int) agedIOLoad;
493 }
494 
495 
501 static unsigned int
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>
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, str, nbs);
572  }
573  else
574  GNUNET_break (0);
575  GNUNET_free (str);
576 
577 reschedule:
578  sample_load_task_id =
580  &sample_load_task, NULL);
581 }
582 
583 
589 void
591 {
592  char *hostname;
593  char *stats_dir;
594  char *fn;
595  size_t len;
596 
597  if (GNUNET_OK !=
599  "STATS_DIR", &stats_dir))
600  return;
602  hostname = GNUNET_malloc (len);
603  if (0 != gethostname (hostname, len))
604  {
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 (fn)))
616  {
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)
629  "fopen", "/proc/stat");
630 #elif OSX
631  initMachCpuStats ();
632 #endif
633  updateUsage (); /* initialize */
634 }
635 
636 
640 void
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  }
660  bw = NULL;
661 }
662 
663 
664 /* end of cpustatus.c */
static int cpu_get_load()
Get the load of the CPU relative to what is allowed.
static char * dir
Set to the directory where runtime files are stored.
Definition: gnunet-arm.c:89
static void updateAgedLoad()
Update load values (if enough time has expired), including computation of averages.
static void sample_load_task(void *cls)
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
struct GNUNET_BIO_WriteHandle * bw
hanlde to the file to write the load statistics to
#define GNUNET_TIME_UNIT_SECONDS
One second.
unsigned long kb_main_used
static double agedIOLoad
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
void GST_stats_destroy()
Shutdown the status calls module.
#define GNUNET_NO
Definition: gnunet_common.h:78
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
#define GNUNET_free_non_null(ptr)
Free the memory pointed to by ptr if ptr is not NULL.
uint64_t abs_value_us
The actual value.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
int GNUNET_asprintf(char **buf, const char *format,...)
Like asprintf, just portable.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1253
static char * line
Desired phone line (string to be converted to a hash).
unsigned long kb_main_total
#define GNUNET_OS_get_hostname_max_length()
Get maximum string length returned by gethostname()
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
static char * fn
Filename of the unique file.
static int currentIOLoad
Current IO load, as percentage of CPU cycles blocked on IO.
struct GNUNET_STATISTICS_Handle * stats
Handle to the statistics service.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1280
Handle for buffered writing.
Definition: bio.c:379
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:118
void meminfo(void)
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
static int currentCPULoad
Current CPU load, as percentage of CPU cycles not idle or blocked on IO.
int GNUNET_BIO_write_close(struct GNUNET_BIO_WriteHandle *h)
Close an open file for writing.
Definition: bio.c:438
struct GNUNET_SCHEDULER_Task * sample_load_task_id
static char * hostname
Our hostname; we give this to all the peers we start.
configuration data
Definition: configuration.c:85
struct GNUNET_BIO_WriteHandle * GNUNET_BIO_write_open(const char *fn)
Open a file for writing.
Definition: bio.c:410
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration(struct GNUNET_TIME_Absolute whence)
Get the duration of an operation as the difference of the current time and the given start time "henc...
Definition: time.c:375
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:134
int GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
Time for absolute times used by GNUnet, in microseconds.
#define GNUNET_YES
Definition: gnunet_common.h:77
int GNUNET_BIO_write(struct GNUNET_BIO_WriteHandle *h, const void *buffer, size_t n)
Write a buffer to a file.
Definition: bio.c:483
static unsigned int mem_get_usage()
Get the percentage of memory used.
void GST_stats_init(const struct GNUNET_CONFIGURATION_Handle *cfg)
Initialize logging CPU and IO statisticfs.
static int updateUsage()
Update the currentCPU and currentIO load (and on Linux, memory) values.
static double agedCPULoad
static int disk_get_load()
Get the load of the CPU relative to what is allowed.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.
Time for relative time used by GNUnet, in microseconds.
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:966