1 /* $NetBSD: drvstats.c,v 1.4 2006/10/17 15:13:08 christos Exp $ */
4 * Copyright (c) 1996 John M. Vinopal
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/param.h>
36 #include <sys/sched.h>
37 #include <sys/sysctl.h>
39 #include <sys/iostat.h>
52 static struct nlist namelist
[] = {
54 { .n_name
= "_tk_nin" }, /* tty characters in */
56 { .n_name
= "_tk_nout" }, /* tty characters out */
58 { .n_name
= "_hz" }, /* ticks per second */
60 { .n_name
= "_stathz" },
61 #define X_DRIVE_COUNT 4
62 { .n_name
= "_iostat_count" }, /* number of drives */
64 { .n_name
= "_iostatlist" }, /* TAILQ of drives */
68 /* Structures to hold the statistics. */
69 struct _drive cur
, last
;
71 /* Kernel pointers: nlistf and memf defined in calling program. */
72 static kvm_t
*kd
= NULL
;
77 /* Pointer to list of drives. */
78 static struct io_stats
*iostathead
= NULL
;
79 /* sysctl hw.drivestats buffer. */
80 static struct io_sysctl
*drives
= NULL
;
82 /* Backward compatibility references. */
87 #define KVM_ERROR(_string) do { \
88 warnx("%s", (_string)); \
89 errx(1, "%s", kvm_geterr(kd)); \
90 } while (/* CONSTCOND */0)
93 * Dereference the namelist pointer `v' and fill in the local copy
94 * 'p' which is of size 's'.
96 #define deref_nl(v, p, s) do { \
97 deref_kptr((void *)namelist[(v)].n_value, (p), (s)); \
98 } while (/* CONSTCOND */0)
100 /* Missing from <sys/time.h> */
101 #define timerset(tvp, uvp) do { \
102 ((uvp)->tv_sec = (tvp)->tv_sec); \
103 ((uvp)->tv_usec = (tvp)->tv_usec); \
104 } while (/* CONSTCOND */0)
106 static void deref_kptr(void *, void *, size_t);
109 * Take the delta between the present values and the last recorded
110 * values, storing the present values in the 'last' structure, and
111 * the delta values in the 'cur' structure.
119 #define SWAP(fld) do { \
121 cur.fld -= last.fld; \
123 } while (/* CONSTCOND */0)
125 for (i
= 0; i
< ndrive
; i
++) {
126 struct timeval tmp_timer
;
139 timerclear(&tmp_timer
);
140 timerset(&(cur
.time
[i
]), &tmp_timer
);
141 timersub(&tmp_timer
, &(last
.time
[i
]), &(cur
.time
[i
]));
142 timerclear(&(last
.time
[i
]));
143 timerset(&tmp_timer
, &(last
.time
[i
]));
163 for (i
= 0; i
< CPUSTATES
; i
++)
167 for (state
= 0; state
< CPUSTATES
; ++state
) {
168 etime
+= cur
.cp_time
[state
];
173 etime
/= cur
.cp_ncpu
;
175 cur
.cp_etime
= etime
;
180 * Read the drive statistics for each drive in the drive list.
181 * Also collect statistics for tty i/o and CPU ticks.
186 struct io_stats cur_drive
, *p
;
195 mib
[2] = sizeof(struct io_sysctl
);
197 size
= ndrive
* sizeof(struct io_sysctl
);
198 if (sysctl(mib
, 3, drives
, &size
, NULL
, 0) < 0)
199 err(1, "sysctl hw.iostats failed");
200 for (i
= 0; i
< ndrive
; i
++) {
201 cur
.rxfer
[i
] = drives
[i
].rxfer
;
202 cur
.wxfer
[i
] = drives
[i
].wxfer
;
203 cur
.seek
[i
] = drives
[i
].seek
;
204 cur
.rbytes
[i
] = drives
[i
].rbytes
;
205 cur
.wbytes
[i
] = drives
[i
].wbytes
;
206 cur
.time
[i
].tv_sec
= drives
[i
].time_sec
;
207 cur
.time
[i
].tv_usec
= drives
[i
].time_usec
;
211 mib
[1] = KERN_TKSTAT
;
212 mib
[2] = KERN_TKSTAT_NIN
;
213 size
= sizeof(cur
.tk_nin
);
214 if (sysctl(mib
, 3, &cur
.tk_nin
, &size
, NULL
, 0) < 0)
217 mib
[2] = KERN_TKSTAT_NOUT
;
218 size
= sizeof(cur
.tk_nout
);
219 if (sysctl(mib
, 3, &cur
.tk_nout
, &size
, NULL
, 0) < 0)
222 for (i
= 0; i
< ndrive
; i
++) {
223 deref_kptr(p
, &cur_drive
, sizeof(cur_drive
));
224 cur
.rxfer
[i
] = cur_drive
.io_rxfer
;
225 cur
.wxfer
[i
] = cur_drive
.io_wxfer
;
226 cur
.seek
[i
] = cur_drive
.io_seek
;
227 cur
.rbytes
[i
] = cur_drive
.io_rbytes
;
228 cur
.wbytes
[i
] = cur_drive
.io_wbytes
;
229 timerset(&(cur_drive
.io_time
), &(cur
.time
[i
]));
230 p
= cur_drive
.io_link
.tqe_next
;
233 deref_nl(X_TK_NIN
, &cur
.tk_nin
, sizeof(cur
.tk_nin
));
234 deref_nl(X_TK_NOUT
, &cur
.tk_nout
, sizeof(cur
.tk_nout
));
238 * XXX Need to locate the `correct' CPU when looking for this
239 * XXX in crash dumps. Just don't report it for now, in that
242 size
= sizeof(cur
.cp_time
);
243 (void)memset(cur
.cp_time
, 0, size
);
246 mib
[1] = KERN_CP_TIME
;
247 if (sysctl(mib
, 2, cur
.cp_time
, &size
, NULL
, 0) < 0)
248 (void)memset(cur
.cp_time
, 0, sizeof(cur
.cp_time
));
253 * Read collect statistics for tty i/o.
264 mib
[1] = KERN_TKSTAT
;
265 mib
[2] = KERN_TKSTAT_NIN
;
266 size
= sizeof(cur
.tk_nin
);
267 if (sysctl(mib
, 3, &cur
.tk_nin
, &size
, NULL
, 0) < 0)
270 mib
[2] = KERN_TKSTAT_NOUT
;
271 size
= sizeof(cur
.tk_nout
);
272 if (sysctl(mib
, 3, &cur
.tk_nout
, &size
, NULL
, 0) < 0)
275 deref_nl(X_TK_NIN
, &cur
.tk_nin
, sizeof(cur
.tk_nin
));
276 deref_nl(X_TK_NOUT
, &cur
.tk_nout
, sizeof(cur
.tk_nout
));
281 * Read collect statistics for CPU ticks.
291 * XXX Need to locate the `correct' CPU when looking for this
292 * XXX in crash dumps. Just don't report it for now, in that
295 size
= sizeof(cur
.cp_time
);
296 (void)memset(cur
.cp_time
, 0, size
);
299 mib
[1] = KERN_CP_TIME
;
300 if (sysctl(mib
, 2, cur
.cp_time
, &size
, NULL
, 0) < 0)
301 (void)memset(cur
.cp_time
, 0, sizeof(cur
.cp_time
));
306 * Perform all of the initialization and memory allocation needed to
307 * track drive statistics.
310 drvinit(int selected
)
312 struct iostatlist_head iostat_head
;
313 struct io_stats cur_drive
, *p
;
314 struct clockinfo clockinfo
;
315 char errbuf
[_POSIX2_LINE_MAX
];
326 size
= sizeof(cur
.cp_ncpu
);
327 if (sysctl(mib
, 2, &cur
.cp_ncpu
, &size
, NULL
, 0) == -1)
328 err(1, "sysctl hw.ncpu failed");
331 mib
[1] = KERN_CLOCKRATE
;
332 size
= sizeof(clockinfo
);
333 if (sysctl(mib
, 2, &clockinfo
, &size
, NULL
, 0) == -1)
334 err(1, "sysctl kern.clockrate failed");
335 hz
= clockinfo
.stathz
;
341 mib
[2] = sizeof(struct io_sysctl
);
342 if (sysctl(mib
, 3, NULL
, &size
, NULL
, 0) == -1)
343 err(1, "sysctl hw.drivestats failed");
344 ndrive
= size
/ sizeof(struct io_sysctl
);
347 warnx("No drives attached.");
349 drives
= (struct io_sysctl
*)malloc(size
);
351 errx(1, "Memory allocation failure.");
355 /* Open the kernel. */
356 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
,
358 errx(1, "kvm_openfiles: %s", errbuf
);
360 /* Obtain the namelist symbols from the kernel. */
361 if (kvm_nlist(kd
, namelist
))
362 KVM_ERROR("kvm_nlist failed to read symbols.");
364 /* Get the number of attached drives. */
365 deref_nl(X_DRIVE_COUNT
, &drive_count
, sizeof(drive_count
));
368 errx(1, "invalid _drive_count %d.", drive_count
);
369 else if (drive_count
== 0) {
370 warnx("No drives attached.");
372 /* Get a pointer to the first drive. */
373 deref_nl(X_DRIVELIST
, &iostat_head
,
374 sizeof(iostat_head
));
375 iostathead
= iostat_head
.tqh_first
;
377 ndrive
= drive_count
;
379 /* Get ticks per second. */
380 deref_nl(X_STATHZ
, &hz
, sizeof(hz
));
382 deref_nl(X_HZ
, &hz
, sizeof(hz
));
385 /* Allocate space for the statistics. */
386 cur
.time
= calloc(ndrive
, sizeof(struct timeval
));
387 cur
.rxfer
= calloc(ndrive
, sizeof(u_int64_t
));
388 cur
.wxfer
= calloc(ndrive
, sizeof(u_int64_t
));
389 cur
.seek
= calloc(ndrive
, sizeof(u_int64_t
));
390 cur
.rbytes
= calloc(ndrive
, sizeof(u_int64_t
));
391 cur
.wbytes
= calloc(ndrive
, sizeof(u_int64_t
));
392 last
.time
= calloc(ndrive
, sizeof(struct timeval
));
393 last
.rxfer
= calloc(ndrive
, sizeof(u_int64_t
));
394 last
.wxfer
= calloc(ndrive
, sizeof(u_int64_t
));
395 last
.seek
= calloc(ndrive
, sizeof(u_int64_t
));
396 last
.rbytes
= calloc(ndrive
, sizeof(u_int64_t
));
397 last
.wbytes
= calloc(ndrive
, sizeof(u_int64_t
));
398 cur
.select
= calloc(ndrive
, sizeof(int));
399 cur
.name
= calloc(ndrive
, sizeof(char *));
401 if (cur
.time
== NULL
|| cur
.rxfer
== NULL
||
402 cur
.wxfer
== NULL
|| cur
.seek
== NULL
||
403 cur
.rbytes
== NULL
|| cur
.wbytes
== NULL
||
404 last
.time
== NULL
|| last
.rxfer
== NULL
||
405 last
.wxfer
== NULL
|| last
.seek
== NULL
||
406 last
.rbytes
== NULL
|| last
.wbytes
== NULL
||
407 cur
.select
== NULL
|| cur
.name
== NULL
)
408 errx(1, "Memory allocation failure.");
410 /* Set up the compatibility interfaces. */
411 drv_select
= cur
.select
;
414 /* Read the drive names and set intial selection. */
416 mib
[0] = CTL_HW
; /* Should be still set from */
417 mib
[1] = HW_IOSTATS
; /* ... above, but be safe... */
418 mib
[2] = sizeof(struct io_sysctl
);
419 if (sysctl(mib
, 3, drives
, &size
, NULL
, 0) == -1)
420 err(1, "sysctl hw.iostats failed");
421 for (i
= 0; i
< ndrive
; i
++) {
422 cur
.name
[i
] = drives
[i
].name
;
423 cur
.select
[i
] = selected
;
427 for (i
= 0; i
< ndrive
; i
++) {
429 deref_kptr(p
, &cur_drive
, sizeof(cur_drive
));
430 deref_kptr(cur_drive
.io_name
, buf
, sizeof(buf
));
431 cur
.name
[i
] = strdup(buf
);
434 cur
.select
[i
] = selected
;
436 p
= cur_drive
.io_link
.tqe_next
;
440 /* Never do this initialization again. */
446 * Dereference the kernel pointer `kptr' and fill in the local copy
447 * pointed to by `ptr'. The storage space must be pre-allocated,
448 * and the size of the copy passed in `len'.
451 deref_kptr(void *kptr
, void *ptr
, size_t len
)
455 if ((size_t)kvm_read(kd
, (u_long
)kptr
, (char *)ptr
, len
) != len
) {
456 (void)memset(buf
, 0, sizeof(buf
));
457 (void)snprintf(buf
, sizeof buf
, "can't dereference kptr 0x%lx",