• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 char   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 */
43 static uint64_t  lib_start_count[MAXCPUS];
44 static 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 
52 static int proc_stat_fd = -1;
53 static char *proc_stat_buf = NULL;
54 static int proc_stat_buflen = 0;
55 
56 void
cpu_util_init(void)57 cpu_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 
95 void
cpu_util_terminate(void)96 cpu_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 
105 int
get_cpu_method()106 get_cpu_method()
107 {
108   return PROC_STAT;
109 }
110 
111 float
calibrate_idle_rate(int iterations,int interval)112 calibrate_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 
143 void
get_cpu_idle(uint64_t * res)144 get_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 
180 void
measure_cpu_start()181 measure_cpu_start()
182 {
183   cpu_method = PROC_STAT;
184   get_cpu_idle(lib_start_count);
185 }
186 
187 /* collect final CPU utilization raw data */
188 void
measure_cpu_stop()189 measure_cpu_stop()
190 {
191   get_cpu_idle(lib_end_count);
192 }
193 
194 float
calc_cpu_util_internal(float elapsed_time)195 calc_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 
254 void
cpu_start_internal(void)255 cpu_start_internal(void)
256 {
257   get_cpu_idle(lib_start_count);
258   return;
259 }
260 
261 void
cpu_stop_internal(void)262 cpu_stop_internal(void)
263 {
264   get_cpu_idle(lib_end_count);
265 }
266