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;
58 #define DEBUG_STATUSCALLS GNUNET_NO
61 static FILE *proc_stat;
65 * Current CPU load, as percentage of CPU cycles not idle or
68 static int currentCPULoad;
70 static double agedCPULoad = -1;
73 * Current IO load, as percentage of CPU cycles blocked on IO.
75 static int currentIOLoad;
77 static double agedIOLoad = -1;
81 * hanlde to the file to write the load statistics to
83 struct GNUNET_BIO_WriteHandle *bw;
85 struct GNUNET_SCHEDULER_Task *sample_load_task_id;
92 unsigned int cpu_count;
93 processor_cpu_load_info_t cpu_load;
94 mach_msg_type_number_t cpu_msg_count;
98 kret = host_processor_info (mach_host_self (),
99 PROCESSOR_CPU_LOAD_INFO,
101 (processor_info_array_t *) &cpu_load,
103 if (kret != KERN_SUCCESS)
105 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
106 return GNUNET_SYSERR;
108 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof(*prev_cpu_load));
109 for (i = 0; i < cpu_count; i++)
111 for (j = 0; j < CPU_STATE_MAX; j++)
113 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
116 vm_deallocate (mach_task_self (),
117 (vm_address_t) cpu_load,
118 (vm_size_t) (cpu_msg_count * sizeof(*cpu_load)));
124 * Update the currentCPU and currentIO load (and on Linux, memory) values.
126 * Before its first invocation the method initStatusCalls() must be called.
127 * If there is an error the method returns -1.
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)
140 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
141 static int have_last_cpu = GNUNET_NO;
144 unsigned long long user_read, system_read, nice_read, idle_read,
146 unsigned long long user, system, nice, idle, iowait;
147 unsigned long long usage_time = 0, total_time = 1;
149 /* Get the first line with the data */
152 if (NULL == fgets (line, 256, proc_stat))
154 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
155 "fgets", "/proc/stat");
156 proc_stat = NULL; /* don't try again */
161 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
163 &system_read, &nice_read, &idle_read, &iowait_read);
166 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
167 "fgets-sscanf", "/proc/stat");
169 proc_stat = NULL; /* don't try again */
170 have_last_cpu = GNUNET_NO;
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))
185 currentCPULoad = (int) (100L * usage_time / total_time);
187 currentIOLoad = (int) (100L * iowait / total_time);
189 currentIOLoad = -1; /* 2.4 kernel */
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;
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;
214 t_idle_all = t_total_all = 0;
215 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
217 (processor_info_array_t *) &cpu_load,
219 if (kret == KERN_SUCCESS)
221 for (i = 0; i < cpu_count; i++)
223 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
224 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
226 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
227 - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
231 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
232 + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]
236 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
237 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
239 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER]
240 - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
244 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER]
245 + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER]
249 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
250 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
252 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE]
253 - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
257 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE]
258 + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE]
262 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
263 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
265 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
266 - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
270 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
271 + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE]
274 t_total = t_sys + t_user + t_nice + t_idle;
275 t_idle_all += t_idle;
276 t_total_all += t_total;
278 for (i = 0; i < cpu_count; i++)
280 for (j = 0; j < CPU_STATE_MAX; j++)
282 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
286 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
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! */
297 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "host_processor_info failed.");
298 return GNUNET_SYSERR;
302 /* try kstat (Solaris only) */
303 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
305 static long long last_idlecount;
306 static long long last_totalcount;
307 static int kstat_once; /* if open fails, don't keep
312 long long totalcount;
314 long long deltatotal;
321 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
327 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
331 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
333 if (khelper->ks_data_size > sizeof(cpu_stat_t))
334 continue; /* better save then sorry! */
335 if (-1 != kstat_read (kc, khelper, &stats))
337 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
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];
345 if (0 != kstat_close (kc))
346 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kstat_close");
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))
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! */
362 currentIOLoad = -1; /* FIXME-SOLARIS! */
363 last_idlecount = idlecount;
364 last_totalcount = totalcount;
367 kstat_once = 1; /* failed, don't try again */
368 return GNUNET_SYSERR;
372 /* insert methods better than getloadavg for
373 other platforms HERE! */
375 /* ok, maybe we have getloadavg on this platform */
378 static int warnOnce = 0;
380 if (1 != getloadavg (&loadavg, 1))
382 /* only warn once, if there is a problem with
383 getloadavg, we're going to hit it frequently... */
387 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getloadavg");
389 return GNUNET_SYSERR;
393 /* success with getloadavg */
394 currentCPULoad = (int) (100 * loadavg);
395 currentIOLoad = -1; /* FIXME */
401 /* loadaverage not defined and no platform
402 specific alternative defined
405 return GNUNET_SYSERR;
410 * Update load values (if enough time has expired),
411 * including computation of averages. Code assumes
412 * that lock has already been obtained.
417 static struct GNUNET_TIME_Absolute lastCall;
418 struct GNUNET_TIME_Relative age;
420 age = GNUNET_TIME_absolute_get_duration (lastCall);
421 if ((agedCPULoad == -1)
422 || (age.rel_value_us > 500000))
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 ();
430 if (currentCPULoad == -1)
436 if (agedCPULoad == -1)
438 agedCPULoad = currentCPULoad;
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;
447 if (currentIOLoad == -1)
453 if (agedIOLoad == -1)
455 agedIOLoad = currentIOLoad;
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;
468 * Get the load of the CPU relative to what is allowed.
469 * @return the CPU load as a percentage of allowed
470 * (100 is equivalent to full load)
476 return (int) agedCPULoad;
481 * Get the load of the CPU relative to what is allowed.
482 * @return the CPU load as a percentage of allowed
483 * (100 is equivalent to full load)
489 return (int) agedIOLoad;
493 * Get the percentage of memory used
495 * @return the percentage of memory used
503 percentage = (((double) kb_main_used) / ((double) kb_main_total) * 100.0);
504 return (unsigned int) percentage;
511 * Returns the number of processes
513 * @return the number of processes
522 dir = opendir ("/proc");
526 while (NULL != (ent = readdir (dir)))
528 if ((*ent->d_name > '0') && (*ent->d_name <= '9'))
538 sample_load_task (void *cls)
540 struct GNUNET_TIME_Absolute now;
545 unsigned int mem_usage;
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))
553 mem_usage = mem_get_usage ();
555 nproc = get_nproc ();
559 now = GNUNET_TIME_absolute_get ();
560 nbs = GNUNET_asprintf (&str, "%llu %d %d %u %u\n", now.abs_value_us / 1000LL
562 ld_cpu, ld_disk, mem_usage, nproc);
565 GNUNET_BIO_write (bw, str, nbs);
572 sample_load_task_id =
573 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
574 &sample_load_task, NULL);
579 * Initialize logging CPU and IO statisticfs. Checks the configuration for
580 * "STATS_DIR" and logs to a file in that directory. The file is name is
581 * generated from the hostname and the process's PID.
584 GST_stats_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
592 GNUNET_CONFIGURATION_get_value_filename (cfg, "testbed",
593 "STATS_DIR", &stats_dir))
595 len = GNUNET_OS_get_hostname_max_length ();
596 hostname = GNUNET_malloc (len);
597 if (0 != gethostname (hostname, len))
599 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
600 GNUNET_free (stats_dir);
601 GNUNET_free (hostname);
605 (void) GNUNET_asprintf (&fn, "%s/%.*s-%jd.dat", stats_dir, len,
606 hostname, (intmax_t) getpid ());
607 GNUNET_free (stats_dir);
608 GNUNET_free (hostname);
609 if (NULL == (bw = GNUNET_BIO_write_open (fn)))
611 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
612 _ ("Cannot open %s for writing load statistics. "
613 "Not logging load statistics\n"), fn);
618 sample_load_task_id = GNUNET_SCHEDULER_add_now (&sample_load_task, NULL);
620 proc_stat = fopen ("/proc/stat", "r");
621 if (NULL == proc_stat)
622 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
623 "fopen", "/proc/stat");
627 updateUsage (); /* initialize */
632 * Shutdown the status calls module.
640 if (proc_stat != NULL)
646 GNUNET_free_non_null (prev_cpu_load);
648 if (NULL != sample_load_task_id)
650 GNUNET_SCHEDULER_cancel (sample_load_task_id);
651 sample_load_task_id = NULL;
653 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (bw));
657 /* end of cpustatus.c */