2 This file is part of GNUnet.
3 Copyright (C) 2008--2013 GNUnet e.V.
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.
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.
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/>.
20 * @file testbed/gnunet-service-testbed_cpustatus.c
21 * @brief calls to determine current CPU load
22 * @author Tzvetan Horozov
23 * @author Christian Grothoff
24 * @author Igor Wronsky
25 * @author Alex Harper (OS X portion)
26 * @author Sree Harsha Totakura
30 #include "gnunet_util_lib.h"
31 #include "gnunet-service-testbed_meminfo.h"
37 #if HAVE_SYS_SYSINFO_H
38 #include <sys/sysinfo.h>
51 #include <mach/mach.h>
53 static processor_cpu_load_info_t prev_cpu_load;
59 #define DEBUG_STATUSCALLS GNUNET_NO
62 static FILE *proc_stat;
66 * Current CPU load, as percentage of CPU cycles not idle or
69 static int currentCPULoad;
71 static double agedCPULoad = -1;
74 * Current IO load, as percentage of CPU cycles blocked on IO.
76 static int currentIOLoad;
78 static double agedIOLoad = -1;
82 * hanlde to the file to write the load statistics to
84 struct GNUNET_BIO_WriteHandle *bw;
86 struct GNUNET_SCHEDULER_Task * sample_load_task_id;
93 unsigned int cpu_count;
94 processor_cpu_load_info_t cpu_load;
95 mach_msg_type_number_t cpu_msg_count;
99 kret = host_processor_info (mach_host_self (),
100 PROCESSOR_CPU_LOAD_INFO,
102 (processor_info_array_t *) & cpu_load,
104 if (kret != KERN_SUCCESS)
106 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
107 return GNUNET_SYSERR;
109 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
110 for (i = 0; i < cpu_count; i++)
112 for (j = 0; j < CPU_STATE_MAX; j++)
114 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
117 vm_deallocate (mach_task_self (),
118 (vm_address_t) cpu_load,
119 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
125 * Update the currentCPU and currentIO load (and on Linux, memory) values.
127 * Before its first invocation the method initStatusCalls() must be called.
128 * If there is an error the method returns -1.
136 /* under linux, first try %idle/usage using /proc/stat;
137 if that does not work, disable /proc/stat for the future
138 by closing the file and use the next-best method. */
139 if (proc_stat != NULL)
141 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
142 static int have_last_cpu = GNUNET_NO;
145 unsigned long long user_read, system_read, nice_read, idle_read,
147 unsigned long long user, system, nice, idle, iowait;
148 unsigned long long usage_time = 0, total_time = 1;
150 /* Get the first line with the data */
153 if (NULL == fgets (line, 256, proc_stat))
155 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
156 "fgets", "/proc/stat");
157 proc_stat = NULL; /* don't try again */
162 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
164 &system_read, &nice_read, &idle_read, &iowait_read);
167 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
168 "fgets-sscanf", "/proc/stat");
170 proc_stat = NULL; /* don't try again */
171 have_last_cpu = GNUNET_NO;
175 /* Store the current usage */
176 user = user_read - last_cpu_results[0];
177 system = system_read - last_cpu_results[1];
178 nice = nice_read - last_cpu_results[2];
179 idle = idle_read - last_cpu_results[3];
180 iowait = iowait_read - last_cpu_results[4];
181 /* Calculate the % usage */
182 usage_time = user + system + nice;
183 total_time = usage_time + idle + iowait;
184 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
186 currentCPULoad = (int) (100L * usage_time / total_time);
188 currentIOLoad = (int) (100L * iowait / total_time);
190 currentIOLoad = -1; /* 2.4 kernel */
192 /* Store the values for the next calculation */
193 last_cpu_results[0] = user_read;
194 last_cpu_results[1] = system_read;
195 last_cpu_results[2] = nice_read;
196 last_cpu_results[3] = idle_read;
197 last_cpu_results[4] = iowait_read;
198 have_last_cpu = GNUNET_YES;
207 unsigned int cpu_count;
208 processor_cpu_load_info_t cpu_load;
209 mach_msg_type_number_t cpu_msg_count;
210 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
211 unsigned long long t_idle_all, t_total_all;
215 t_idle_all = t_total_all = 0;
216 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
218 (processor_info_array_t *) & cpu_load,
220 if (kret == KERN_SUCCESS)
222 for (i = 0; i < cpu_count; i++)
224 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
225 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
227 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
228 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
232 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
233 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
237 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
238 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
240 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
241 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
245 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
246 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
250 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
251 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
253 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
254 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
258 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
259 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
263 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
264 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
266 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
267 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
271 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
272 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
275 t_total = t_sys + t_user + t_nice + t_idle;
276 t_idle_all += t_idle;
277 t_total_all += t_total;
279 for (i = 0; i < cpu_count; i++)
281 for (j = 0; j < CPU_STATE_MAX; j++)
283 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
287 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
290 vm_deallocate (mach_task_self (),
291 (vm_address_t) cpu_load,
292 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
293 currentIOLoad = -1; /* FIXME-OSX! */
298 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
299 return GNUNET_SYSERR;
303 /* try kstat (Solaris only) */
304 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
306 static long long last_idlecount;
307 static long long last_totalcount;
308 static int kstat_once; /* if open fails, don't keep
313 long long totalcount;
315 long long deltatotal;
322 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
328 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
332 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
334 if (khelper->ks_data_size > sizeof (cpu_stat_t))
335 continue; /* better save then sorry! */
336 if (-1 != kstat_read (kc, khelper, &stats))
338 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
340 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
341 stats.cpu_sysinfo.cpu[CPU_USER] +
342 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
343 stats.cpu_sysinfo.cpu[CPU_WAIT];
346 if (0 != kstat_close (kc))
347 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
348 if ((idlecount == 0) && (totalcount == 0))
349 goto ABORT_KSTAT; /* no stats found => abort */
350 deltaidle = idlecount - last_idlecount;
351 deltatotal = totalcount - last_totalcount;
352 if ((deltatotal > 0) && (last_totalcount > 0))
354 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
355 if (currentCPULoad > 100)
356 currentCPULoad = 100; /* odd */
357 if (currentCPULoad < 0)
358 currentCPULoad = 0; /* odd */
359 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
363 currentIOLoad = -1; /* FIXME-SOLARIS! */
364 last_idlecount = idlecount;
365 last_totalcount = totalcount;
368 kstat_once = 1; /* failed, don't try again */
369 return GNUNET_SYSERR;
373 /* insert methods better than getloadavg for
374 other platforms HERE! */
376 /* ok, maybe we have getloadavg on this platform */
379 static int warnOnce = 0;
381 if (1 != getloadavg (&loadavg, 1))
383 /* only warn once, if there is a problem with
384 getloadavg, we're going to hit it frequently... */
388 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getloadavg");
390 return GNUNET_SYSERR;
394 /* success with getloadavg */
395 currentCPULoad = (int) (100 * loadavg);
396 currentIOLoad = -1; /* FIXME */
404 if (GNNtQuerySystemInformation)
406 static double dLastKernel;
407 static double dLastIdle;
408 static double dLastUser;
415 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
417 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
419 sizeof (theInfo), NULL) == NO_ERROR)
421 /* PORT-ME MINGW: Multi-processor? */
422 dKernel = Li2Double (theInfo.KernelTime);
423 dIdle = Li2Double (theInfo.IdleTime);
424 dUser = Li2Double (theInfo.UserTime);
425 dDiffKernel = dKernel - dLastKernel;
426 dDiffIdle = dIdle - dLastIdle;
427 dDiffUser = dUser - dLastUser;
429 if (((dDiffKernel + dDiffUser) > 0) &&
430 (dLastIdle + dLastKernel + dLastUser > 0))
432 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
434 currentCPULoad = -1; /* don't know (yet) */
436 dLastKernel = dKernel;
440 currentIOLoad = -1; /* FIXME-MINGW */
445 /* only warn once, if there is a problem with
446 NtQuery..., we're going to hit it frequently... */
451 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
452 "Cannot query the CPU usage (Windows NT).\n");
454 return GNUNET_SYSERR;
460 DWORD dwDataSize, dwType, dwDummy;
463 if (RegOpenKeyEx (HKEY_DYN_DATA,
464 "PerfStats\\StartSrv",
465 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
472 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
473 "Cannot query the CPU usage (Win 9x)\n");
477 RegOpenKeyEx (HKEY_DYN_DATA,
478 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
479 dwDataSize = sizeof (dwDummy);
480 RegQueryValueEx (hKey,
482 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
486 RegOpenKeyEx (HKEY_DYN_DATA,
487 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
488 dwDataSize = sizeof (currentCPULoad);
489 RegQueryValueEx (hKey,
491 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
493 currentIOLoad = -1; /* FIXME-MINGW! */
496 RegOpenKeyEx (HKEY_DYN_DATA,
497 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
498 RegOpenKeyEx (HKEY_DYN_DATA,
499 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
500 dwDataSize = sizeof (dwDummy);
501 RegQueryValueEx (hKey,
503 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
510 /* loadaverage not defined and no platform
511 specific alternative defined
514 return GNUNET_SYSERR;
519 * Update load values (if enough time has expired),
520 * including computation of averages. Code assumes
521 * that lock has already been obtained.
526 static struct GNUNET_TIME_Absolute lastCall;
527 struct GNUNET_TIME_Relative age;
529 age = GNUNET_TIME_absolute_get_duration (lastCall);
530 if ( (agedCPULoad == -1)
531 || (age.rel_value_us > 500000) )
533 /* use smoothing, but do NOT update lastRet at frequencies higher
534 than 500ms; this makes the smoothing (mostly) independent from
535 the frequency at which getCPULoad is called (and we don't spend
536 more time measuring CPU than actually computing something). */
537 lastCall = GNUNET_TIME_absolute_get ();
539 if (currentCPULoad == -1)
545 if (agedCPULoad == -1)
547 agedCPULoad = currentCPULoad;
551 /* for CPU, we don't do the 'fast increase' since CPU is much
552 more jitterish to begin with */
553 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
556 if (currentIOLoad == -1)
562 if (agedIOLoad == -1)
564 agedIOLoad = currentIOLoad;
568 /* for IO, we don't do the 'fast increase' since IO is much
569 more jitterish to begin with */
570 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
577 * Get the load of the CPU relative to what is allowed.
578 * @return the CPU load as a percentage of allowed
579 * (100 is equivalent to full load)
585 return (int) agedCPULoad;
590 * Get the load of the CPU relative to what is allowed.
591 * @return the CPU load as a percentage of allowed
592 * (100 is equivalent to full load)
598 return (int) agedIOLoad;
602 * Get the percentage of memory used
604 * @return the percentage of memory used
612 percentage = ( ((double) kb_main_used) / ((double) kb_main_total) * 100.0 );
613 return (unsigned int) percentage;
620 * Returns the number of processes
622 * @return the number of processes
631 dir = opendir ("/proc");
635 while (NULL != (ent = readdir (dir)))
637 if((*ent->d_name > '0') && (*ent->d_name <= '9'))
647 sample_load_task (void *cls)
649 struct GNUNET_TIME_Absolute now;
654 unsigned int mem_usage;
657 sample_load_task_id = NULL;
658 ld_cpu = cpu_get_load ();
659 ld_disk = disk_get_load ();
660 if ( (-1 == ld_cpu) || (-1 == ld_disk) )
662 mem_usage = mem_get_usage ();
664 nproc = get_nproc ();
668 now = GNUNET_TIME_absolute_get ();
669 nbs = GNUNET_asprintf (&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL / 1000LL,
670 ld_cpu, ld_disk, mem_usage, nproc);
673 GNUNET_BIO_write (bw, str, nbs);
680 sample_load_task_id =
681 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
682 &sample_load_task, NULL);
687 * Initialize logging CPU and IO statisticfs. Checks the configuration for
688 * "STATS_DIR" and logs to a file in that directory. The file is name is
689 * generated from the hostname and the process's PID.
692 GST_stats_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
700 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
701 "Load statistics logging now available for windows\n");
702 return; /* No logging on windows for now :( */
706 GNUNET_CONFIGURATION_get_value_filename (cfg, "testbed",
707 "STATS_DIR", &stats_dir))
709 len = GNUNET_OS_get_hostname_max_length ();
710 hostname = GNUNET_malloc (len);
711 if (0 != gethostname (hostname, len))
713 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
714 GNUNET_free (stats_dir);
715 GNUNET_free (hostname);
719 (void) GNUNET_asprintf (&fn, "%s/%.*s-%jd.dat", stats_dir, len,
720 hostname, (intmax_t) getpid());
721 GNUNET_free (stats_dir);
722 GNUNET_free (hostname);
723 if (NULL == (bw = GNUNET_BIO_write_open (fn)))
725 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
726 _("Cannot open %s for writing load statistics. "
727 "Not logging load statistics\n"), fn);
732 sample_load_task_id = GNUNET_SCHEDULER_add_now (&sample_load_task, NULL);
734 proc_stat = fopen ("/proc/stat", "r");
735 if (NULL == proc_stat)
736 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
737 "fopen", "/proc/stat");
741 updateUsage (); /* initialize */
747 * Shutdown the status calls module.
758 if (proc_stat != NULL)
764 GNUNET_free_non_null (prev_cpu_load);
766 if (NULL != sample_load_task_id)
768 GNUNET_SCHEDULER_cancel (sample_load_task_id);
769 sample_load_task_id = NULL;
771 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bw));
775 /* end of cpustatus.c */