1char   netcpu_procstat_id[]="\
2@(#)netcpu_procstat.c (c) Copyright 2005-2007 Version 2.4.3";
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 */
43static uint64_t  lib_start_count[MAXCPUS];
44static uint64_t  lib_end_count[MAXCPUS];
45
46
47/* The max. length of one line of /proc/stat cpu output */
48#define CPU_LINE_LENGTH ((8 * sizeof (long) / 3 + 1) * 4 + 8)
49#define PROC_STAT_FILE_NAME "/proc/stat"
50#define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr)
51
52static int proc_stat_fd = -1;
53static char *proc_stat_buf = NULL;
54static int proc_stat_buflen = 0;
55
56void
57cpu_util_init(void)
58{
59
60  if (debug) {
61    fprintf(where,
62	    "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n",
63	    proc_stat_fd,
64	    proc_stat_buf);
65    fflush(where);
66  }
67  if (proc_stat_fd < 0) {
68    proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
69    if (proc_stat_fd < 0) {
70      fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
71      exit (1);
72    };
73  };
74
75  if (!proc_stat_buf) {
76    proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
77    if (debug) {
78      fprintf(where,
79	      "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
80	      lib_num_loc_cpus,
81	      N_CPU_LINES(lib_num_loc_cpus),
82	      CPU_LINE_LENGTH,
83	      proc_stat_buflen);
84      fflush(where);
85    }
86    proc_stat_buf = (char *)malloc (proc_stat_buflen);
87    if (!proc_stat_buf) {
88      fprintf (stderr, "Cannot allocate buffer memory!\n");
89      exit (1);
90    }
91  }
92  return;
93}
94
95void
96cpu_util_terminate(void)
97{
98  close(proc_stat_fd);
99  proc_stat_fd = -1;
100  free(proc_stat_buf);
101  proc_stat_buf = NULL;
102  return;
103}
104
105int
106get_cpu_method()
107{
108  return PROC_STAT;
109}
110
111float
112calibrate_idle_rate (int iterations, int interval)
113{
114  if (proc_stat_fd < 0) {
115    proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL);
116    if (proc_stat_fd < 0) {
117      fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME);
118      exit (1);
119    };
120  };
121
122  if (!proc_stat_buf) {
123    proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH;
124    if (debug) {
125      fprintf(where,
126	      "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n",
127	      lib_num_loc_cpus,
128	      N_CPU_LINES(lib_num_loc_cpus),
129	      CPU_LINE_LENGTH,
130	      proc_stat_buflen);
131      fflush(where);
132    }
133    proc_stat_buf = (char *)malloc (proc_stat_buflen);
134    if (!proc_stat_buf) {
135      fprintf (stderr, "Cannot allocate buffer memory!\n");
136      exit (1);
137    };
138  };
139
140  return sysconf (_SC_CLK_TCK);
141}
142
143void
144get_cpu_idle (uint64_t *res)
145{
146  int space;
147  int i;
148  int n = lib_num_loc_cpus;
149  char *p = proc_stat_buf;
150
151  lseek (proc_stat_fd, 0, SEEK_SET);
152  read (proc_stat_fd, p, proc_stat_buflen);
153
154  if (debug) {
155    fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p);
156    fflush(where);
157  }
158  /* Skip first line (total) on SMP */
159  if (n > 1) p = strchr (p, '\n');
160
161  /* Idle time is the 4th space-separated token */
162  for (i = 0; i < n; i++) {
163    for (space = 0; space < 4; space ++) {
164      p = strchr (p, ' ');
165      while (*++p == ' ');
166    };
167    res[i] = strtoul (p, &p, 10);
168    if (debug) {
169      fprintf(where,"res[%d] is %llu\n",i,res[i]);
170      fflush(where);
171    }
172    p = strchr (p, '\n');
173  };
174
175}
176
177/* take the initial timestamp and start collecting CPU utilization if
178   requested */
179
180void
181measure_cpu_start()
182{
183  cpu_method = PROC_STAT;
184  get_cpu_idle(lib_start_count);
185}
186
187/* collect final CPU utilization raw data */
188void
189measure_cpu_stop()
190{
191  get_cpu_idle(lib_end_count);
192}
193
194float
195calc_cpu_util_internal(float elapsed_time)
196{
197  int i;
198
199  float actual_rate;
200  float correction_factor;
201
202  lib_local_cpu_util = (float)0.0;
203  /* It is possible that the library measured a time other than */
204  /* the one that the user want for the cpu utilization */
205  /* calculations - for example, tests that were ended by */
206  /* watchdog timers such as the udp stream test. We let these */
207  /* tests tell up what the elapsed time should be. */
208
209  if (elapsed_time != 0.0) {
210    correction_factor = (float) 1.0 +
211      ((lib_elapsed - elapsed_time) / elapsed_time);
212  }
213  else {
214    correction_factor = (float) 1.0;
215  }
216
217  for (i = 0; i < lib_num_loc_cpus; i++) {
218
219    /* it would appear that on some systems, in loopback, nice is
220     *very* effective, causing the looper process to stop dead in its
221     tracks. if this happens, we need to ensure that the calculation
222     does not go south. raj 6/95 and if we run completely out of idle,
223     the same thing could in theory happen to the USE_KSTAT path. raj
224     8/2000 */
225
226    if (lib_end_count[i] == lib_start_count[i]) {
227      lib_end_count[i]++;
228    }
229
230    actual_rate = (lib_end_count[i] > lib_start_count[i]) ?
231      (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed :
232      (float)(lib_end_count[i] - lib_start_count[i] +
233	      MAXLONG)/ lib_elapsed;
234    lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) /
235      lib_local_maxrate * 100;
236    if (debug) {
237      fprintf(where,
238              "calc_cpu_util: actual_rate on processor %d is %f start %llx end %llx util %f\n",
239              i,
240              actual_rate,
241              lib_start_count[i],
242              lib_end_count[i],
243	      lib_local_per_cpu_util[i]);
244    }
245    lib_local_cpu_util += lib_local_per_cpu_util[i];
246  }
247  /* we want the average across all n processors */
248  lib_local_cpu_util /= (float)lib_num_loc_cpus;
249
250  lib_local_cpu_util *= correction_factor;
251  return lib_local_cpu_util;
252}
253
254void
255cpu_start_internal(void)
256{
257  get_cpu_idle(lib_start_count);
258  return;
259}
260
261void
262cpu_stop_internal(void)
263{
264  get_cpu_idle(lib_end_count);
265}
266