2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 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 util/os_load.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)
31 #include "gnunet_common.h"
32 #include "gnunet_os_lib.h"
33 #include "gnunet_strings_lib.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
62 * File descriptor for reading /proc/stat (or NULL)
64 static FILE *proc_stat;
67 * Is this the first time we're trying to open /proc/stat? If
68 * not, we don't try again if we failed...
70 static int first_time;
74 * Current CPU load, as percentage of CPU cycles not idle or
77 static int currentCPULoad;
79 static double agedCPULoad = -1;
82 * Current IO load, as percentage of CPU cycles blocked on IO.
84 static int currentIOLoad;
86 static double agedIOLoad = -1;
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_ERROR | GNUNET_ERROR_TYPE_BULK,
106 "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)));
126 * Update the currentCPU and currentIO load values.
128 * Before its first invocation the method initStatusCalls() must be called.
129 * If there is an error the method returns -1.
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. */
143 proc_stat = fopen ("/proc/stat", "r");
144 if (NULL == proc_stat)
145 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", "/proc/stat");
147 if (proc_stat != NULL)
149 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
150 static int have_last_cpu = GNUNET_NO;
153 unsigned long long user_read, system_read, nice_read, idle_read,
155 unsigned long long user, system, nice, idle, iowait;
156 unsigned long long usage_time = 0, total_time = 1;
158 /* Get the first line with the data */
159 memset (line, 0, sizeof (line));
162 if (NULL == fgets (line, sizeof (line), proc_stat))
164 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
165 GNUNET_ERROR_TYPE_BULK,
166 "fgets", "/proc/stat");
168 proc_stat = NULL; /* don't try again */
173 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
175 &system_read, &nice_read, &idle_read, &iowait_read);
178 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
179 GNUNET_ERROR_TYPE_BULK,
180 "fgets-sscanf", "/proc/stat");
182 proc_stat = NULL; /* don't try again */
183 have_last_cpu = GNUNET_NO;
187 /* Store the current usage */
188 user = user_read - last_cpu_results[0];
189 system = system_read - last_cpu_results[1];
190 nice = nice_read - last_cpu_results[2];
191 idle = idle_read - last_cpu_results[3];
192 iowait = iowait_read - last_cpu_results[4];
193 /* Calculate the % usage */
194 usage_time = user + system + nice;
195 total_time = usage_time + idle + iowait;
196 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
198 currentCPULoad = (int) (100L * usage_time / total_time);
200 currentIOLoad = (int) (100L * iowait / total_time);
202 currentIOLoad = -1; /* 2.4 kernel */
204 /* Store the values for the next calculation */
205 last_cpu_results[0] = user_read;
206 last_cpu_results[1] = system_read;
207 last_cpu_results[2] = nice_read;
208 last_cpu_results[3] = idle_read;
209 last_cpu_results[4] = iowait_read;
210 have_last_cpu = GNUNET_YES;
219 unsigned int cpu_count;
220 processor_cpu_load_info_t cpu_load;
221 mach_msg_type_number_t cpu_msg_count;
222 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
223 unsigned long long t_idle_all, t_total_all;
227 t_idle_all = t_total_all = 0;
228 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
230 (processor_info_array_t *) & cpu_load,
232 if (kret == KERN_SUCCESS)
234 for (i = 0; i < cpu_count; i++)
236 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
237 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
239 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
240 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
244 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
245 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
249 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
250 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
252 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
253 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
257 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
258 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
262 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
263 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
265 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
266 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
270 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
271 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
275 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
276 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
278 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
279 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
283 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
284 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
287 t_total = t_sys + t_user + t_nice + t_idle;
288 t_idle_all += t_idle;
289 t_total_all += t_total;
291 for (i = 0; i < cpu_count; i++)
293 for (j = 0; j < CPU_STATE_MAX; j++)
295 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
299 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
302 vm_deallocate (mach_task_self (),
303 (vm_address_t) cpu_load,
304 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
305 currentIOLoad = -1; /* There's no IO load meter on darwin */
310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
311 "host_processor_info failed.");
312 return GNUNET_SYSERR;
316 /* try kstat (Solaris only) */
317 #if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
319 static long long last_idlecount;
320 static long long last_totalcount;
321 static int kstat_once; /* if open fails, don't keep
326 long long totalcount;
328 long long deltatotal;
335 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
342 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
346 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
348 if (khelper->ks_data_size > sizeof (cpu_stat_t))
349 continue; /* better save then sorry! */
350 if (-1 != kstat_read (kc, khelper, &stats))
352 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
354 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
355 stats.cpu_sysinfo.cpu[CPU_USER] +
356 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
357 stats.cpu_sysinfo.cpu[CPU_WAIT];
360 if (0 != kstat_close (kc))
361 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
363 if ((idlecount == 0) && (totalcount == 0))
364 goto ABORT_KSTAT; /* no stats found => abort */
365 deltaidle = idlecount - last_idlecount;
366 deltatotal = totalcount - last_totalcount;
367 if ((deltatotal > 0) && (last_totalcount > 0))
369 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
370 if (currentCPULoad > 100)
371 currentCPULoad = 100; /* odd */
372 if (currentCPULoad < 0)
373 currentCPULoad = 0; /* odd */
374 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
378 currentIOLoad = -1; /* FIXME-SOLARIS! */
379 last_idlecount = idlecount;
380 last_totalcount = totalcount;
383 kstat_once = 1; /* failed, don't try again */
384 return GNUNET_SYSERR;
388 /* insert methods better than getloadavg for
389 other platforms HERE! */
391 /* ok, maybe we have getloadavg on this platform */
394 static int warnOnce = 0;
396 if (1 != getloadavg (&loadavg, 1))
398 /* only warn once, if there is a problem with
399 getloadavg, we're going to hit it frequently... */
403 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getloadavg");
405 return GNUNET_SYSERR;
409 /* success with getloadavg */
410 currentCPULoad = (int) (100 * loadavg);
411 currentIOLoad = -1; /* FIXME */
419 if (GNNtQuerySystemInformation)
421 static double dLastKernel;
422 static double dLastIdle;
423 static double dLastUser;
430 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
432 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
434 sizeof (theInfo), NULL) == NO_ERROR)
436 /* PORT-ME MINGW: Multi-processor? */
437 dKernel = Li2Double (theInfo.KernelTime);
438 dIdle = Li2Double (theInfo.IdleTime);
439 dUser = Li2Double (theInfo.UserTime);
440 dDiffKernel = dKernel - dLastKernel;
441 dDiffIdle = dIdle - dLastIdle;
442 dDiffUser = dUser - dLastUser;
444 if (((dDiffKernel + dDiffUser) > 0) &&
445 (dLastIdle + dLastKernel + dLastUser > 0))
447 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
449 currentCPULoad = -1; /* don't know (yet) */
451 dLastKernel = dKernel;
455 currentIOLoad = -1; /* FIXME-MINGW */
460 /* only warn once, if there is a problem with
461 NtQuery..., we're going to hit it frequently... */
466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
467 _("Cannot query the CPU usage (Windows NT).\n"));
469 return GNUNET_SYSERR;
475 DWORD dwDataSize, dwType, dwDummy;
478 if (RegOpenKeyEx (HKEY_DYN_DATA,
479 "PerfStats\\StartSrv",
480 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
487 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
488 _("Cannot query the CPU usage (Win 9x)\n"));
492 RegOpenKeyEx (HKEY_DYN_DATA,
493 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
494 dwDataSize = sizeof (dwDummy);
495 RegQueryValueEx (hKey,
497 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
501 RegOpenKeyEx (HKEY_DYN_DATA,
502 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
503 dwDataSize = sizeof (currentCPULoad);
504 RegQueryValueEx (hKey,
506 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
508 currentIOLoad = -1; /* FIXME-MINGW! */
511 RegOpenKeyEx (HKEY_DYN_DATA,
512 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
513 RegOpenKeyEx (HKEY_DYN_DATA,
514 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
515 dwDataSize = sizeof (dwDummy);
516 RegQueryValueEx (hKey,
518 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
525 /* loadaverage not defined and no platform
526 specific alternative defined
529 return GNUNET_SYSERR;
533 * Update load values (if enough time has expired),
534 * including computation of averages. Code assumes
535 * that lock has already been obtained.
538 updateAgedLoad (const struct GNUNET_CONFIGURATION_Handle *cfg)
540 static struct GNUNET_TIME_Absolute lastCall;
542 if ((agedCPULoad == -1)
543 || (GNUNET_TIME_absolute_get_duration (lastCall).value > 500))
545 /* use smoothing, but do NOT update lastRet at frequencies higher
546 than 500ms; this makes the smoothing (mostly) independent from
547 the frequency at which getCPULoad is called (and we don't spend
548 more time measuring CPU than actually computing something). */
549 lastCall = GNUNET_TIME_absolute_get ();
551 if (currentCPULoad == -1)
557 if (agedCPULoad == -1)
559 agedCPULoad = currentCPULoad;
563 /* for CPU, we don't do the 'fast increase' since CPU is much
564 more jitterish to begin with */
565 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
568 if (currentIOLoad == -1)
574 if (agedIOLoad == -1)
576 agedIOLoad = currentIOLoad;
580 /* for IO, we don't do the 'fast increase' since IO is much
581 more jitterish to begin with */
582 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
589 * Get the load of the CPU relative to what is allowed.
590 * @return the CPU load as a percentage of allowed
591 * (100 is equivalent to full load)
594 GNUNET_OS_load_cpu_get (const struct GNUNET_CONFIGURATION_Handle *cfg)
596 unsigned long long maxCPULoad;
599 updateAgedLoad (cfg);
604 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXCPULOAD",
606 return GNUNET_SYSERR;
609 return (100 * ret) / maxCPULoad;
614 * Get the load of the CPU relative to what is allowed.
615 * @return the CPU load as a percentage of allowed
616 * (100 is equivalent to full load)
619 GNUNET_OS_load_disk_get (const struct GNUNET_CONFIGURATION_Handle *cfg)
621 unsigned long long maxIOLoad;
624 updateAgedLoad (cfg);
629 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXIOLOAD",
631 return GNUNET_SYSERR;
634 return (100 * ret) / maxIOLoad;
638 * The following method is called in order to initialize the status calls
639 * routines. After that it is safe to call each of the status calls separately
640 * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit).
642 void __attribute__ ((constructor)) GNUNET_cpustats_ltdl_init ()
645 updateUsage (); /* initialize */
646 /* Most GNUnet processes don't really need this, so close the FD, but allow
647 re-opening it (unless we failed) */
648 if (proc_stat != NULL)
650 GNUNET_break (0 == fclose (proc_stat));
654 #elif defined(DARWIN)
656 updateUsage (); /* initialize */
659 updateUsage (); /* initialize */
661 updateUsage (); /* initialize */
666 * Shutdown the status calls module.
668 void __attribute__ ((destructor)) GNUNET_cpustats_ltdl_fini ()
671 if (proc_stat != NULL)
676 #elif defined(DARWIN)
677 GNUNET_free_non_null (prev_cpu_load);
684 /* end of os_load.c */