aboutsummaryrefslogtreecommitdiff
path: root/src/testbed/gnunet-service-testbed_cpustatus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testbed/gnunet-service-testbed_cpustatus.c')
-rw-r--r--src/testbed/gnunet-service-testbed_cpustatus.c664
1 files changed, 0 insertions, 664 deletions
diff --git a/src/testbed/gnunet-service-testbed_cpustatus.c b/src/testbed/gnunet-service-testbed_cpustatus.c
deleted file mode 100644
index 5b9528ba3..000000000
--- a/src/testbed/gnunet-service-testbed_cpustatus.c
+++ /dev/null
@@ -1,664 +0,0 @@
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
55static processor_cpu_load_info_t prev_cpu_load;
56#endif
57
58#define DEBUG_STATUSCALLS GNUNET_NO
59
60#ifdef __linux__
61static FILE *proc_stat;
62#endif
63
64/**
65 * Current CPU load, as percentage of CPU cycles not idle or
66 * blocked on IO.
67 */
68static int currentCPULoad;
69
70static double agedCPULoad = -1;
71
72/**
73 * Current IO load, as percentage of CPU cycles blocked on IO.
74 */
75static int currentIOLoad;
76
77static double agedIOLoad = -1;
78
79
80/**
81 * handle to the file to write the load statistics to
82 */
83struct GNUNET_BIO_WriteHandle *bw;
84
85struct GNUNET_SCHEDULER_Task *sample_load_task_id;
86
87
88#ifdef OSX
89static int
90initMachCpuStats ()
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 */
131static int
132updateUsage ()
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;
368ABORT_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 */
416static void
417updateAgedLoad ()
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 */
475static int
476cpu_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 */
488static int
489disk_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 */
501static unsigned int
502mem_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 */
519static unsigned int
520get_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
543static void
544sample_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
577reschedule:
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 */
589void
590GST_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, (int)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 */
640void
641GST_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 (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 */