FreeSWITCH API Documentation  1.7.0
switch_profile.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, 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 #include "switch.h"
36 
37 #ifdef __linux__
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #endif
46 
48 {
49  /* bool, just used to retrieve the values for the first time and not calculate the percentage of idle time */
51 
52  /* last calculated percentage of idle time */
55  unsigned int last_idle_time_index;
57 
58 #ifdef __linux__
59  /* the cpu feature gets disabled on errors */
60  int disabled;
61  /* all of these are the Linux jiffies last retrieved count */
62  unsigned long long last_user_time;
63  unsigned long long last_system_time;
64  unsigned long long last_idle_time;
65 
66  unsigned long long last_nice_time;
67  unsigned long long last_irq_time;
68  unsigned long long last_soft_irq_time;
69  unsigned long long last_io_wait_time;
70  unsigned long long last_steal_time;
71 
72  /* /proc/stat file descriptor used to retrieve the counters */
73  int procfd;
74  int initd;
75 #elif defined (WIN32) || defined (WIN64)
76  __int64 i64LastUserTime;
77  __int64 i64LastKernelTime;
78  __int64 i64LastIdleTime;
79 #else
80  /* Unsupported */
81 #endif
82 };
83 
84 #ifdef __linux__
85 static int read_cpu_stats(switch_profile_timer_t *p,
86  unsigned long long *user,
87  unsigned long long *nice,
88  unsigned long long *system,
89  unsigned long long *idle,
90  unsigned long long *iowait,
91  unsigned long long *irq,
92  unsigned long long *softirq,
93  unsigned long long *steal)
94 {
95 // the output of proc should not change that often from one kernel to other
96 // see fs/proc/proc_misc.c or fs/proc/stat.c in the Linux kernel for more details
97 // also man 5 proc is useful.
98 #define CPU_ELEMENTS_1 7 // change this if you change the format string
99 #define CPU_INFO_FORMAT_1 "cpu %llu %llu %llu %llu %llu %llu %llu"
100 
101 #define CPU_ELEMENTS_2 8 // change this if you change the format string
102 #define CPU_INFO_FORMAT_2 "cpu %llu %llu %llu %llu %llu %llu %llu %llu"
103 
104 #define CPU_ELEMENTS_3 9 // change this if you change the format string
105 #define CPU_INFO_FORMAT_3 "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
106  static const char procfile[] = "/proc/stat";
107  int rc = 0;
108  int myerrno = 0;
109  int elements = 0;
110  const char *cpustr = NULL;
111  char statbuff[1024];
112  unsigned long long guest = 0;
113 
114  if (!p->initd) {
115  p->procfd = open(procfile, O_RDONLY, 0);
116  if(p->procfd == -1) {
117  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to open CPU statistics file %s: %s\n", procfile, strerror(myerrno));
118  return -1;
119  }
120  p->initd = 1;
121  } else {
122  lseek(p->procfd, 0L, SEEK_SET);
123  }
124 
125  rc = read(p->procfd, statbuff, sizeof(statbuff) - 1);
126  if (rc <= 0) {
127  myerrno = errno;
128  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to read CPU statistics file %s: %s\n", procfile, strerror(myerrno));
129  return -1;
130  } else {
131  statbuff[rc] = '\0';
132  }
133 
134  cpustr = strstr(statbuff, "cpu ");
135  if (!cpustr) {
136  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "wrong format for Linux proc cpu statistics: missing cpu string\n");
137  return -1;
138  }
139 
140  /* test each of the known formats starting from the bigger one */
141  elements = sscanf(cpustr, CPU_INFO_FORMAT_3, user, nice, system, idle, iowait, irq, softirq, steal, &guest);
142  if (elements == CPU_ELEMENTS_3) {
143  *user += guest; /* guest operating system's run in user space */
144  return 0;
145  }
146 
147  elements = sscanf(cpustr, CPU_INFO_FORMAT_2, user, nice, system, idle, iowait, irq, softirq, steal);
148  if (elements == CPU_ELEMENTS_2) {
149  return 0;
150  }
151 
152  elements = sscanf(cpustr, CPU_INFO_FORMAT_1, user, nice, system, idle, iowait, irq, softirq);
153  if (elements == CPU_ELEMENTS_1) {
154  *steal = 0;
155  return 0;
156  }
157 
158  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unexpected format for Linux proc cpu statistics: %s\n", cpustr);
159  return -1;
160 }
161 
163 {
164  unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
165  unsigned long long usertime, kerneltime, idletime, totaltime, halftime;
166  int x;
167 
168  *idle_percentage = 100.0;
169  if (p->disabled) {
170  return SWITCH_FALSE;
171  }
172 
173  if (read_cpu_stats(p, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal)) {
174  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to retrieve Linux CPU statistics, disabling profile timer ...\n");
175  p->disabled = 1;
176  return SWITCH_FALSE;
177  }
178 
179  if (!p->valid_last_times) {
180  // we dont strictly need to save all of them but I feel code is more clear if we do
181  p->valid_last_times = 1;
182  p->last_user_time = user;
183  p->last_nice_time = nice;
184  p->last_system_time = system;
185  p->last_irq_time = irq;
186  p->last_soft_irq_time = softirq;
187  p->last_io_wait_time = iowait;
188  p->last_steal_time = steal;
189  p->last_idle_time = idle;
190  p->last_percentage_of_idle_time = 100.0;
191  *idle_percentage = p->last_percentage_of_idle_time;
192  return SWITCH_TRUE;
193  }
194 
195  usertime = (user - p->last_user_time) + (nice - p->last_nice_time);
196  kerneltime = (system - p->last_system_time) + (irq - p->last_irq_time) + (softirq - p->last_soft_irq_time);
197  kerneltime += (iowait - p->last_io_wait_time);
198  kerneltime += (steal - p->last_steal_time);
199  idletime = (idle - p->last_idle_time);
200 
201  totaltime = usertime + kerneltime + idletime;
202 
203  if (totaltime <= 0) {
204  // this may happen if not enough time has elapsed and the jiffies counters are the same than the last time we checked
205  // jiffies depend on timer interrupts which depend on the number of HZ compile time setting of the kernel
206  // typical configs set HZ to 100 (that means, 100 jiffies updates per second, that is one each 10ms)
207  // avoid an arithmetic exception and return the same values
208  *idle_percentage = p->last_percentage_of_idle_time;
209  return SWITCH_TRUE;
210  }
211 
212  halftime = totaltime / 2UL;
213 
214  p->last_idle_time_index += 1;
215  if ( p->last_idle_time_index >= p->cpu_idle_smoothing_depth ) {
216  p->last_idle_time_index = 0;
217  }
218  p->percentage_of_idle_time_ring[p->last_idle_time_index] = ((100 * idletime + halftime) / totaltime);
219 
220  p->last_percentage_of_idle_time = 0;
221  for ( x = 0; x < p->cpu_idle_smoothing_depth; x++ ) {
222  //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "IDLE TIME: (%d)[%lf]\n", x, p->percentage_of_idle_time_ring[x]);
223  p->last_percentage_of_idle_time += p->percentage_of_idle_time_ring[x];
224  }
225  p->last_percentage_of_idle_time /= p->cpu_idle_smoothing_depth;
226 
227  *idle_percentage = p->last_percentage_of_idle_time;
228  //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "IDLE TIME finalized: [%lf]\n", *idle_percentage);
229 
230  p->last_user_time = user;
231  p->last_nice_time = nice;
232  p->last_system_time = system;
233  p->last_irq_time = irq;
234  p->last_soft_irq_time = softirq;
235  p->last_io_wait_time = iowait;
236  p->last_steal_time = steal;
237  p->last_idle_time = idle;
238 
239  return SWITCH_TRUE;
240 }
241 
242 #elif defined (WIN32) || defined (WIN64)
243 
245 {
246  FILETIME idleTime;
247  FILETIME kernelTime;
248  FILETIME userTime;
249  __int64 i64UserTime, i64KernelTime, i64IdleTime;
250 
251  if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
252  return SWITCH_FALSE;
253  }
254 
255  i64UserTime = (__int64)userTime.dwLowDateTime | ((__int64)userTime.dwHighDateTime << 32);
256 
257  i64KernelTime = (__int64)kernelTime.dwLowDateTime | ((__int64)kernelTime.dwHighDateTime << 32);
258 
259  i64IdleTime = (__int64)idleTime.dwLowDateTime | ((__int64)idleTime.dwHighDateTime << 32);
260 
261  if (p->valid_last_times) {
262  __int64 i64User = i64UserTime - p->i64LastUserTime;
263  __int64 i64Kernel = i64KernelTime - p->i64LastKernelTime;
264  __int64 i64Idle = i64IdleTime - p->i64LastIdleTime;
265  __int64 i64System = i64User + i64Kernel;
266  unsigned int x;
267 
268  p->last_idle_time_index += 1;
269  if ( p->last_idle_time_index >= p->cpu_idle_smoothing_depth ) {
270  p->last_idle_time_index = 0;
271  }
272  p->percentage_of_idle_time_ring[p->last_idle_time_index] = 100.0 * i64Idle / i64System;
273 
274  *idle_percentage = 0;
275  for (x = 0; x < p->cpu_idle_smoothing_depth; x++ ) {
276  *idle_percentage += p->percentage_of_idle_time_ring[x];
277  }
278  *idle_percentage /= p->cpu_idle_smoothing_depth;
279  } else {
280  *idle_percentage = 100.0;
281  p->valid_last_times = 1;
282  }
283 
284  /* Remember current value for the next call */
285  p->i64LastUserTime = i64UserTime;
286  p->i64LastKernelTime = i64KernelTime;
287  p->i64LastIdleTime = i64IdleTime;
288 
289  /* Success */
290  return SWITCH_TRUE;
291 }
292 
293 #else
294 
295  /* Unsupported */
297 {
298  *idle_percentage = 100.0;
299  return SWITCH_FALSE;
300 }
301 
302 #endif
303 
305 {
306  unsigned int x;
307  switch_profile_timer_t *p = calloc(1, sizeof(switch_profile_timer_t));
308 
311  } else {
312  p->cpu_idle_smoothing_depth = 30;
313  }
314 
315  p->percentage_of_idle_time_ring = calloc(1, sizeof(double) * p->cpu_idle_smoothing_depth);
316 
317  for ( x = 0; x < p->cpu_idle_smoothing_depth; x++ ) {
318  p->percentage_of_idle_time_ring[x] = 100.0;
319  }
320 
321  return p;
322 }
323 
325 {
326  if (!p) return;
327 
328 #ifdef __linux__
329  close((*p)->procfd);
330 #endif
331  free((*p)->percentage_of_idle_time_ring);
332  free(*p);
333  *p = NULL;
334 }
335 
336 /* For Emacs:
337  * Local Variables:
338  * mode:c
339  * indent-tabs-mode:t
340  * tab-width:4
341  * c-basic-offset:4
342  * End:
343  * For VIM:
344  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
345  */
#define SWITCH_CHANNEL_LOG
switch_bool_t
Definition: switch_types.h:405
struct switch_runtime runtime
Definition: switch_core.c:64
switch_profile_timer_t * switch_new_profile_timer(void)
create a new profile timer
void switch_delete_profile_timer(switch_profile_timer_t **p)
Deletes profile timer.
unsigned int cpu_idle_smoothing_depth
double * percentage_of_idle_time_ring
double last_percentage_of_idle_time
unsigned int last_idle_time_index
Main Library Header.
#define SWITCH_DECLARE(type)
switch_bool_t switch_get_system_idle_time(switch_profile_timer_t *p, double *idle_percentage)
provides the percentage of idle system time
void switch_log_printf(_In_ switch_text_channel_t channel, _In_z_ const char *file, _In_z_ const char *func, _In_ int line, _In_opt_z_ const char *userdata, _In_ switch_log_level_t level, _In_z_ _Printf_format_string_ const char *fmt,...) PRINTF_FUNCTION(7
Write log data to the logging engine.
uint32_t cpu_idle_smoothing_depth