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