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