root/src/ftdm_cpu_monitor.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftdm_cpu_read_stats
  2. FT_DECLARE
  3. FT_DECLARE
  4. FT_DECLARE
  5. FT_DECLARE
  6. FT_DECLARE

   1 /*
   2  * Copyright (c) 2010, Sangoma Technologies
   3  * Moises Silva <moy@sangoma.com>
   4  * All rights reserved.
   5  * 
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 
  10  * * Redistributions of source code must retain the above copyright
  11  * notice, this list of conditions and the following disclaimer.
  12  * 
  13  * * Redistributions in binary form must reproduce the above copyright
  14  * notice, this list of conditions and the following disclaimer in the
  15  * documentation and/or other materials provided with the distribution.
  16  * 
  17  * * Neither the name of the original author; nor the names of any contributors
  18  * may be used to endorse or promote products derived from this software
  19  * without specific prior written permission.
  20  * 
  21  * 
  22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  *
  34  * Contributors:
  35  * David Yat Sin <dyatsin@sangoma.com>
  36  * 
  37  */
  38 
  39 #ifdef WIN32
  40 #define _WIN32_WINNT 0x0501 // To make GetSystemTimes visible in windows.h
  41 #include <windows.h>
  42 #else /* LINUX */
  43 
  44 #include <stdio.h>
  45 #include <string.h>
  46 #include <stdlib.h>
  47 #include <unistd.h>
  48 #include <sys/types.h>
  49 #include <fcntl.h>
  50 #include <errno.h>
  51 #endif
  52 
  53 #include "private/ftdm_core.h"
  54 #include "ftdm_cpu_monitor.h"
  55 struct ftdm_cpu_monitor_stats
  56 {
  57         /* bool, just used to retrieve the values for the first time and not calculate the percentage of idle time */
  58         int valid_last_times;
  59 
  60         /* last calculated percentage of idle time */
  61         double last_percentage_of_idle_time;
  62 
  63 #ifdef __linux__
  64         /* the cpu feature gets disabled on errors */
  65         int disabled;
  66 
  67         /* all of these are the Linux jiffies last retrieved count */
  68         unsigned long long last_user_time;
  69         unsigned long long last_system_time;
  70         unsigned long long last_idle_time;
  71 
  72         unsigned long long last_nice_time;
  73         unsigned long long last_irq_time;
  74         unsigned long long last_soft_irq_time;
  75         unsigned long long last_io_wait_time;
  76         unsigned long long last_steal_time;
  77 
  78         /* /proc/stat file descriptor used to retrieve the counters */
  79         int procfd;
  80         int initd;
  81 #elif defined (WIN32)  || defined (WIN64)
  82         __int64 i64LastUserTime;
  83         __int64 i64LastKernelTime;
  84         __int64 i64LastIdleTime;
  85 #else
  86 /* Unsupported */
  87 #endif
  88 };
  89 
  90 #ifdef __linux__
  91 static ftdm_status_t ftdm_cpu_read_stats(struct ftdm_cpu_monitor_stats *p,
  92                                                                                         unsigned long long *user,
  93                                                                                         unsigned long long *nice,
  94                                                                                         unsigned long long *system,
  95                                                                                         unsigned long long *idle,
  96                                                                                         unsigned long long *iowait,
  97                                                                                         unsigned long long *irq,
  98                                                                                         unsigned long long *softirq,
  99                                                                                         unsigned long long *steal)
 100 {
 101 // the output of proc should not change that often from one kernel to other
 102 // see fs/proc/proc_misc.c or fs/proc/stat.c in the Linux kernel for more details
 103 // also man 5 proc is useful.
 104 #define CPU_ELEMENTS_1 7 // change this if you change the format string
 105 #define CPU_INFO_FORMAT_1 "cpu  %llu %llu %llu %llu %llu %llu %llu"
 106 
 107 #define CPU_ELEMENTS_2 8 // change this if you change the format string
 108 #define CPU_INFO_FORMAT_2 "cpu  %llu %llu %llu %llu %llu %llu %llu %llu"
 109 
 110 #define CPU_ELEMENTS_3 9 // change this if you change the format string
 111 #define CPU_INFO_FORMAT_3 "cpu  %llu %llu %llu %llu %llu %llu %llu %llu %llu"
 112 
 113         static const char procfile[] = "/proc/stat";
 114         int rc = 0;
 115         int myerrno = 0;
 116         int elements = 0;
 117         const char *cpustr = NULL;
 118         char statbuff[1024];
 119         unsigned long long guest = 0;
 120 
 121         if (!p->initd) {
 122                 p->procfd = open(procfile, O_RDONLY, 0);
 123                 if(p->procfd == -1) {
 124                         ftdm_log(FTDM_LOG_ERROR, "Failed to open CPU statistics file %s: %s\n", procfile, strerror(myerrno));
 125                         return FTDM_FAIL;
 126                 }
 127                 p->initd = 1;
 128         } else {
 129                 lseek(p->procfd, 0L, SEEK_SET);
 130         }
 131 
 132         rc = read(p->procfd, statbuff, sizeof(statbuff) - 1);
 133         if (rc <= 0) {
 134                 myerrno = errno;
 135                 ftdm_log(FTDM_LOG_ERROR, "Failed to read CPU statistics file %s: %s\n", procfile, strerror(myerrno));
 136                 return FTDM_FAIL;
 137         }
 138 
 139         cpustr = strstr(statbuff, "cpu ");
 140         if (!cpustr) {
 141                 ftdm_log(FTDM_LOG_ERROR, "wrong format for Linux proc cpu statistics: missing cpu string\n");
 142                 return FTDM_FAIL;
 143         }
 144 
 145         /* test each of the known formats starting from the bigger one */
 146         elements = sscanf(cpustr, CPU_INFO_FORMAT_3, user, nice, system, idle, iowait, irq, softirq, steal, &guest);
 147         if (elements == CPU_ELEMENTS_3) {
 148                 user += guest; /* guest operating system's run in user space */
 149                 return FTDM_SUCCESS;
 150         }
 151 
 152         elements = sscanf(cpustr, CPU_INFO_FORMAT_2, user, nice, system, idle, iowait, irq, softirq, steal);
 153         if (elements == CPU_ELEMENTS_2) {
 154                 return FTDM_SUCCESS;
 155         }
 156 
 157         elements = sscanf(cpustr, CPU_INFO_FORMAT_1, user, nice, system, idle, iowait, irq, softirq);
 158         if (elements == CPU_ELEMENTS_1) {
 159                 *steal = 0;
 160                 return FTDM_SUCCESS;
 161         }
 162 
 163         ftdm_log(FTDM_LOG_ERROR, "Unexpected format for Linux proc cpu statistics:%s\n", cpustr);
 164         return FTDM_FAIL;
 165 }
 166 #endif
 167 
 168 #ifdef __linux__
 169 FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time (struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
 170 {
 171         unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
 172         unsigned long long usertime, kerneltime, idletime, totaltime, halftime;
 173 
 174         *idle_percentage = 100.0;
 175         if (p->disabled) {
 176                 return FTDM_FAIL;
 177         }
 178         if (ftdm_cpu_read_stats(p, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal)) {
 179                 ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve Linux CPU statistics - disabling cpu monitor\n");
 180                 p->disabled = 1;
 181                 return FTDM_FAIL;
 182         }
 183 
 184         if (!p->valid_last_times) {
 185                 // we dont strictly need to save all of them but I feel code is more clear if we do
 186                 p->valid_last_times = 1;
 187                 p->last_user_time = user;
 188                 p->last_nice_time = nice;
 189                 p->last_system_time = system;
 190                 p->last_irq_time = irq;
 191                 p->last_soft_irq_time = softirq;
 192                 p->last_io_wait_time = iowait;
 193                 p->last_steal_time = steal;
 194                 p->last_idle_time = idle;
 195                 p->last_percentage_of_idle_time = 100.0;
 196                 *idle_percentage = p->last_percentage_of_idle_time;
 197                 return FTDM_SUCCESS;
 198         }
 199 
 200         usertime = (user - p->last_user_time) + (nice - p->last_nice_time);
 201         kerneltime = (system - p->last_system_time) + (irq - p->last_irq_time) + (softirq - p->last_soft_irq_time);
 202         kerneltime += (iowait - p->last_io_wait_time);
 203         kerneltime += (steal - p->last_steal_time);
 204         idletime = (idle - p->last_idle_time);
 205 
 206         totaltime = usertime + kerneltime + idletime;
 207         
 208         if (totaltime <= 0) {
 209                 // this may happen if not enough time has elapsed and the jiffies counters are the same than the last time we checked
 210                 // jiffies depend on timer interrupts which depend on the number of HZ compile time setting of the kernel
 211                 // typical configs set HZ to 100 (that means, 100 jiffies updates per second, that is one each 10ms)
 212                 // avoid an arithmetic exception and return the same values
 213                 *idle_percentage = p->last_percentage_of_idle_time;
 214                 return FTDM_SUCCESS;
 215         }
 216 
 217         halftime = totaltime / 2UL;
 218 
 219         p->last_percentage_of_idle_time = ((100 * idletime + halftime) / totaltime);
 220         *idle_percentage = p->last_percentage_of_idle_time;
 221 
 222         p->last_user_time = user;
 223         p->last_nice_time = nice;
 224         p->last_system_time = system;
 225         p->last_irq_time = irq;
 226         p->last_soft_irq_time = softirq;
 227         p->last_io_wait_time = iowait;
 228         p->last_steal_time = steal;
 229         p->last_idle_time = idle;
 230 
 231         return FTDM_SUCCESS;
 232 }
 233 
 234 #elif defined (__WINDOWS__)
 235 FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time(struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
 236 {
 237         FILETIME idleTime;
 238         FILETIME kernelTime;
 239         FILETIME userTime;
 240         int64_t i64UserTime;
 241         int64_t i64KernelTime;
 242         int64_t i64IdleTime;
 243   
 244         if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
 245                 return FTDM_FAIL;
 246         }
 247   
 248         i64UserTime = (int64_t)userTime.dwLowDateTime | ((int64_t)userTime.dwHighDateTime << 32);
 249 
 250         i64KernelTime = (int64_t)kernelTime.dwLowDateTime | ((int64_t)kernelTime.dwHighDateTime << 32);
 251 
 252         i64IdleTime = (int64_t)idleTime.dwLowDateTime | ((int64_t)idleTime.dwHighDateTime << 32);
 253 
 254         if (p->valid_last_times) {
 255                 int64_t i64User = i64UserTime - p->i64LastUserTime;
 256                 int64_t i64Kernel = i64KernelTime - p->i64LastKernelTime;
 257                 int64_t i64Idle = i64IdleTime - p->i64LastIdleTime;
 258                 int64_t i64System = i64User + i64Kernel;
 259                 *idle_percentage = 100.0 * i64Idle / i64System;
 260         } else {
 261                 *idle_percentage = 100.0;
 262                 p->valid_last_times = 1;
 263         }
 264 
 265         /* Remember current value for the next call */
 266         p->i64LastUserTime = i64UserTime;
 267         p->i64LastKernelTime = i64KernelTime;
 268         p->i64LastIdleTime = i64IdleTime;
 269 
 270         /* Success */
 271         return FTDM_SUCCESS;
 272 }
 273 #else
 274 /* Unsupported */
 275 FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time(struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
 276 {
 277         *idle_percentage = 100.0;
 278         return FTDM_FAIL;
 279 }
 280 #endif
 281 
 282 FT_DECLARE(struct ftdm_cpu_monitor_stats*) ftdm_new_cpu_monitor(void)
 283 {
 284         return calloc(1, sizeof(struct ftdm_cpu_monitor_stats));
 285 }
 286 
 287 FT_DECLARE(void) ftdm_delete_cpu_monitor(struct ftdm_cpu_monitor_stats *p)
 288 {
 289 #ifdef __linux__
 290         close(p->procfd);
 291 #endif
 292         free(p);
 293 }
 294 
 295 
 296 /* For Emacs:
 297  * Local Variables:
 298  * mode:c
 299  * indent-tabs-mode:t
 300  * tab-width:4
 301  * c-basic-offset:4
 302  * End:
 303  * For VIM:
 304  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 305  */

/* [<][>][^][v][top][bottom][index][help] */