drvstats.c revision 1.14 1 1.14 rillig /* $NetBSD: drvstats.c,v 1.14 2021/11/27 22:16:42 rillig Exp $ */
2 1.1 blymn
3 1.1 blymn /*
4 1.1 blymn * Copyright (c) 1996 John M. Vinopal
5 1.1 blymn * All rights reserved.
6 1.1 blymn *
7 1.1 blymn * Redistribution and use in source and binary forms, with or without
8 1.1 blymn * modification, are permitted provided that the following conditions
9 1.1 blymn * are met:
10 1.1 blymn * 1. Redistributions of source code must retain the above copyright
11 1.1 blymn * notice, this list of conditions and the following disclaimer.
12 1.1 blymn * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 blymn * notice, this list of conditions and the following disclaimer in the
14 1.1 blymn * documentation and/or other materials provided with the distribution.
15 1.1 blymn * 3. All advertising materials mentioning features or use of this software
16 1.1 blymn * must display the following acknowledgement:
17 1.1 blymn * This product includes software developed for the NetBSD Project
18 1.1 blymn * by John M. Vinopal.
19 1.1 blymn * 4. The name of the author may not be used to endorse or promote products
20 1.1 blymn * derived from this software without specific prior written permission.
21 1.1 blymn *
22 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 1.1 blymn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 1.1 blymn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 1.1 blymn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 1.1 blymn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.1 blymn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.1 blymn * SUCH DAMAGE.
33 1.1 blymn */
34 1.1 blymn
35 1.1 blymn #include <sys/param.h>
36 1.1 blymn #include <sys/sched.h>
37 1.1 blymn #include <sys/sysctl.h>
38 1.1 blymn #include <sys/time.h>
39 1.1 blymn #include <sys/iostat.h>
40 1.1 blymn
41 1.1 blymn #include <err.h>
42 1.1 blymn #include <fcntl.h>
43 1.1 blymn #include <limits.h>
44 1.1 blymn #include <stdio.h>
45 1.1 blymn #include <stdlib.h>
46 1.1 blymn #include <string.h>
47 1.1 blymn #include <unistd.h>
48 1.1 blymn #include "drvstats.h"
49 1.1 blymn
50 1.1 blymn /* Structures to hold the statistics. */
51 1.1 blymn struct _drive cur, last;
52 1.1 blymn
53 1.1 blymn extern int hz;
54 1.1 blymn
55 1.1 blymn /* sysctl hw.drivestats buffer. */
56 1.1 blymn static struct io_sysctl *drives = NULL;
57 1.1 blymn
58 1.1 blymn /* Backward compatibility references. */
59 1.4 christos size_t ndrive = 0;
60 1.1 blymn int *drv_select;
61 1.1 blymn char **dr_name;
62 1.1 blymn
63 1.1 blymn /* Missing from <sys/time.h> */
64 1.1 blymn #define timerset(tvp, uvp) do { \
65 1.1 blymn ((uvp)->tv_sec = (tvp)->tv_sec); \
66 1.1 blymn ((uvp)->tv_usec = (tvp)->tv_usec); \
67 1.14 rillig } while (0)
68 1.1 blymn
69 1.1 blymn /*
70 1.1 blymn * Take the delta between the present values and the last recorded
71 1.1 blymn * values, storing the present values in the 'last' structure, and
72 1.1 blymn * the delta values in the 'cur' structure.
73 1.1 blymn */
74 1.1 blymn void
75 1.1 blymn drvswap(void)
76 1.1 blymn {
77 1.1 blymn u_int64_t tmp;
78 1.5 lukem size_t i;
79 1.1 blymn
80 1.1 blymn #define SWAP(fld) do { \
81 1.1 blymn tmp = cur.fld; \
82 1.1 blymn cur.fld -= last.fld; \
83 1.1 blymn last.fld = tmp; \
84 1.14 rillig } while (0)
85 1.1 blymn
86 1.10 mlelstv #define DELTA(x) do { \
87 1.10 mlelstv timerclear(&tmp_timer); \
88 1.10 mlelstv timerset(&(cur.x), &tmp_timer); \
89 1.10 mlelstv timersub(&tmp_timer, &(last.x), &(cur.x)); \
90 1.10 mlelstv timerclear(&(last.x)); \
91 1.10 mlelstv timerset(&tmp_timer, &(last.x)); \
92 1.14 rillig } while (0)
93 1.10 mlelstv
94 1.1 blymn for (i = 0; i < ndrive; i++) {
95 1.1 blymn struct timeval tmp_timer;
96 1.1 blymn
97 1.1 blymn if (!cur.select[i])
98 1.1 blymn continue;
99 1.1 blymn
100 1.11 mlelstv /*
101 1.11 mlelstv * When a drive is replaced with one of the same
102 1.11 mlelstv * name, the previous statistics are invalid. Try
103 1.11 mlelstv * to detect this by validating counters and timestamp
104 1.11 mlelstv */
105 1.11 mlelstv if ((cur.rxfer[i] == 0 && cur.wxfer[i] == 0)
106 1.11 mlelstv || cur.rxfer[i] - last.rxfer[i] > INT64_MAX
107 1.11 mlelstv || cur.wxfer[i] - last.wxfer[i] > INT64_MAX
108 1.11 mlelstv || cur.seek[i] - last.seek[i] > INT64_MAX
109 1.11 mlelstv || (cur.timestamp[i].tv_sec == 0 &&
110 1.11 mlelstv cur.timestamp[i].tv_usec == 0)) {
111 1.11 mlelstv
112 1.11 mlelstv last.rxfer[i] = cur.rxfer[i];
113 1.11 mlelstv last.wxfer[i] = cur.wxfer[i];
114 1.11 mlelstv last.seek[i] = cur.seek[i];
115 1.11 mlelstv last.rbytes[i] = cur.rbytes[i];
116 1.11 mlelstv last.wbytes[i] = cur.wbytes[i];
117 1.11 mlelstv
118 1.11 mlelstv timerclear(&last.wait[i]);
119 1.11 mlelstv timerclear(&last.time[i]);
120 1.11 mlelstv timerclear(&last.waitsum[i]);
121 1.11 mlelstv timerclear(&last.busysum[i]);
122 1.11 mlelstv timerclear(&last.timestamp[i]);
123 1.11 mlelstv }
124 1.11 mlelstv
125 1.1 blymn /* Delta Values. */
126 1.1 blymn SWAP(rxfer[i]);
127 1.1 blymn SWAP(wxfer[i]);
128 1.1 blymn SWAP(seek[i]);
129 1.1 blymn SWAP(rbytes[i]);
130 1.1 blymn SWAP(wbytes[i]);
131 1.1 blymn
132 1.10 mlelstv DELTA(wait[i]);
133 1.10 mlelstv DELTA(time[i]);
134 1.10 mlelstv DELTA(waitsum[i]);
135 1.10 mlelstv DELTA(busysum[i]);
136 1.11 mlelstv DELTA(timestamp[i]);
137 1.1 blymn }
138 1.1 blymn }
139 1.1 blymn
140 1.1 blymn void
141 1.1 blymn tkswap(void)
142 1.1 blymn {
143 1.1 blymn u_int64_t tmp;
144 1.1 blymn
145 1.1 blymn SWAP(tk_nin);
146 1.1 blymn SWAP(tk_nout);
147 1.1 blymn }
148 1.1 blymn
149 1.1 blymn void
150 1.1 blymn cpuswap(void)
151 1.1 blymn {
152 1.1 blymn double etime;
153 1.1 blymn u_int64_t tmp;
154 1.1 blymn int i, state;
155 1.1 blymn
156 1.1 blymn for (i = 0; i < CPUSTATES; i++)
157 1.1 blymn SWAP(cp_time[i]);
158 1.1 blymn
159 1.1 blymn etime = 0;
160 1.1 blymn for (state = 0; state < CPUSTATES; ++state) {
161 1.1 blymn etime += cur.cp_time[state];
162 1.1 blymn }
163 1.1 blymn if (etime == 0)
164 1.1 blymn etime = 1;
165 1.1 blymn etime /= hz;
166 1.1 blymn etime /= cur.cp_ncpu;
167 1.1 blymn
168 1.1 blymn cur.cp_etime = etime;
169 1.1 blymn }
170 1.10 mlelstv #undef DELTA
171 1.1 blymn #undef SWAP
172 1.1 blymn
173 1.1 blymn /*
174 1.1 blymn * Read the drive statistics for each drive in the drive list.
175 1.1 blymn * Also collect statistics for tty i/o and CPU ticks.
176 1.1 blymn */
177 1.1 blymn void
178 1.1 blymn drvreadstats(void)
179 1.1 blymn {
180 1.11 mlelstv size_t size, i, j, count;
181 1.1 blymn int mib[3];
182 1.1 blymn
183 1.7 joerg mib[0] = CTL_HW;
184 1.7 joerg mib[1] = HW_IOSTATS;
185 1.7 joerg mib[2] = sizeof(struct io_sysctl);
186 1.7 joerg
187 1.7 joerg size = ndrive * sizeof(struct io_sysctl);
188 1.7 joerg if (sysctl(mib, 3, drives, &size, NULL, 0) < 0)
189 1.7 joerg err(1, "sysctl hw.iostats failed");
190 1.11 mlelstv /* recalculate array length */
191 1.11 mlelstv count = size / sizeof(struct io_sysctl);
192 1.10 mlelstv
193 1.11 mlelstv #define COPYF(x,k,l) cur.x[k] = drives[l].x
194 1.11 mlelstv #define COPYT(x,k,l) do { \
195 1.11 mlelstv cur.x[k].tv_sec = drives[l].x##_sec; \
196 1.11 mlelstv cur.x[k].tv_usec = drives[l].x##_usec; \
197 1.14 rillig } while (0)
198 1.10 mlelstv
199 1.11 mlelstv for (i = 0, j = 0; i < ndrive && j < count; i++) {
200 1.11 mlelstv
201 1.11 mlelstv /*
202 1.11 mlelstv * skip removed entries
203 1.11 mlelstv *
204 1.11 mlelstv * we cannot detect entries replaced with
205 1.11 mlelstv * devices of the same name (e.g. unplug/replug).
206 1.11 mlelstv */
207 1.11 mlelstv if (strcmp(cur.name[i], drives[j].name)) {
208 1.11 mlelstv cur.select[i] = 0;
209 1.11 mlelstv continue;
210 1.11 mlelstv }
211 1.11 mlelstv
212 1.11 mlelstv COPYF(rxfer, i, j);
213 1.11 mlelstv COPYF(wxfer, i, j);
214 1.11 mlelstv COPYF(seek, i, j);
215 1.11 mlelstv COPYF(rbytes, i, j);
216 1.11 mlelstv COPYF(wbytes, i, j);
217 1.11 mlelstv
218 1.11 mlelstv COPYT(wait, i, j);
219 1.11 mlelstv COPYT(time, i, j);
220 1.11 mlelstv COPYT(waitsum, i, j);
221 1.11 mlelstv COPYT(busysum, i, j);
222 1.11 mlelstv COPYT(timestamp, i, j);
223 1.10 mlelstv
224 1.11 mlelstv ++j;
225 1.7 joerg }
226 1.1 blymn
227 1.11 mlelstv /* shrink table to new size */
228 1.11 mlelstv ndrive = j;
229 1.11 mlelstv
230 1.10 mlelstv mib[0] = CTL_KERN;
231 1.7 joerg mib[1] = KERN_TKSTAT;
232 1.7 joerg mib[2] = KERN_TKSTAT_NIN;
233 1.7 joerg size = sizeof(cur.tk_nin);
234 1.7 joerg if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0)
235 1.7 joerg cur.tk_nin = 0;
236 1.7 joerg
237 1.7 joerg mib[2] = KERN_TKSTAT_NOUT;
238 1.7 joerg size = sizeof(cur.tk_nout);
239 1.7 joerg if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0)
240 1.7 joerg cur.tk_nout = 0;
241 1.1 blymn
242 1.1 blymn size = sizeof(cur.cp_time);
243 1.4 christos (void)memset(cur.cp_time, 0, size);
244 1.7 joerg mib[0] = CTL_KERN;
245 1.7 joerg mib[1] = KERN_CP_TIME;
246 1.7 joerg if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0)
247 1.7 joerg (void)memset(cur.cp_time, 0, sizeof(cur.cp_time));
248 1.1 blymn }
249 1.10 mlelstv #undef COPYT
250 1.10 mlelstv #undef COPYF
251 1.1 blymn
252 1.1 blymn /*
253 1.1 blymn * Read collect statistics for tty i/o.
254 1.1 blymn */
255 1.1 blymn
256 1.1 blymn void
257 1.1 blymn tkreadstats(void)
258 1.1 blymn {
259 1.1 blymn size_t size;
260 1.1 blymn int mib[3];
261 1.1 blymn
262 1.7 joerg mib[0] = CTL_KERN;
263 1.7 joerg mib[1] = KERN_TKSTAT;
264 1.7 joerg mib[2] = KERN_TKSTAT_NIN;
265 1.7 joerg size = sizeof(cur.tk_nin);
266 1.7 joerg if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0)
267 1.7 joerg cur.tk_nin = 0;
268 1.7 joerg
269 1.7 joerg mib[2] = KERN_TKSTAT_NOUT;
270 1.7 joerg size = sizeof(cur.tk_nout);
271 1.7 joerg if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0)
272 1.7 joerg cur.tk_nout = 0;
273 1.1 blymn }
274 1.1 blymn
275 1.1 blymn /*
276 1.1 blymn * Read collect statistics for CPU ticks.
277 1.1 blymn */
278 1.1 blymn
279 1.1 blymn void
280 1.1 blymn cpureadstats(void)
281 1.1 blymn {
282 1.1 blymn size_t size;
283 1.1 blymn int mib[2];
284 1.1 blymn
285 1.1 blymn size = sizeof(cur.cp_time);
286 1.4 christos (void)memset(cur.cp_time, 0, size);
287 1.7 joerg mib[0] = CTL_KERN;
288 1.7 joerg mib[1] = KERN_CP_TIME;
289 1.7 joerg if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0)
290 1.7 joerg (void)memset(cur.cp_time, 0, sizeof(cur.cp_time));
291 1.1 blymn }
292 1.1 blymn
293 1.1 blymn /*
294 1.1 blymn * Perform all of the initialization and memory allocation needed to
295 1.1 blymn * track drive statistics.
296 1.1 blymn */
297 1.1 blymn int
298 1.1 blymn drvinit(int selected)
299 1.1 blymn {
300 1.1 blymn struct clockinfo clockinfo;
301 1.5 lukem size_t size, i;
302 1.1 blymn static int once = 0;
303 1.5 lukem int mib[3];
304 1.1 blymn
305 1.1 blymn if (once)
306 1.1 blymn return (1);
307 1.1 blymn
308 1.7 joerg mib[0] = CTL_HW;
309 1.7 joerg mib[1] = HW_NCPU;
310 1.7 joerg size = sizeof(cur.cp_ncpu);
311 1.7 joerg if (sysctl(mib, 2, &cur.cp_ncpu, &size, NULL, 0) == -1)
312 1.7 joerg err(1, "sysctl hw.ncpu failed");
313 1.7 joerg
314 1.7 joerg mib[0] = CTL_KERN;
315 1.7 joerg mib[1] = KERN_CLOCKRATE;
316 1.7 joerg size = sizeof(clockinfo);
317 1.7 joerg if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1)
318 1.7 joerg err(1, "sysctl kern.clockrate failed");
319 1.7 joerg hz = clockinfo.stathz;
320 1.7 joerg if (!hz)
321 1.7 joerg hz = clockinfo.hz;
322 1.7 joerg
323 1.7 joerg mib[0] = CTL_HW;
324 1.7 joerg mib[1] = HW_IOSTATS;
325 1.7 joerg mib[2] = sizeof(struct io_sysctl);
326 1.7 joerg if (sysctl(mib, 3, NULL, &size, NULL, 0) == -1)
327 1.7 joerg err(1, "sysctl hw.drivestats failed");
328 1.7 joerg ndrive = size / sizeof(struct io_sysctl);
329 1.1 blymn
330 1.7 joerg if (size == 0) {
331 1.7 joerg warnx("No drives attached.");
332 1.1 blymn } else {
333 1.7 joerg drives = (struct io_sysctl *)malloc(size);
334 1.7 joerg if (drives == NULL)
335 1.7 joerg errx(1, "Memory allocation failure.");
336 1.1 blymn }
337 1.1 blymn
338 1.1 blymn /* Allocate space for the statistics. */
339 1.1 blymn cur.time = calloc(ndrive, sizeof(struct timeval));
340 1.10 mlelstv cur.wait = calloc(ndrive, sizeof(struct timeval));
341 1.10 mlelstv cur.waitsum = calloc(ndrive, sizeof(struct timeval));
342 1.10 mlelstv cur.busysum = calloc(ndrive, sizeof(struct timeval));
343 1.11 mlelstv cur.timestamp = calloc(ndrive, sizeof(struct timeval));
344 1.1 blymn cur.rxfer = calloc(ndrive, sizeof(u_int64_t));
345 1.1 blymn cur.wxfer = calloc(ndrive, sizeof(u_int64_t));
346 1.1 blymn cur.seek = calloc(ndrive, sizeof(u_int64_t));
347 1.1 blymn cur.rbytes = calloc(ndrive, sizeof(u_int64_t));
348 1.1 blymn cur.wbytes = calloc(ndrive, sizeof(u_int64_t));
349 1.13 he cur.scale = calloc(ndrive, sizeof(int));
350 1.1 blymn last.time = calloc(ndrive, sizeof(struct timeval));
351 1.10 mlelstv last.wait = calloc(ndrive, sizeof(struct timeval));
352 1.10 mlelstv last.waitsum = calloc(ndrive, sizeof(struct timeval));
353 1.10 mlelstv last.busysum = calloc(ndrive, sizeof(struct timeval));
354 1.11 mlelstv last.timestamp = calloc(ndrive, sizeof(struct timeval));
355 1.1 blymn last.rxfer = calloc(ndrive, sizeof(u_int64_t));
356 1.1 blymn last.wxfer = calloc(ndrive, sizeof(u_int64_t));
357 1.1 blymn last.seek = calloc(ndrive, sizeof(u_int64_t));
358 1.1 blymn last.rbytes = calloc(ndrive, sizeof(u_int64_t));
359 1.1 blymn last.wbytes = calloc(ndrive, sizeof(u_int64_t));
360 1.1 blymn cur.select = calloc(ndrive, sizeof(int));
361 1.1 blymn cur.name = calloc(ndrive, sizeof(char *));
362 1.1 blymn
363 1.10 mlelstv if (cur.time == NULL || cur.wait == NULL ||
364 1.10 mlelstv cur.waitsum == NULL || cur.busysum == NULL ||
365 1.11 mlelstv cur.timestamp == NULL ||
366 1.10 mlelstv cur.rxfer == NULL || cur.wxfer == NULL ||
367 1.10 mlelstv cur.seek == NULL || cur.rbytes == NULL ||
368 1.10 mlelstv cur.wbytes == NULL ||
369 1.10 mlelstv last.time == NULL || last.wait == NULL ||
370 1.10 mlelstv last.waitsum == NULL || last.busysum == NULL ||
371 1.11 mlelstv last.timestamp == NULL ||
372 1.10 mlelstv last.rxfer == NULL || last.wxfer == NULL ||
373 1.10 mlelstv last.seek == NULL || last.rbytes == NULL ||
374 1.10 mlelstv last.wbytes == NULL ||
375 1.1 blymn cur.select == NULL || cur.name == NULL)
376 1.1 blymn errx(1, "Memory allocation failure.");
377 1.1 blymn
378 1.1 blymn /* Set up the compatibility interfaces. */
379 1.1 blymn drv_select = cur.select;
380 1.1 blymn dr_name = cur.name;
381 1.1 blymn
382 1.12 dholland /* Read the drive names and set initial selection. */
383 1.7 joerg mib[0] = CTL_HW; /* Should be still set from */
384 1.7 joerg mib[1] = HW_IOSTATS; /* ... above, but be safe... */
385 1.7 joerg mib[2] = sizeof(struct io_sysctl);
386 1.7 joerg if (sysctl(mib, 3, drives, &size, NULL, 0) == -1)
387 1.7 joerg err(1, "sysctl hw.iostats failed");
388 1.11 mlelstv /* Recalculate array length */
389 1.11 mlelstv ndrive = size / sizeof(struct io_sysctl);
390 1.7 joerg for (i = 0; i < ndrive; i++) {
391 1.11 mlelstv cur.name[i] = strndup(drives[i].name, sizeof(drives[i].name));
392 1.11 mlelstv if (cur.name[i] == NULL)
393 1.11 mlelstv errx(1, "Memory allocation failure");
394 1.7 joerg cur.select[i] = selected;
395 1.1 blymn }
396 1.1 blymn
397 1.1 blymn /* Never do this initialization again. */
398 1.1 blymn once = 1;
399 1.1 blymn return (1);
400 1.1 blymn }
401