1 char   netcpu_procstat_id[]="\
2 @(#)netcpu_procstat.c (c) Copyright 2005-2012 Version 2.6.0";
3 
4 /* netcpu_procstat.c
5 
6    Implement the /proc/stat specific portions of netperf CPU
7    utilization measurements. These are broken-out into a separate file
8    to make life much nicer over in netlib.c which had become a maze of
9    twisty, CPU-util-related, #ifdefs, all different.  raj 2005-01-26
10    */
11 
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15 
16 #include <stdio.h>
17 
18 #ifdef HAVE_FCNTL_H
19 # include <fcntl.h>
20 #endif
21 #if HAVE_UNISTD_H
22 # include <unistd.h>
23 #endif
24 #if STDC_HEADERS
25 # include <stdlib.h>
26 # include <stddef.h>
27 #else
28 # if HAVE_STDLIB_H
29 #  include <stdlib.h>
30 # endif
31 #endif
32 
33 #include <string.h>
34 
35 #include "netsh.h"
36 #include "netlib.h"
37 
38 /* the lib_start_count and lib_end_count arrays hold the starting
39    and ending values of whatever is counting when the system is
40    idle. The rate at which this increments during a test is compared
41    with a previous calibrarion to arrive at a CPU utilization
42    percentage. raj 2005-01-26 */
43 
44 #define IDLE_IDX 4
45 #define CPU_STATES 10
46 
47 typedef struct cpu_states
48 {
49   uint64_t     	user;
50   uint64_t     	nice;
51   uint64_t     	sys;
52   uint64_t     	idle;
53   uint64_t     	iowait;
54   uint64_t     	hard_irq;
55   uint64_t     	soft_irq;
56   uint64_t     	steal;
57   uint64_t     	guest;
58   uint64_t     	guest_nice;
59 } cpu_states_t;
60 
61 static cpu_states_t  lib_start_count[MAXCPUS];
62 static cpu_states_t  lib_end_count[MAXCPUS];
63 
64 
65 /* The max. length of one line of /proc/stat cpu output */
66 #define CPU_LINE_LENGTH (int)((CPU_STATES * sizeof (long) / 3 + 1) * 4 + 8)
67 #define PROC_STAT_FILE_NAME "/proc/stat"
68 #define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr)
69 
70 static int proc_stat_fd = -1;
71 static char *proc_stat_buf = NULL;
72 static int proc_stat_buflen = 0;
73 
74 void
cpu_util_init(void)75 cpu_util_init(void)
76 {
77 
78   if (debug) {
79     fprintf(where,
80 	    "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n",
81 	    proc_stat_fd,
82 	    proc_stat_buf);
83     fflush(where);
84   }
85   if (proc_stat_fd < 0) {
86     proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
87     if (proc_stat_fd < 0) {
88       fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
89       exit (1);
90     };
91   };
92 
93   if (!proc_stat_buf) {
94     proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
95     if (debug) {
96       fprintf(where,
97 	      "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
98 	      lib_num_loc_cpus,
99 	      N_CPU_LINES(lib_num_loc_cpus),
100 	      CPU_LINE_LENGTH,
101 	      proc_stat_buflen);
102       fflush(where);
103     }
104     proc_stat_buf = (char *)malloc (proc_stat_buflen);
105     if (!proc_stat_buf) {
106       fprintf (stderr, "Cannot allocate buffer memory!\n");
107       exit (1);
108     }
109   }
110   return;
111 }
112 
113 void
cpu_util_terminate(void)114 cpu_util_terminate(void)
115 {
116   close(proc_stat_fd);
117   proc_stat_fd = -1;
118   free(proc_stat_buf);
119   proc_stat_buf = NULL;
120   return;
121 }
122 
123 int
get_cpu_method()124 get_cpu_method()
125 {
126   return PROC_STAT;
127 }
128 
129 float
calibrate_idle_rate(int iterations,int interval)130 calibrate_idle_rate (int iterations, int interval)
131 {
132   if (proc_stat_fd < 0) {
133     proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
134     if (proc_stat_fd < 0) {
135       fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
136       exit (1);
137     };
138   };
139 
140   if (!proc_stat_buf) {
141     proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
142     if (debug) {
143       fprintf(where,
144 	      "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
145 	      lib_num_loc_cpus,
146 	      N_CPU_LINES(lib_num_loc_cpus),
147 	      CPU_LINE_LENGTH,
148 	      proc_stat_buflen);
149       fflush(where);
150     }
151     proc_stat_buf = (char *)malloc (proc_stat_buflen);
152     if (!proc_stat_buf) {
153       fprintf (stderr, "Cannot allocate buffer memory!\n");
154       exit (1);
155     };
156   };
157 
158   return sysconf (_SC_CLK_TCK);
159 }
160 
161 static void
get_cpu(cpu_states_t * res)162 get_cpu (cpu_states_t *res)
163 {
164   int i;
165   int n = lib_num_loc_cpus;
166   char *p = proc_stat_buf;
167 
168   lseek (proc_stat_fd, 0, SEEK_SET);
169   read (proc_stat_fd, p, proc_stat_buflen);
170 
171   if (debug) {
172     fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p);
173     fflush(where);
174   }
175   /* Skip first line (total) on SMP */
176   if (n > 1) p = strchr (p, '\n');
177 
178   for (i = 0; i < n; i++) {
179     memset(&res[i], 0, sizeof (res[i]));
180     p = strchr (p, ' ');
181     sscanf(p, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
182 	   (unsigned long long *)&res[i].user,
183 	   (unsigned long long *)&res[i].nice,
184 	   (unsigned long long *)&res[i].sys,
185 	   (unsigned long long *)&res[i].idle,
186 	   (unsigned long long *)&res[i].iowait,
187 	   (unsigned long long *)&res[i].hard_irq,
188 	   (unsigned long long *)&res[i].soft_irq,
189 	   (unsigned long long *)&res[i].steal,
190 	   (unsigned long long *)&res[i].guest,
191            (unsigned long long *)&res[i].guest_nice);
192     if (debug) {
193       fprintf(where,
194 	      "res[%d] is %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
195 	      i,
196 	      (unsigned long long)res[i].user,
197 	      (unsigned long long)res[i].nice,
198 	      (unsigned long long)res[i].sys,
199 	      (unsigned long long)res[i].idle,
200 	      (unsigned long long)res[i].iowait,
201 	      (unsigned long long)res[i].hard_irq,
202 	      (unsigned long long)res[i].soft_irq,
203 	      (unsigned long long)res[i].steal,
204 	      (unsigned long long)res[i].guest,
205               (unsigned long long)res[i].guest_nice);
206       fflush(where);
207     }
208     p = strchr (p, '\n');
209   };
210 
211 }
212 
213 /* take the initial timestamp and start collecting CPU utilization if
214    requested */
215 
216 void
measure_cpu_start()217 measure_cpu_start()
218 {
219   cpu_method = PROC_STAT;
220   get_cpu(lib_start_count);
221 }
222 
223 /* collect final CPU utilization raw data */
224 void
measure_cpu_stop()225 measure_cpu_stop()
226 {
227   get_cpu(lib_end_count);
228 }
229 
230 static uint64_t
tick_subtract(uint64_t start,uint64_t end)231 tick_subtract(uint64_t start, uint64_t end)
232 {
233   if (end >= start || (start & 0xffffffff00000000ULL))
234     return (end - start);
235 
236   /*
237    *  We wrapped, and it is likely that the kernel is suppling 32-bit
238    *  counters, because "start" is less than 32-bits wide.  If that's
239    *  the case, then handle the wrap by subtracting off everything but
240    *  the lower 32-bits so as to get back to unsigned 32-bit
241    *  arithmetic.
242    */
243   return (end - start +  0xffffffff00000000ULL);
244 }
245 
246 float
calc_cpu_util_internal(float elapsed_time)247 calc_cpu_util_internal(float elapsed_time)
248 {
249   int i;
250 
251   float correction_factor;
252   cpu_states_t diff;
253   uint64_t total_ticks;
254 
255   memset(&lib_local_cpu_stats, 0, sizeof(lib_local_cpu_stats));
256 
257   /* It is possible that the library measured a time other than the
258      one that the user want for the cpu utilization calculations - for
259      example, tests that were ended by watchdog timers such as the udp
260      stream test. We let these tests tell up what the elapsed time
261      should be. */
262 
263   if (elapsed_time != 0.0) {
264     correction_factor = (float) 1.0 +
265       ((lib_elapsed - elapsed_time) / elapsed_time);
266   }
267   else {
268     correction_factor = (float) 1.0;
269   }
270 
271   if (debug) {
272     fprintf(where,
273 	    "lib_local_maxrate = %f\n", lib_local_maxrate);
274   }
275   for (i = 0; i < lib_num_loc_cpus; i++) {
276 
277     /* Find the difference in all CPU stat fields */
278     diff.user =
279       tick_subtract(lib_start_count[i].user, lib_end_count[i].user);
280     diff.nice =
281       tick_subtract(lib_start_count[i].nice, lib_end_count[i].nice);
282     diff.sys =
283       tick_subtract(lib_start_count[i].sys, lib_end_count[i].sys);
284     diff.idle =
285       tick_subtract(lib_start_count[i].idle, lib_end_count[i].idle);
286     diff.iowait =
287       tick_subtract(lib_start_count[i].iowait, lib_end_count[i].iowait);
288     diff.hard_irq =
289       tick_subtract(lib_start_count[i].hard_irq, lib_end_count[i].hard_irq);
290     diff.soft_irq =
291       tick_subtract(lib_start_count[i].soft_irq, lib_end_count[i].soft_irq);
292     diff.steal =
293       tick_subtract(lib_start_count[i].steal, lib_end_count[i].steal);
294     diff.guest =
295       tick_subtract(lib_start_count[i].guest, lib_end_count[i].guest);
296     diff.guest_nice =
297       tick_subtract(lib_start_count[i].guest_nice, lib_end_count[i].guest_nice);
298     total_ticks = diff.user + diff.nice + diff.sys + diff.idle + diff.iowait
299       + diff.hard_irq + diff.soft_irq + diff.steal
300       + diff.guest + diff.guest_nice;
301 
302     /* calculate idle time as a percentage of all CPU states */
303     if (total_ticks == 0) {
304       if (debug) {
305 	fprintf(where, "Total ticks 0 on CPU %d, charging nothing!\n", i);
306       }
307       lib_local_per_cpu_util[i] = 0.0;
308     } else {
309 #define CPU_STAT_PERCENTIZE(x) (100. * (((float)(x)) / ((float)(total_ticks))))
310       /* utilization = 100% - %idle */
311       lib_local_per_cpu_util[i] = 100. - CPU_STAT_PERCENTIZE(diff.idle);
312       lib_local_cpu_stats.cpu_util += lib_local_per_cpu_util[i];
313       lib_local_cpu_stats.cpu_user += CPU_STAT_PERCENTIZE(diff.user);
314       lib_local_cpu_stats.cpu_system += CPU_STAT_PERCENTIZE(diff.sys);
315       lib_local_cpu_stats.cpu_iowait += CPU_STAT_PERCENTIZE(diff.iowait);
316       lib_local_cpu_stats.cpu_irq += CPU_STAT_PERCENTIZE(diff.hard_irq);
317       lib_local_cpu_stats.cpu_swintr += CPU_STAT_PERCENTIZE(diff.soft_irq);
318     }
319     /* apply correction factor */
320     lib_local_per_cpu_util[i] *= correction_factor;
321     if (debug) {
322       fprintf(where,
323               "calc_cpu_util: util on processor %d, diff = %llu %llu %llu "
324 	      "%llu %llu %llu %llu %llu %llu util %f cf %f\n",
325               i,
326 	      (unsigned long long)diff.user,
327 	      (unsigned long long)diff.nice,
328 	      (unsigned long long)diff.sys,
329 	      (unsigned long long)diff.idle,
330 	      (unsigned long long)diff.iowait,
331 	      (unsigned long long)diff.hard_irq,
332 	      (unsigned long long)diff.soft_irq,
333 	      (unsigned long long)diff.steal,
334 	      (unsigned long long)diff.guest,
335 	      lib_local_per_cpu_util[i],
336 	      correction_factor);
337     }
338   }
339 
340   /* we want to apply correction factor and average across all n processors */
341 #define CPU_STAT_FIXUP(fldname)                                         \
342   lib_local_cpu_stats.fldname = ((correction_factor                     \
343                                   * lib_local_cpu_stats.fldname)        \
344                                  / ((float)lib_num_loc_cpus))
345 
346   CPU_STAT_FIXUP(cpu_util);
347   CPU_STAT_FIXUP(cpu_user);
348   CPU_STAT_FIXUP(cpu_system);
349   CPU_STAT_FIXUP(cpu_iowait);
350   CPU_STAT_FIXUP(cpu_irq);
351   CPU_STAT_FIXUP(cpu_swintr);
352 
353   return lib_local_cpu_stats.cpu_util;
354 }
355 
356 void
cpu_start_internal(void)357 cpu_start_internal(void)
358 {
359   get_cpu(lib_start_count);
360   return;
361 }
362 
363 void
cpu_stop_internal(void)364 cpu_stop_internal(void)
365 {
366   get_cpu(lib_end_count);
367 }
368