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