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