2 This file is part of GNUnet.
3 (C) 2008--2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
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
32 #include "gnunet_util_lib.h"
38 #if HAVE_SYS_SYSINFO_H
39 #include <sys/sysinfo.h>
52 #include <mach/mach.h>
54 static processor_cpu_load_info_t prev_cpu_load;
57 #define DEBUG_STATUSCALLS GNUNET_NO
60 static FILE *proc_stat;
64 * Current CPU load, as percentage of CPU cycles not idle or
67 static int currentCPULoad;
69 static double agedCPULoad = -1;
72 * Current IO load, as percentage of CPU cycles blocked on IO.
74 static int currentIOLoad;
76 static double agedIOLoad = -1;
80 * hanlde to the file to write the load statistics to
82 struct GNUNET_BIO_WriteHandle *bw;
84 GNUNET_SCHEDULER_TaskIdentifier sample_load_task_id;
91 unsigned int cpu_count;
92 processor_cpu_load_info_t cpu_load;
93 mach_msg_type_number_t cpu_msg_count;
97 kret = host_processor_info (mach_host_self (),
98 PROCESSOR_CPU_LOAD_INFO,
100 (processor_info_array_t *) & cpu_load,
102 if (kret != KERN_SUCCESS)
104 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
105 return GNUNET_SYSERR;
107 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
108 for (i = 0; i < cpu_count; i++)
110 for (j = 0; j < CPU_STATE_MAX; j++)
112 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
115 vm_deallocate (mach_task_self (),
116 (vm_address_t) cpu_load,
117 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
123 * Update the currentCPU and currentIO load values.
125 * Before its first invocation the method initStatusCalls() must be called.
126 * If there is an error the method returns -1.
134 /* under linux, first try %idle/usage using /proc/stat;
135 if that does not work, disable /proc/stat for the future
136 by closing the file and use the next-best method. */
137 if (proc_stat != NULL)
139 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
140 static int have_last_cpu = GNUNET_NO;
143 unsigned long long user_read, system_read, nice_read, idle_read,
145 unsigned long long user, system, nice, idle, iowait;
146 unsigned long long usage_time = 0, total_time = 1;
148 /* Get the first line with the data */
151 if (NULL == fgets (line, 256, proc_stat))
153 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
154 "fgets", "/proc/stat");
155 proc_stat = NULL; /* don't try again */
160 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
162 &system_read, &nice_read, &idle_read, &iowait_read);
165 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
166 "fgets-sscanf", "/proc/stat");
168 proc_stat = NULL; /* don't try again */
169 have_last_cpu = GNUNET_NO;
173 /* Store the current usage */
174 user = user_read - last_cpu_results[0];
175 system = system_read - last_cpu_results[1];
176 nice = nice_read - last_cpu_results[2];
177 idle = idle_read - last_cpu_results[3];
178 iowait = iowait_read - last_cpu_results[4];
179 /* Calculate the % usage */
180 usage_time = user + system + nice;
181 total_time = usage_time + idle + iowait;
182 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
184 currentCPULoad = (int) (100L * usage_time / total_time);
186 currentIOLoad = (int) (100L * iowait / total_time);
188 currentIOLoad = -1; /* 2.4 kernel */
190 /* Store the values for the next calculation */
191 last_cpu_results[0] = user_read;
192 last_cpu_results[1] = system_read;
193 last_cpu_results[2] = nice_read;
194 last_cpu_results[3] = idle_read;
195 last_cpu_results[4] = iowait_read;
196 have_last_cpu = GNUNET_YES;
205 unsigned int cpu_count;
206 processor_cpu_load_info_t cpu_load;
207 mach_msg_type_number_t cpu_msg_count;
208 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
209 unsigned long long t_idle_all, t_total_all;
213 t_idle_all = t_total_all = 0;
214 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
216 (processor_info_array_t *) & cpu_load,
218 if (kret == KERN_SUCCESS)
220 for (i = 0; i < cpu_count; i++)
222 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
223 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
225 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
226 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
230 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
231 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
235 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
236 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
238 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
239 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
243 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
244 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
248 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
249 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
251 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
252 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
256 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
257 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
261 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
262 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
264 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
265 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
269 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
270 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
273 t_total = t_sys + t_user + t_nice + t_idle;
274 t_idle_all += t_idle;
275 t_total_all += t_total;
277 for (i = 0; i < cpu_count; i++)
279 for (j = 0; j < CPU_STATE_MAX; j++)
281 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
285 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
288 vm_deallocate (mach_task_self (),
289 (vm_address_t) cpu_load,
290 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
291 currentIOLoad = -1; /* FIXME-OSX! */
296 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
297 return GNUNET_SYSERR;
301 /* try kstat (Solaris only) */
302 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
304 static long long last_idlecount;
305 static long long last_totalcount;
306 static int kstat_once; /* if open fails, don't keep
311 long long totalcount;
313 long long deltatotal;
320 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
326 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
330 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
332 if (khelper->ks_data_size > sizeof (cpu_stat_t))
333 continue; /* better save then sorry! */
334 if (-1 != kstat_read (kc, khelper, &stats))
336 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
338 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
339 stats.cpu_sysinfo.cpu[CPU_USER] +
340 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
341 stats.cpu_sysinfo.cpu[CPU_WAIT];
344 if (0 != kstat_close (kc))
345 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
346 if ((idlecount == 0) && (totalcount == 0))
347 goto ABORT_KSTAT; /* no stats found => abort */
348 deltaidle = idlecount - last_idlecount;
349 deltatotal = totalcount - last_totalcount;
350 if ((deltatotal > 0) && (last_totalcount > 0))
352 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
353 if (currentCPULoad > 100)
354 currentCPULoad = 100; /* odd */
355 if (currentCPULoad < 0)
356 currentCPULoad = 0; /* odd */
357 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
361 currentIOLoad = -1; /* FIXME-SOLARIS! */
362 last_idlecount = idlecount;
363 last_totalcount = totalcount;
366 kstat_once = 1; /* failed, don't try again */
367 return GNUNET_SYSERR;
371 /* insert methods better than getloadavg for
372 other platforms HERE! */
374 /* ok, maybe we have getloadavg on this platform */
377 static int warnOnce = 0;
379 if (1 != getloadavg (&loadavg, 1))
381 /* only warn once, if there is a problem with
382 getloadavg, we're going to hit it frequently... */
386 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getloadavg");
388 return GNUNET_SYSERR;
392 /* success with getloadavg */
393 currentCPULoad = (int) (100 * loadavg);
394 currentIOLoad = -1; /* FIXME */
402 if (GNNtQuerySystemInformation)
404 static double dLastKernel;
405 static double dLastIdle;
406 static double dLastUser;
413 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
415 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
417 sizeof (theInfo), NULL) == NO_ERROR)
419 /* PORT-ME MINGW: Multi-processor? */
420 dKernel = Li2Double (theInfo.KernelTime);
421 dIdle = Li2Double (theInfo.IdleTime);
422 dUser = Li2Double (theInfo.UserTime);
423 dDiffKernel = dKernel - dLastKernel;
424 dDiffIdle = dIdle - dLastIdle;
425 dDiffUser = dUser - dLastUser;
427 if (((dDiffKernel + dDiffUser) > 0) &&
428 (dLastIdle + dLastKernel + dLastUser > 0))
430 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
432 currentCPULoad = -1; /* don't know (yet) */
434 dLastKernel = dKernel;
438 currentIOLoad = -1; /* FIXME-MINGW */
443 /* only warn once, if there is a problem with
444 NtQuery..., we're going to hit it frequently... */
449 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
450 "Cannot query the CPU usage (Windows NT).\n");
452 return GNUNET_SYSERR;
458 DWORD dwDataSize, dwType, dwDummy;
461 if (RegOpenKeyEx (HKEY_DYN_DATA,
462 "PerfStats\\StartSrv",
463 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
471 "Cannot query the CPU usage (Win 9x)\n");
475 RegOpenKeyEx (HKEY_DYN_DATA,
476 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
477 dwDataSize = sizeof (dwDummy);
478 RegQueryValueEx (hKey,
480 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
484 RegOpenKeyEx (HKEY_DYN_DATA,
485 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
486 dwDataSize = sizeof (currentCPULoad);
487 RegQueryValueEx (hKey,
489 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
491 currentIOLoad = -1; /* FIXME-MINGW! */
494 RegOpenKeyEx (HKEY_DYN_DATA,
495 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
496 RegOpenKeyEx (HKEY_DYN_DATA,
497 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
498 dwDataSize = sizeof (dwDummy);
499 RegQueryValueEx (hKey,
501 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
508 /* loadaverage not defined and no platform
509 specific alternative defined
512 return GNUNET_SYSERR;
517 * Update load values (if enough time has expired),
518 * including computation of averages. Code assumes
519 * that lock has already been obtained.
524 static struct GNUNET_TIME_Absolute lastCall;
525 struct GNUNET_TIME_Relative age;
527 age = GNUNET_TIME_absolute_get_duration (lastCall);
528 if ( (agedCPULoad == -1)
529 || (age.rel_value > 500) )
531 /* use smoothing, but do NOT update lastRet at frequencies higher
532 than 500ms; this makes the smoothing (mostly) independent from
533 the frequency at which getCPULoad is called (and we don't spend
534 more time measuring CPU than actually computing something). */
535 lastCall = GNUNET_TIME_absolute_get ();
537 if (currentCPULoad == -1)
543 if (agedCPULoad == -1)
545 agedCPULoad = currentCPULoad;
549 /* for CPU, we don't do the 'fast increase' since CPU is much
550 more jitterish to begin with */
551 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
554 if (currentIOLoad == -1)
560 if (agedIOLoad == -1)
562 agedIOLoad = currentIOLoad;
566 /* for IO, we don't do the 'fast increase' since IO is much
567 more jitterish to begin with */
568 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
575 * Get the load of the CPU relative to what is allowed.
576 * @return the CPU load as a percentage of allowed
577 * (100 is equivalent to full load)
583 return (int) agedCPULoad;
588 * Get the load of the CPU relative to what is allowed.
589 * @return the CPU load as a percentage of allowed
590 * (100 is equivalent to full load)
596 return (int) agedIOLoad;
601 sample_load_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
603 struct GNUNET_TIME_Absolute now;
609 sample_load_task_id = GNUNET_SCHEDULER_NO_TASK;
610 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
612 ld_cpu = cpu_get_load ();
613 ld_disk = disk_get_load ();
614 if ( (-1 == ld_cpu) || (-1 == ld_disk) )
616 now = GNUNET_TIME_absolute_get ();
617 nbs = GNUNET_asprintf (&str, "%llu %d %d\n", now.abs_value / 1000,
621 GNUNET_BIO_write (bw, str, nbs);
628 sample_load_task_id =
629 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
630 &sample_load_task, NULL);
635 * Initialize logging CPU and IO statisticfs. Checks the configuration for
636 * "STATS_DIR" and logs to a file in that directory. The file is name is
637 * generated from the hostname and the process's PID.
640 GST_stats_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
648 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
649 "Load statistics logging now available for windows\n");
650 return; /* No logging on windows for now :( */
654 GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
655 "STATS_DIR", &stats_dir))
657 len = GNUNET_OS_get_hostname_max_length ();
658 hostname = GNUNET_malloc (len);
659 if (0 != gethostname (hostname, len))
661 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
662 GNUNET_free (stats_dir);
663 GNUNET_free (hostname);
667 (void) GNUNET_asprintf (&fn, "%s/%.*s-%jd.dat", stats_dir, len,
668 hostname, (intmax_t) getpid());
669 GNUNET_free (stats_dir);
670 GNUNET_free (hostname);
671 if (NULL == (bw = GNUNET_BIO_write_open (fn)))
673 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
674 _("Cannot open %s for writing load statistics. "
675 "Not logging load statistics\n"), fn);
680 sample_load_task_id = GNUNET_SCHEDULER_add_now (&sample_load_task, NULL);
682 proc_stat = fopen ("/proc/stat", "r");
683 if (NULL == proc_stat)
684 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
685 "fopen", "/proc/stat");
689 updateUsage (); /* initialize */
695 * Shutdown the status calls module.
706 if (proc_stat != NULL)
712 GNUNET_free_non_null (prev_cpu_load);
714 if (GNUNET_SCHEDULER_NO_TASK != sample_load_task_id)
716 GNUNET_SCHEDULER_cancel (sample_load_task_id);
717 sample_load_task_id = GNUNET_SCHEDULER_NO_TASK;
719 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bw));
723 /* end of cpustatus.c */