1char   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
47typedef 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
61static cpu_states_t  lib_start_count[MAXCPUS];
62static 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
70static int proc_stat_fd = -1;
71static char *proc_stat_buf = NULL;
72static int proc_stat_buflen = 0;
73
74void
75cpu_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
113void
114cpu_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
123int
124get_cpu_method()
125{
126  return PROC_STAT;
127}
128
129float
130calibrate_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
161static void
162get_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
216void
217measure_cpu_start()
218{
219  cpu_method = PROC_STAT;
220  get_cpu(lib_start_count);
221}
222
223/* collect final CPU utilization raw data */
224void
225measure_cpu_stop()
226{
227  get_cpu(lib_end_count);
228}
229
230static uint64_t
231tick_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
246float
247calc_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
356void
357cpu_start_internal(void)
358{
359  get_cpu(lib_start_count);
360  return;
361}
362
363void
364cpu_stop_internal(void)
365{
366  get_cpu(lib_end_count);
367}
368