GNUnet  0.10.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 #endif
122 
129 static int
131 {
132  currentIOLoad = -1;
133  currentCPULoad = -1;
134 #ifdef LINUX
135  /* under linux, first try %idle/usage using /proc/stat;
136  if that does not work, disable /proc/stat for the future
137  by closing the file and use the next-best method. */
138  if (proc_stat != NULL)
139  {
140  static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
141  static int have_last_cpu = GNUNET_NO;
142  int ret;
143  char line[256];
144  unsigned long long user_read, system_read, nice_read, idle_read,
145  iowait_read;
146  unsigned long long user, system, nice, idle, iowait;
147  unsigned long long usage_time = 0, total_time = 1;
148 
149  /* Get the first line with the data */
150  rewind(proc_stat);
151  fflush(proc_stat);
152  if (NULL == fgets(line, 256, proc_stat))
153  {
155  "fgets", "/proc/stat");
156  proc_stat = NULL; /* don't try again */
157  }
158  else
159  {
160  iowait_read = 0;
161  ret = sscanf(line, "%*s %llu %llu %llu %llu %llu",
162  &user_read,
163  &system_read, &nice_read, &idle_read, &iowait_read);
164  if (ret < 4)
165  {
167  "fgets-sscanf", "/proc/stat");
168  fclose(proc_stat);
169  proc_stat = NULL; /* don't try again */
170  have_last_cpu = GNUNET_NO;
171  }
172  else
173  {
174  /* Store the current usage */
175  user = user_read - last_cpu_results[0];
176  system = system_read - last_cpu_results[1];
177  nice = nice_read - last_cpu_results[2];
178  idle = idle_read - last_cpu_results[3];
179  iowait = iowait_read - last_cpu_results[4];
180  /* Calculate the % usage */
181  usage_time = user + system + nice;
182  total_time = usage_time + idle + iowait;
183  if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
184  {
185  currentCPULoad = (int)(100L * usage_time / total_time);
186  if (ret > 4)
187  currentIOLoad = (int)(100L * iowait / total_time);
188  else
189  currentIOLoad = -1; /* 2.4 kernel */
190  }
191  /* Store the values for the next calculation */
192  last_cpu_results[0] = user_read;
193  last_cpu_results[1] = system_read;
194  last_cpu_results[2] = nice_read;
195  last_cpu_results[3] = idle_read;
196  last_cpu_results[4] = iowait_read;
197  have_last_cpu = GNUNET_YES;
198  return GNUNET_OK;
199  }
200  }
201  }
202 #endif
203 
204 #ifdef OSX
205  {
206  unsigned int cpu_count;
207  processor_cpu_load_info_t cpu_load;
208  mach_msg_type_number_t cpu_msg_count;
209  unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
210  unsigned long long t_idle_all, t_total_all;
211  kern_return_t kret;
212  int i, j;
213 
214  t_idle_all = t_total_all = 0;
215  kret = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO,
216  &cpu_count,
217  (processor_info_array_t *)&cpu_load,
218  &cpu_msg_count);
219  if (kret == KERN_SUCCESS)
220  {
221  for (i = 0; i < cpu_count; i++)
222  {
223  if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
224  prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
225  {
226  t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
227  prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
228  }
229  else
230  {
231  t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
232  (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
233  1);
234  }
235 
236  if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
237  prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
238  {
239  t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
240  prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
241  }
242  else
243  {
244  t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
245  (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
246  1);
247  }
248 
249  if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
250  prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
251  {
252  t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
253  prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
254  }
255  else
256  {
257  t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
258  (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
259  1);
260  }
261 
262  if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
263  prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
264  {
265  t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
266  prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
267  }
268  else
269  {
270  t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
271  (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
272  1);
273  }
274  t_total = t_sys + t_user + t_nice + t_idle;
275  t_idle_all += t_idle;
276  t_total_all += t_total;
277  }
278  for (i = 0; i < cpu_count; i++)
279  {
280  for (j = 0; j < CPU_STATE_MAX; j++)
281  {
282  prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
283  }
284  }
285  if (t_total_all > 0)
286  currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
287  else
288  currentCPULoad = -1;
289  vm_deallocate(mach_task_self(),
290  (vm_address_t)cpu_load,
291  (vm_size_t)(cpu_msg_count * sizeof(*cpu_load)));
292  currentIOLoad = -1; /* FIXME-OSX! */
293  return GNUNET_OK;
294  }
295  else
296  {
297  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
298  return GNUNET_SYSERR;
299  }
300  }
301 #endif
302  /* try kstat (Solaris only) */
303 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
304  {
305  static long long last_idlecount;
306  static long long last_totalcount;
307  static int kstat_once; /* if open fails, don't keep
308  trying */
309  kstat_ctl_t *kc;
310  kstat_t *khelper;
311  long long idlecount;
312  long long totalcount;
313  long long deltaidle;
314  long long deltatotal;
315 
316  if (kstat_once == 1)
317  goto ABORT_KSTAT;
318  kc = kstat_open();
319  if (kc == NULL)
320  {
322  goto ABORT_KSTAT;
323  }
324 
325  idlecount = 0;
326  totalcount = 0;
327  for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
328  {
329  cpu_stat_t stats;
330 
331  if (0 != strncmp(khelper->ks_name, "cpu_stat", strlen("cpu_stat")))
332  continue;
333  if (khelper->ks_data_size > sizeof(cpu_stat_t))
334  continue; /* better save then sorry! */
335  if (-1 != kstat_read(kc, khelper, &stats))
336  {
337  idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
338  totalcount
339  += stats.cpu_sysinfo.cpu[CPU_IDLE] +
340  stats.cpu_sysinfo.cpu[CPU_USER] +
341  stats.cpu_sysinfo.cpu[CPU_KERNEL] +
342  stats.cpu_sysinfo.cpu[CPU_WAIT];
343  }
344  }
345  if (0 != kstat_close(kc))
347  if ((idlecount == 0) && (totalcount == 0))
348  goto ABORT_KSTAT; /* no stats found => abort */
349  deltaidle = idlecount - last_idlecount;
350  deltatotal = totalcount - last_totalcount;
351  if ((deltatotal > 0) && (last_totalcount > 0))
352  {
353  currentCPULoad = (unsigned int)(100.0 * deltaidle / deltatotal);
354  if (currentCPULoad > 100)
355  currentCPULoad = 100; /* odd */
356  if (currentCPULoad < 0)
357  currentCPULoad = 0; /* odd */
358  currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
359  }
360  else
361  currentCPULoad = -1;
362  currentIOLoad = -1; /* FIXME-SOLARIS! */
363  last_idlecount = idlecount;
364  last_totalcount = totalcount;
365  return GNUNET_OK;
366 ABORT_KSTAT:
367  kstat_once = 1; /* failed, don't try again */
368  return GNUNET_SYSERR;
369  }
370 #endif
371 
372  /* insert methods better than getloadavg for
373  other platforms HERE! */
374 
375  /* ok, maybe we have getloadavg on this platform */
376 #if HAVE_GETLOADAVG
377  {
378  static int warnOnce = 0;
379  double loadavg;
380  if (1 != getloadavg(&loadavg, 1))
381  {
382  /* only warn once, if there is a problem with
383  getloadavg, we're going to hit it frequently... */
384  if (warnOnce == 0)
385  {
386  warnOnce = 1;
388  }
389  return GNUNET_SYSERR;
390  }
391  else
392  {
393  /* success with getloadavg */
394  currentCPULoad = (int)(100 * loadavg);
395  currentIOLoad = -1; /* FIXME */
396  return GNUNET_OK;
397  }
398  }
399 #endif
400 
401  /* loadaverage not defined and no platform
402  specific alternative defined
403  => default: error
404  */
405  return GNUNET_SYSERR;
406 }
407 
408 
414 static void
416 {
417  static struct GNUNET_TIME_Absolute lastCall;
418  struct GNUNET_TIME_Relative age;
419 
420  age = GNUNET_TIME_absolute_get_duration(lastCall);
421  if ((agedCPULoad == -1)
422  || (age.rel_value_us > 500000))
423  {
424  /* use smoothing, but do NOT update lastRet at frequencies higher
425  than 500ms; this makes the smoothing (mostly) independent from
426  the frequency at which getCPULoad is called (and we don't spend
427  more time measuring CPU than actually computing something). */
428  lastCall = GNUNET_TIME_absolute_get();
429  updateUsage();
430  if (currentCPULoad == -1)
431  {
432  agedCPULoad = -1;
433  }
434  else
435  {
436  if (agedCPULoad == -1)
437  {
439  }
440  else
441  {
442  /* for CPU, we don't do the 'fast increase' since CPU is much
443  more jitterish to begin with */
444  agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
445  }
446  }
447  if (currentIOLoad == -1)
448  {
449  agedIOLoad = -1;
450  }
451  else
452  {
453  if (agedIOLoad == -1)
454  {
456  }
457  else
458  {
459  /* for IO, we don't do the 'fast increase' since IO is much
460  more jitterish to begin with */
461  agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
462  }
463  }
464  }
465 }
466 
472 static int
474 {
475  updateAgedLoad();
476  return (int)agedCPULoad;
477 }
478 
479 
485 static int
487 {
488  updateAgedLoad();
489  return (int)agedIOLoad;
490 }
491 
497 static unsigned int
499 {
500  double percentage;
501 
502  meminfo();
503  percentage = (((double)kb_main_used) / ((double)kb_main_total) * 100.0);
504  return (unsigned int)percentage;
505 }
506 
507 
508 #ifdef LINUX
509 #include <dirent.h>
515 static unsigned int
516 get_nproc()
517 {
518  DIR *dir;
519  struct dirent *ent;
520  unsigned int nproc;
521 
522  dir = opendir("/proc");
523  if (NULL == dir)
524  return 0;
525  nproc = 0;
526  while (NULL != (ent = readdir(dir)))
527  {
528  if ((*ent->d_name > '0') && (*ent->d_name <= '9'))
529  nproc++;
530  }
531  closedir(dir);
532  return nproc;
533 }
534 #endif
535 
536 
537 static void
539 {
540  struct GNUNET_TIME_Absolute now;
541  char *str;
542  int nbs;
543  int ld_cpu;
544  int ld_disk;
545  unsigned int mem_usage;
546  unsigned int nproc;
547 
548  sample_load_task_id = NULL;
549  ld_cpu = cpu_get_load();
550  ld_disk = disk_get_load();
551  if ((-1 == ld_cpu) || (-1 == ld_disk))
552  goto reschedule;
553  mem_usage = mem_get_usage();
554 #ifdef LINUX
555  nproc = get_nproc();
556 #else
557  nproc = 0;
558 #endif
559  now = GNUNET_TIME_absolute_get();
560  nbs = GNUNET_asprintf(&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL / 1000LL,
561  ld_cpu, ld_disk, mem_usage, nproc);
562  if (0 < nbs)
563  {
564  GNUNET_BIO_write(bw, str, nbs);
565  }
566  else
567  GNUNET_break(0);
568  GNUNET_free(str);
569 
570 reschedule:
571  sample_load_task_id =
573  &sample_load_task, NULL);
574 }
575 
576 
582 void
584 {
585  char *hostname;
586  char *stats_dir;
587  char *fn;
588  size_t len;
589 
590  if (GNUNET_OK !=
592  "STATS_DIR", &stats_dir))
593  return;
595  hostname = GNUNET_malloc(len);
596  if (0 != gethostname(hostname, len))
597  {
599  GNUNET_free(stats_dir);
600  GNUNET_free(hostname);
601  return;
602  }
603  fn = NULL;
604  (void)GNUNET_asprintf(&fn, "%s/%.*s-%jd.dat", stats_dir, len,
605  hostname, (intmax_t)getpid());
606  GNUNET_free(stats_dir);
607  GNUNET_free(hostname);
608  if (NULL == (bw = GNUNET_BIO_write_open(fn)))
609  {
611  _("Cannot open %s for writing load statistics. "
612  "Not logging load statistics\n"), fn);
613  GNUNET_free(fn);
614  return;
615  }
616  GNUNET_free(fn);
617  sample_load_task_id = GNUNET_SCHEDULER_add_now(&sample_load_task, NULL);
618 #ifdef LINUX
619  proc_stat = fopen("/proc/stat", "r");
620  if (NULL == proc_stat)
622  "fopen", "/proc/stat");
623 #elif OSX
624  initMachCpuStats();
625 #endif
626  updateUsage(); /* initialize */
627 }
628 
629 
633 void
635 {
636  if (NULL == bw)
637  return;
638 #ifdef LINUX
639  if (proc_stat != NULL)
640  {
641  fclose(proc_stat);
642  proc_stat = NULL;
643  }
644 #elif OSX
645  GNUNET_free_non_null(prev_cpu_load);
646 #endif
647  if (NULL != sample_load_task_id)
648  {
649  GNUNET_SCHEDULER_cancel(sample_load_task_id);
650  sample_load_task_id = NULL;
651  }
653  bw = NULL;
654 }
655 
656 /* end of cpustatus.c */
static int cpu_get_load()
Get the load of the CPU relative to what is allowed.
static struct GNUNET_STATISTICS_Handle * stats
Handle for statistics.
static char * dir
Set to the directory where runtime files are stored.
Definition: gnunet-arm.c:84
static void updateAgedLoad()
Update load values (if enough time has expired), including computation of averages.
static void sample_load_task(void *cls)
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
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.
static int ret
Final status code.
Definition: gnunet-arm.c:89
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:1237
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_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:1264
Handle for buffered writing.
Definition: bio.c:378
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:118
void meminfo(void)
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition: gnunet-arm.c:104
#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:436
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:83
struct GNUNET_BIO_WriteHandle * GNUNET_BIO_write_open(const char *fn)
Open a file for writing.
Definition: bio.c:408
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:373
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:131
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:481
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:956