aboutsummaryrefslogtreecommitdiff
path: root/src/util/os_load.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/os_load.c')
-rw-r--r--src/util/os_load.c653
1 files changed, 653 insertions, 0 deletions
diff --git a/src/util/os_load.c b/src/util/os_load.c
new file mode 100644
index 000000000..d1115e33f
--- /dev/null
+++ b/src/util/os_load.c
@@ -0,0 +1,653 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20
21/**
22 * @file util/os_load_cpu.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 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_os_lib.h"
33#include "gnunet_strings_lib.h"
34
35#if SOLARIS
36#if HAVE_KSTAT_H
37#include <kstat.h>
38#endif
39#if HAVE_SYS_SYSINFO_H
40#include <sys/sysinfo.h>
41#endif
42#if HAVE_KVM_H
43#include <kvm.h>
44#endif
45#endif
46#if SOMEBSD
47#if HAVE_KVM_H
48#include <kvm.h>
49#endif
50#endif
51
52#ifdef OSX
53#include <mach/mach.h>
54
55static processor_cpu_load_info_t prev_cpu_load;
56#endif
57
58#define DEBUG_STATUSCALLS GNUNET_NO
59
60#ifdef LINUX
61static FILE *proc_stat;
62#endif
63
64/**
65 * Current CPU load, as percentage of CPU cycles not idle or
66 * blocked on IO.
67 */
68static int currentCPULoad;
69
70static double agedCPULoad = -1;
71
72/**
73 * Current IO load, as percentage of CPU cycles blocked on IO.
74 */
75static int currentIOLoad;
76
77static double agedIOLoad = -1;
78
79#ifdef OSX
80static int
81initMachCpuStats ()
82{
83 unsigned int cpu_count;
84 processor_cpu_load_info_t cpu_load;
85 mach_msg_type_number_t cpu_msg_count;
86 kern_return_t kret;
87 int i, j;
88
89 kret = host_processor_info (mach_host_self (),
90 PROCESSOR_CPU_LOAD_INFO,
91 &cpu_count,
92 (processor_info_array_t *) & cpu_load,
93 &cpu_msg_count);
94 if (kret != KERN_SUCCESS)
95 {
96 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
97 "host_processor_info failed.");
98 return GNUNET_SYSERR;
99 }
100 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
101 for (i = 0; i < cpu_count; i++)
102 {
103 for (j = 0; j < CPU_STATE_MAX; j++)
104 {
105 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
106 }
107 }
108 vm_deallocate (mach_task_self (),
109 (vm_address_t) cpu_load,
110 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
111 return GNUNET_OK;
112}
113#endif
114
115/**
116 * Update the currentCPU and currentIO load values.
117 *
118 * Before its first invocation the method initStatusCalls() must be called.
119 * If there is an error the method returns -1.
120 */
121static int
122updateUsage ()
123{
124 currentIOLoad = -1;
125 currentCPULoad = -1;
126#ifdef LINUX
127 /* under linux, first try %idle/usage using /proc/stat;
128 if that does not work, disable /proc/stat for the future
129 by closing the file and use the next-best method. */
130 if (proc_stat != NULL)
131 {
132 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
133 static int have_last_cpu = GNUNET_NO;
134 int ret;
135 char line[256];
136 unsigned long long user_read, system_read, nice_read, idle_read,
137 iowait_read;
138 unsigned long long user, system, nice, idle, iowait;
139 unsigned long long usage_time = 0, total_time = 1;
140
141 /* Get the first line with the data */
142 rewind (proc_stat);
143 fflush (proc_stat);
144 if (NULL == fgets (line, 256, proc_stat))
145 {
146 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
147 GNUNET_ERROR_TYPE_BULK,
148 "fgets", "/proc/stat");
149 fclose (proc_stat);
150 proc_stat = NULL; /* don't try again */
151 }
152 else
153 {
154 iowait_read = 0;
155 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
156 &user_read,
157 &system_read, &nice_read, &idle_read, &iowait_read);
158 if (ret < 4)
159 {
160 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
161 GNUNET_ERROR_TYPE_BULK,
162 "fgets-sscanf", "/proc/stat");
163 fclose (proc_stat);
164 proc_stat = NULL; /* don't try again */
165 have_last_cpu = GNUNET_NO;
166 }
167 else
168 {
169 /* Store the current usage */
170 user = user_read - last_cpu_results[0];
171 system = system_read - last_cpu_results[1];
172 nice = nice_read - last_cpu_results[2];
173 idle = idle_read - last_cpu_results[3];
174 iowait = iowait_read - last_cpu_results[4];
175 /* Calculate the % usage */
176 usage_time = user + system + nice;
177 total_time = usage_time + idle + iowait;
178 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
179 {
180 currentCPULoad = (int) (100L * usage_time / total_time);
181 if (ret > 4)
182 currentIOLoad = (int) (100L * iowait / total_time);
183 else
184 currentIOLoad = -1; /* 2.4 kernel */
185 }
186 /* Store the values for the next calculation */
187 last_cpu_results[0] = user_read;
188 last_cpu_results[1] = system_read;
189 last_cpu_results[2] = nice_read;
190 last_cpu_results[3] = idle_read;
191 last_cpu_results[4] = iowait_read;
192 have_last_cpu = GNUNET_YES;
193 return GNUNET_OK;
194 }
195 }
196 }
197#endif
198
199#ifdef OSX
200 {
201 unsigned int cpu_count;
202 processor_cpu_load_info_t cpu_load;
203 mach_msg_type_number_t cpu_msg_count;
204 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
205 unsigned long long t_idle_all, t_total_all;
206 kern_return_t kret;
207 int i, j;
208
209 t_idle_all = t_total_all = 0;
210 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
211 &cpu_count,
212 (processor_info_array_t *) & cpu_load,
213 &cpu_msg_count);
214 if (kret == KERN_SUCCESS)
215 {
216 for (i = 0; i < cpu_count; i++)
217 {
218 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
219 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
220 {
221 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
222 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
223 }
224 else
225 {
226 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
227 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
228 1);
229 }
230
231 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
232 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
233 {
234 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
235 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
236 }
237 else
238 {
239 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
240 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
241 1);
242 }
243
244 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
245 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
246 {
247 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
248 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
249 }
250 else
251 {
252 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
253 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
254 1);
255 }
256
257 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
258 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
259 {
260 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
261 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
262 }
263 else
264 {
265 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
266 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
267 1);
268 }
269 t_total = t_sys + t_user + t_nice + t_idle;
270 t_idle_all += t_idle;
271 t_total_all += t_total;
272 }
273 for (i = 0; i < cpu_count; i++)
274 {
275 for (j = 0; j < CPU_STATE_MAX; j++)
276 {
277 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
278 }
279 }
280 if (t_total_all > 0)
281 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
282 else
283 currentCPULoad = -1;
284 vm_deallocate (mach_task_self (),
285 (vm_address_t) cpu_load,
286 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
287 currentIOLoad = -1; /* FIXME-OSX! */
288 return GNUNET_OK;
289 }
290 else
291 {
292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
293 "host_processor_info failed.");
294 return GNUNET_SYSERR;
295 }
296 }
297#endif
298 /* try kstat (Solaris only) */
299#if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
300 {
301 static long long last_idlecount;
302 static long long last_totalcount;
303 static int kstat_once; /* if open fails, don't keep
304 trying */
305 kstat_ctl_t *kc;
306 kstat_t *khelper;
307 long long idlecount;
308 long long totalcount;
309 long long deltaidle;
310 long long deltatotal;
311
312 if (kstat_once == 1)
313 goto ABORT_KSTAT;
314 kc = kstat_open ();
315 if (kc == NULL)
316 {
317 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
318 "kstat_open");
319 goto ABORT_KSTAT;
320 }
321
322 idlecount = 0;
323 totalcount = 0;
324 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
325 {
326 cpu_stat_t stats;
327
328 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
329 continue;
330 if (khelper->ks_data_size > sizeof (cpu_stat_t))
331 continue; /* better save then sorry! */
332 if (-1 != kstat_read (kc, khelper, &stats))
333 {
334 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
335 totalcount
336 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
337 stats.cpu_sysinfo.cpu[CPU_USER] +
338 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
339 stats.cpu_sysinfo.cpu[CPU_WAIT];
340 }
341 }
342 if (0 != kstat_close (kc))
343 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
344 "kstat_close");
345 if ((idlecount == 0) && (totalcount == 0))
346 goto ABORT_KSTAT; /* no stats found => abort */
347 deltaidle = idlecount - last_idlecount;
348 deltatotal = totalcount - last_totalcount;
349 if ((deltatotal > 0) && (last_totalcount > 0))
350 {
351 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
352 if (currentCPULoad > 100)
353 currentCPULoad = 100; /* odd */
354 if (currentCPULoad < 0)
355 currentCPULoad = 0; /* odd */
356 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
357 }
358 else
359 currentCPULoad = -1;
360 currentIOLoad = -1; /* FIXME-SOLARIS! */
361 last_idlecount = idlecount;
362 last_totalcount = totalcount;
363 return GNUNET_OK;
364 ABORT_KSTAT:
365 kstat_once = 1; /* failed, don't try again */
366 return GNUNET_SYSERR;
367 }
368#endif
369
370 /* insert methods better than getloadavg for
371 other platforms HERE! */
372
373 /* ok, maybe we have getloadavg on this platform */
374#if HAVE_GETLOADAVG
375 {
376 static int warnOnce = 0;
377 double loadavg;
378 if (1 != getloadavg (&loadavg, 1))
379 {
380 /* only warn once, if there is a problem with
381 getloadavg, we're going to hit it frequently... */
382 if (warnOnce == 0)
383 {
384 warnOnce = 1;
385 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getloadavg");
386 }
387 return GNUNET_SYSERR;
388 }
389 else
390 {
391 /* success with getloadavg */
392 currentCPULoad = (int) (100 * loadavg);
393 currentIOLoad = -1; /* FIXME */
394 return GNUNET_OK;
395 }
396 }
397#endif
398
399#if MINGW
400 /* Win NT? */
401 if (GNNtQuerySystemInformation)
402 {
403 static double dLastKernel;
404 static double dLastIdle;
405 static double dLastUser;
406 double dKernel;
407 double dIdle;
408 double dUser;
409 double dDiffKernel;
410 double dDiffIdle;
411 double dDiffUser;
412 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
413
414 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
415 &theInfo,
416 sizeof (theInfo), NULL) == NO_ERROR)
417 {
418 /* PORT-ME MINGW: Multi-processor? */
419 dKernel = Li2Double (theInfo.KernelTime);
420 dIdle = Li2Double (theInfo.IdleTime);
421 dUser = Li2Double (theInfo.UserTime);
422 dDiffKernel = dKernel - dLastKernel;
423 dDiffIdle = dIdle - dLastIdle;
424 dDiffUser = dUser - dLastUser;
425
426 if (((dDiffKernel + dDiffUser) > 0) &&
427 (dLastIdle + dLastKernel + dLastUser > 0))
428 currentCPULoad =
429 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
430 else
431 currentCPULoad = -1; /* don't know (yet) */
432
433 dLastKernel = dKernel;
434 dLastIdle = dIdle;
435 dLastUser = dUser;
436
437 currentIOLoad = -1; /* FIXME-MINGW */
438 return GNUNET_OK;
439 }
440 else
441 {
442 /* only warn once, if there is a problem with
443 NtQuery..., we're going to hit it frequently... */
444 static int once;
445 if (once == 0)
446 {
447 once = 1;
448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
449 _("Cannot query the CPU usage (Windows NT).\n"));
450 }
451 return GNUNET_SYSERR;
452 }
453 }
454 else
455 { /* Win 9x */
456 HKEY hKey;
457 DWORD dwDataSize, dwType, dwDummy;
458
459 /* Start query */
460 if (RegOpenKeyEx (HKEY_DYN_DATA,
461 "PerfStats\\StartSrv",
462 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
463 {
464 /* only warn once */
465 static int once = 0;
466 if (once == 0)
467 {
468 once = 1;
469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
470 _("Cannot query the CPU usage (Win 9x)\n"));
471 }
472 }
473
474 RegOpenKeyEx (HKEY_DYN_DATA,
475 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
476 dwDataSize = sizeof (dwDummy);
477 RegQueryValueEx (hKey,
478 "KERNEL\\CPUUsage",
479 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
480 RegCloseKey (hKey);
481
482 /* Get CPU usage */
483 RegOpenKeyEx (HKEY_DYN_DATA,
484 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
485 dwDataSize = sizeof (currentCPULoad);
486 RegQueryValueEx (hKey,
487 "KERNEL\\CPUUsage",
488 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
489 RegCloseKey (hKey);
490 currentIOLoad = -1; /* FIXME-MINGW! */
491
492 /* Stop query */
493 RegOpenKeyEx (HKEY_DYN_DATA,
494 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
495 RegOpenKeyEx (HKEY_DYN_DATA,
496 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
497 dwDataSize = sizeof (dwDummy);
498 RegQueryValueEx (hKey,
499 "KERNEL\\CPUUsage",
500 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
501 RegCloseKey (hKey);
502
503 return GNUNET_OK;
504 }
505#endif
506
507 /* loadaverage not defined and no platform
508 specific alternative defined
509 => default: error
510 */
511 return GNUNET_SYSERR;
512}
513
514/**
515 * Update load values (if enough time has expired),
516 * including computation of averages. Code assumes
517 * that lock has already been obtained.
518 */
519static void
520updateAgedLoad (struct GNUNET_CONFIGURATION_Handle *cfg)
521{
522 static struct GNUNET_TIME_Absolute lastCall;
523
524 if ((agedCPULoad == -1)
525 || (GNUNET_TIME_absolute_get_duration (lastCall).value > 500))
526 {
527 /* use smoothing, but do NOT update lastRet at frequencies higher
528 than 500ms; this makes the smoothing (mostly) independent from
529 the frequency at which getCPULoad is called (and we don't spend
530 more time measuring CPU than actually computing something). */
531 lastCall = GNUNET_TIME_absolute_get ();
532 updateUsage ();
533 if (currentCPULoad == -1)
534 {
535 agedCPULoad = -1;
536 }
537 else
538 {
539 if (agedCPULoad == -1)
540 {
541 agedCPULoad = currentCPULoad;
542 }
543 else
544 {
545 /* for CPU, we don't do the 'fast increase' since CPU is much
546 more jitterish to begin with */
547 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
548 }
549 }
550 if (currentIOLoad == -1)
551 {
552 agedIOLoad = -1;
553 }
554 else
555 {
556 if (agedIOLoad == -1)
557 {
558 agedIOLoad = currentIOLoad;
559 }
560 else
561 {
562 /* for IO, we don't do the 'fast increase' since IO is much
563 more jitterish to begin with */
564 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
565 }
566 }
567 }
568}
569
570/**
571 * Get the load of the CPU relative to what is allowed.
572 * @return the CPU load as a percentage of allowed
573 * (100 is equivalent to full load)
574 */
575int
576GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg)
577{
578 unsigned long long maxCPULoad;
579 int ret;
580
581 updateAgedLoad (cfg);
582 ret = agedCPULoad;
583 if (ret == -1)
584 return -1;
585 if (GNUNET_OK !=
586 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXCPULOAD",
587 &maxCPULoad))
588 return GNUNET_SYSERR;
589 return (100 * ret) / maxCPULoad;
590}
591
592
593/**
594 * Get the load of the CPU relative to what is allowed.
595 * @return the CPU load as a percentage of allowed
596 * (100 is equivalent to full load)
597 */
598int
599GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg)
600{
601 unsigned long long maxIOLoad;
602 int ret;
603
604 updateAgedLoad (cfg);
605 ret = agedIOLoad;
606 if (ret == -1)
607 return -1;
608 if (-1 ==
609 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXIOLOAD",
610 &maxIOLoad))
611 return GNUNET_SYSERR;
612 return (100 * ret) / maxIOLoad;
613}
614
615/**
616 * The following method is called in order to initialize the status calls
617 * routines. After that it is safe to call each of the status calls separately
618 * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit).
619 */
620void __attribute__ ((constructor)) GNUNET_cpustats_ltdl_init ()
621{
622#ifdef LINUX
623 proc_stat = fopen ("/proc/stat", "r");
624 if (NULL == proc_stat)
625 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", "/proc/stat");
626#elif OSX
627 initMachCpuStats ();
628#elif MINGW
629 InitWinEnv (NULL);
630#endif
631 updateUsage (); /* initialize */
632}
633
634/**
635 * Shutdown the status calls module.
636 */
637void __attribute__ ((destructor)) GNUNET_cpustats_ltdl_fini ()
638{
639#ifdef LINUX
640 if (proc_stat != NULL)
641 {
642 fclose (proc_stat);
643 proc_stat = NULL;
644 }
645#elif OSX
646 GNUNET_free_non_null (prev_cpu_load);
647#elif MINGW
648 ShutdownWinEnv ();
649#endif
650}
651
652
653/* end of os_load_cpu.c */