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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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"
33 #include "gnunet-service-testbed_meminfo.h"
39 #if HAVE_SYS_SYSINFO_H
40 #include <sys/sysinfo.h>
53 #include <mach/mach.h>
55 static processor_cpu_load_info_t prev_cpu_load;
61 #define DEBUG_STATUSCALLS GNUNET_NO
64 static FILE *proc_stat;
68 * Current CPU load, as percentage of CPU cycles not idle or
71 static int currentCPULoad;
73 static double agedCPULoad = -1;
76 * Current IO load, as percentage of CPU cycles blocked on IO.
78 static int currentIOLoad;
80 static double agedIOLoad = -1;
84 * hanlde to the file to write the load statistics to
86 struct GNUNET_BIO_WriteHandle *bw;
88 struct GNUNET_SCHEDULER_Task * sample_load_task_id;
95 unsigned int cpu_count;
96 processor_cpu_load_info_t cpu_load;
97 mach_msg_type_number_t cpu_msg_count;
101 kret = host_processor_info(mach_host_self(),
102 PROCESSOR_CPU_LOAD_INFO,
104 (processor_info_array_t *)&cpu_load,
106 if (kret != KERN_SUCCESS)
108 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
109 return GNUNET_SYSERR;
111 prev_cpu_load = GNUNET_malloc(cpu_count * sizeof(*prev_cpu_load));
112 for (i = 0; i < cpu_count; i++)
114 for (j = 0; j < CPU_STATE_MAX; j++)
116 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
119 vm_deallocate(mach_task_self(),
120 (vm_address_t)cpu_load,
121 (vm_size_t)(cpu_msg_count * sizeof(*cpu_load)));
127 * Update the currentCPU and currentIO load (and on Linux, memory) values.
129 * Before its first invocation the method initStatusCalls() must be called.
130 * If there is an error the method returns -1.
138 /* under linux, first try %idle/usage using /proc/stat;
139 if that does not work, disable /proc/stat for the future
140 by closing the file and use the next-best method. */
141 if (proc_stat != NULL)
143 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
144 static int have_last_cpu = GNUNET_NO;
147 unsigned long long user_read, system_read, nice_read, idle_read,
149 unsigned long long user, system, nice, idle, iowait;
150 unsigned long long usage_time = 0, total_time = 1;
152 /* Get the first line with the data */
155 if (NULL == fgets(line, 256, proc_stat))
157 GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
158 "fgets", "/proc/stat");
159 proc_stat = NULL; /* don't try again */
164 ret = sscanf(line, "%*s %llu %llu %llu %llu %llu",
166 &system_read, &nice_read, &idle_read, &iowait_read);
169 GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
170 "fgets-sscanf", "/proc/stat");
172 proc_stat = NULL; /* don't try again */
173 have_last_cpu = GNUNET_NO;
177 /* Store the current usage */
178 user = user_read - last_cpu_results[0];
179 system = system_read - last_cpu_results[1];
180 nice = nice_read - last_cpu_results[2];
181 idle = idle_read - last_cpu_results[3];
182 iowait = iowait_read - last_cpu_results[4];
183 /* Calculate the % usage */
184 usage_time = user + system + nice;
185 total_time = usage_time + idle + iowait;
186 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
188 currentCPULoad = (int)(100L * usage_time / total_time);
190 currentIOLoad = (int)(100L * iowait / total_time);
192 currentIOLoad = -1; /* 2.4 kernel */
194 /* Store the values for the next calculation */
195 last_cpu_results[0] = user_read;
196 last_cpu_results[1] = system_read;
197 last_cpu_results[2] = nice_read;
198 last_cpu_results[3] = idle_read;
199 last_cpu_results[4] = iowait_read;
200 have_last_cpu = GNUNET_YES;
209 unsigned int cpu_count;
210 processor_cpu_load_info_t cpu_load;
211 mach_msg_type_number_t cpu_msg_count;
212 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
213 unsigned long long t_idle_all, t_total_all;
217 t_idle_all = t_total_all = 0;
218 kret = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO,
220 (processor_info_array_t *)&cpu_load,
222 if (kret == KERN_SUCCESS)
224 for (i = 0; i < cpu_count; i++)
226 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
227 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
229 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
230 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
234 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
235 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
239 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
240 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
242 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
243 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
247 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
248 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
252 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
253 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
255 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
256 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
260 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
261 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
265 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
266 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
268 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
269 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
273 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
274 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
277 t_total = t_sys + t_user + t_nice + t_idle;
278 t_idle_all += t_idle;
279 t_total_all += t_total;
281 for (i = 0; i < cpu_count; i++)
283 for (j = 0; j < CPU_STATE_MAX; j++)
285 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
289 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
292 vm_deallocate(mach_task_self(),
293 (vm_address_t)cpu_load,
294 (vm_size_t)(cpu_msg_count * sizeof(*cpu_load)));
295 currentIOLoad = -1; /* FIXME-OSX! */
300 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
301 return GNUNET_SYSERR;
305 /* try kstat (Solaris only) */
306 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
308 static long long last_idlecount;
309 static long long last_totalcount;
310 static int kstat_once; /* if open fails, don't keep
315 long long totalcount;
317 long long deltatotal;
324 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING, "kstat_close");
330 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
334 if (0 != strncmp(khelper->ks_name, "cpu_stat", strlen("cpu_stat")))
336 if (khelper->ks_data_size > sizeof(cpu_stat_t))
337 continue; /* better save then sorry! */
338 if (-1 != kstat_read(kc, khelper, &stats))
340 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
342 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
343 stats.cpu_sysinfo.cpu[CPU_USER] +
344 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
345 stats.cpu_sysinfo.cpu[CPU_WAIT];
348 if (0 != kstat_close(kc))
349 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING, "kstat_close");
350 if ((idlecount == 0) && (totalcount == 0))
351 goto ABORT_KSTAT; /* no stats found => abort */
352 deltaidle = idlecount - last_idlecount;
353 deltatotal = totalcount - last_totalcount;
354 if ((deltatotal > 0) && (last_totalcount > 0))
356 currentCPULoad = (unsigned int)(100.0 * deltaidle / deltatotal);
357 if (currentCPULoad > 100)
358 currentCPULoad = 100; /* odd */
359 if (currentCPULoad < 0)
360 currentCPULoad = 0; /* odd */
361 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
365 currentIOLoad = -1; /* FIXME-SOLARIS! */
366 last_idlecount = idlecount;
367 last_totalcount = totalcount;
370 kstat_once = 1; /* failed, don't try again */
371 return GNUNET_SYSERR;
375 /* insert methods better than getloadavg for
376 other platforms HERE! */
378 /* ok, maybe we have getloadavg on this platform */
381 static int warnOnce = 0;
383 if (1 != getloadavg(&loadavg, 1))
385 /* only warn once, if there is a problem with
386 getloadavg, we're going to hit it frequently... */
390 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING, "getloadavg");
392 return GNUNET_SYSERR;
396 /* success with getloadavg */
397 currentCPULoad = (int)(100 * loadavg);
398 currentIOLoad = -1; /* FIXME */
406 if (GNNtQuerySystemInformation)
408 static double dLastKernel;
409 static double dLastIdle;
410 static double dLastUser;
417 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
419 if (GNNtQuerySystemInformation(SystemProcessorPerformanceInformation,
421 sizeof(theInfo), NULL) == NO_ERROR)
423 /* PORT-ME MINGW: Multi-processor? */
424 dKernel = Li2Double(theInfo.KernelTime);
425 dIdle = Li2Double(theInfo.IdleTime);
426 dUser = Li2Double(theInfo.UserTime);
427 dDiffKernel = dKernel - dLastKernel;
428 dDiffIdle = dIdle - dLastIdle;
429 dDiffUser = dUser - dLastUser;
431 if (((dDiffKernel + dDiffUser) > 0) &&
432 (dLastIdle + dLastKernel + dLastUser > 0))
434 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
436 currentCPULoad = -1; /* don't know (yet) */
438 dLastKernel = dKernel;
442 currentIOLoad = -1; /* FIXME-MINGW */
447 /* only warn once, if there is a problem with
448 NtQuery..., we're going to hit it frequently... */
453 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
454 "Cannot query the CPU usage (Windows NT).\n");
456 return GNUNET_SYSERR;
462 DWORD dwDataSize, dwType, dwDummy;
465 if (RegOpenKeyEx(HKEY_DYN_DATA,
466 "PerfStats\\StartSrv",
467 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
474 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
475 "Cannot query the CPU usage (Win 9x)\n");
479 RegOpenKeyEx(HKEY_DYN_DATA,
480 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
481 dwDataSize = sizeof(dwDummy);
482 RegQueryValueEx(hKey,
484 NULL, &dwType, (LPBYTE)&dwDummy, &dwDataSize);
488 RegOpenKeyEx(HKEY_DYN_DATA,
489 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
490 dwDataSize = sizeof(currentCPULoad);
491 RegQueryValueEx(hKey,
493 NULL, &dwType, (LPBYTE)¤tCPULoad, &dwDataSize);
495 currentIOLoad = -1; /* FIXME-MINGW! */
498 RegOpenKeyEx(HKEY_DYN_DATA,
499 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
500 RegOpenKeyEx(HKEY_DYN_DATA,
501 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
502 dwDataSize = sizeof(dwDummy);
503 RegQueryValueEx(hKey,
505 NULL, &dwType, (LPBYTE)&dwDummy, &dwDataSize);
512 /* loadaverage not defined and no platform
513 specific alternative defined
516 return GNUNET_SYSERR;
521 * Update load values (if enough time has expired),
522 * including computation of averages. Code assumes
523 * that lock has already been obtained.
528 static struct GNUNET_TIME_Absolute lastCall;
529 struct GNUNET_TIME_Relative age;
531 age = GNUNET_TIME_absolute_get_duration(lastCall);
532 if ((agedCPULoad == -1)
533 || (age.rel_value_us > 500000))
535 /* use smoothing, but do NOT update lastRet at frequencies higher
536 than 500ms; this makes the smoothing (mostly) independent from
537 the frequency at which getCPULoad is called (and we don't spend
538 more time measuring CPU than actually computing something). */
539 lastCall = GNUNET_TIME_absolute_get();
541 if (currentCPULoad == -1)
547 if (agedCPULoad == -1)
549 agedCPULoad = currentCPULoad;
553 /* for CPU, we don't do the 'fast increase' since CPU is much
554 more jitterish to begin with */
555 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
558 if (currentIOLoad == -1)
564 if (agedIOLoad == -1)
566 agedIOLoad = currentIOLoad;
570 /* for IO, we don't do the 'fast increase' since IO is much
571 more jitterish to begin with */
572 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
579 * Get the load of the CPU relative to what is allowed.
580 * @return the CPU load as a percentage of allowed
581 * (100 is equivalent to full load)
587 return (int)agedCPULoad;
592 * Get the load of the CPU relative to what is allowed.
593 * @return the CPU load as a percentage of allowed
594 * (100 is equivalent to full load)
600 return (int)agedIOLoad;
604 * Get the percentage of memory used
606 * @return the percentage of memory used
614 percentage = (((double)kb_main_used) / ((double)kb_main_total) * 100.0);
615 return (unsigned int)percentage;
622 * Returns the number of processes
624 * @return the number of processes
633 dir = opendir("/proc");
637 while (NULL != (ent = readdir(dir)))
639 if ((*ent->d_name > '0') && (*ent->d_name <= '9'))
649 sample_load_task(void *cls)
651 struct GNUNET_TIME_Absolute now;
656 unsigned int mem_usage;
659 sample_load_task_id = NULL;
660 ld_cpu = cpu_get_load();
661 ld_disk = disk_get_load();
662 if ((-1 == ld_cpu) || (-1 == ld_disk))
664 mem_usage = mem_get_usage();
670 now = GNUNET_TIME_absolute_get();
671 nbs = GNUNET_asprintf(&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL / 1000LL,
672 ld_cpu, ld_disk, mem_usage, nproc);
675 GNUNET_BIO_write(bw, str, nbs);
682 sample_load_task_id =
683 GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS,
684 &sample_load_task, NULL);
689 * Initialize logging CPU and IO statisticfs. Checks the configuration for
690 * "STATS_DIR" and logs to a file in that directory. The file is name is
691 * generated from the hostname and the process's PID.
694 GST_stats_init(const struct GNUNET_CONFIGURATION_Handle *cfg)
702 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
703 "Load statistics logging now available for windows\n");
704 return; /* No logging on windows for now :( */
708 GNUNET_CONFIGURATION_get_value_filename(cfg, "testbed",
709 "STATS_DIR", &stats_dir))
711 len = GNUNET_OS_get_hostname_max_length();
712 hostname = GNUNET_malloc(len);
713 if (0 != gethostname(hostname, len))
715 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING, "gethostname");
716 GNUNET_free(stats_dir);
717 GNUNET_free(hostname);
721 (void)GNUNET_asprintf(&fn, "%s/%.*s-%jd.dat", stats_dir, len,
722 hostname, (intmax_t)getpid());
723 GNUNET_free(stats_dir);
724 GNUNET_free(hostname);
725 if (NULL == (bw = GNUNET_BIO_write_open(fn)))
727 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
728 _("Cannot open %s for writing load statistics. "
729 "Not logging load statistics\n"), fn);
734 sample_load_task_id = GNUNET_SCHEDULER_add_now(&sample_load_task, NULL);
736 proc_stat = fopen("/proc/stat", "r");
737 if (NULL == proc_stat)
738 GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
739 "fopen", "/proc/stat");
743 updateUsage(); /* initialize */
748 * Shutdown the status calls module.
759 if (proc_stat != NULL)
765 GNUNET_free_non_null(prev_cpu_load);
767 if (NULL != sample_load_task_id)
769 GNUNET_SCHEDULER_cancel(sample_load_task_id);
770 sample_load_task_id = NULL;
772 GNUNET_break(GNUNET_OK == GNUNET_BIO_write_close(bw));
776 /* end of cpustatus.c */