bugfix
[oweals/gnunet.git] / src / util / os_load.c
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
55 static processor_cpu_load_info_t prev_cpu_load;
56 #endif
57
58 #define DEBUG_STATUSCALLS GNUNET_NO
59
60 #ifdef LINUX
61 static FILE *proc_stat;
62 #endif
63
64 /**
65  * Current CPU load, as percentage of CPU cycles not idle or
66  * blocked on IO.
67  */
68 static int currentCPULoad;
69
70 static double agedCPULoad = -1;
71
72 /**
73  * Current IO load, as percentage of CPU cycles blocked on IO.
74  */
75 static int currentIOLoad;
76
77 static double agedIOLoad = -1;
78
79 #ifdef OSX
80 static int
81 initMachCpuStats ()
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  */
121 static int
122 updateUsage ()
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  */
519 static void
520 updateAgedLoad (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  */
575 int
576 GNUNET_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  */
598 int
599 GNUNET_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  */
620 void __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  */
637 void __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 */