Annotation of src/usr.bin/vmstat/dkstats.c, Revision 1.10
1.10 ! angelos 1: /* $OpenBSD: dkstats.c,v 1.9 2001/01/02 20:09:02 deraadt Exp $ */
1.2 deraadt 2: /* $NetBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej Exp $ */
1.1 tholo 3:
4: /*
1.2 deraadt 5: * Copyright (c) 1996 John M. Vinopal
1.1 tholo 6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed for the NetBSD Project
19: * by John M. Vinopal.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.10 ! angelos 36: #include <sys/param.h>
1.1 tholo 37: #include <sys/dkstat.h>
38: #include <sys/time.h>
39: #include <sys/disk.h>
1.10 ! angelos 40: #include <sys/sysctl.h>
! 41: #include <sys/tty.h>
1.1 tholo 42:
43: #include <err.h>
44: #include <fcntl.h>
45: #include <kvm.h>
46: #include <limits.h>
47: #include <nlist.h>
48: #include <stdio.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include <unistd.h>
52: #include "dkstats.h"
53:
54: static struct nlist namelist[] = {
55: #define X_TK_NIN 0
56: { "_tk_nin" }, /* tty characters in */
57: #define X_TK_NOUT 1
58: { "_tk_nout" }, /* tty characters out */
59: #define X_CP_TIME 2
60: { "_cp_time" }, /* system timer ticks */
61: #define X_HZ 3
62: { "_hz" }, /* ticks per second */
63: #define X_STATHZ 4
64: { "_stathz" },
65: #define X_DISK_COUNT 5
66: { "_disk_count" }, /* number of disks */
67: #define X_DISKLIST 6
68: { "_disklist" }, /* TAILQ of disks */
69: { NULL },
70: };
71:
72: /* Structures to hold the statistics. */
73: struct _disk cur, last;
74:
75: /* Kernel pointers: nlistf and memf defined in calling program. */
76: static kvm_t *kd = NULL;
77: extern char *nlistf;
78: extern char *memf;
79:
80: /* Pointer to list of disks. */
81: static struct disk *dk_drivehead = NULL;
82:
83: /* Backward compatibility references. */
84: int dk_ndrive = 0;
85: int *dk_select;
86: char **dr_name;
87:
88: #define KVM_ERROR(_string) { \
1.8 aaron 89: warnx("%s", (_string)); \
1.7 millert 90: errx(1, "%s", kvm_geterr(kd)); \
1.1 tholo 91: }
92:
93: /*
94: * Dereference the namelist pointer `v' and fill in the local copy
95: * 'p' which is of size 's'.
96: */
97: #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
98:
99: /* Missing from <sys/time.h> */
100: #define timerset(tvp, uvp) ((uvp)->tv_sec = (tvp)->tv_sec); \
101: ((uvp)->tv_usec = (tvp)->tv_usec)
102:
1.2 deraadt 103: static void deref_kptr __P((void *, void *, size_t));
1.1 tholo 104:
105: /*
106: * Take the delta between the present values and the last recorded
107: * values, storing the present values in the 'last' structure, and
108: * the delta values in the 'cur' structure.
109: */
110: void
111: dkswap()
112: {
113: #define SWAP(fld) tmp = cur.fld; \
114: cur.fld -= last.fld; \
115: last.fld = tmp
116: u_int64_t tmp;
117: int i;
118:
119: for (i = 0; i < dk_ndrive; i++) {
120: struct timeval tmp_timer;
121:
122: if (!cur.dk_select[i])
123: continue;
124:
125: /* Delta Values. */
126: SWAP(dk_xfer[i]);
127: SWAP(dk_seek[i]);
128: SWAP(dk_bytes[i]);
129:
130: /* Delta Time. */
131: timerclear(&tmp_timer);
132: timerset(&(cur.dk_time[i]), &tmp_timer);
133: timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
134: timerclear(&(last.dk_time[i]));
135: timerset(&tmp_timer, &(last.dk_time[i]));
136: }
137: for (i = 0; i < CPUSTATES; i++) {
138: SWAP(cp_time[i]);
139: }
140: SWAP(tk_nin);
141: SWAP(tk_nout);
142:
143: #undef SWAP
144: }
145:
146: /*
147: * Read the disk statistics for each disk in the disk list.
148: * Also collect statistics for tty i/o and cpu ticks.
149: */
150: void
151: dkreadstats()
152: {
153: struct disk cur_disk, *p;
1.10 ! angelos 154: int i, mib[3];
! 155: size_t size;
1.1 tholo 156:
1.10 ! angelos 157: if (nlistf == NULL && memf == NULL) {
! 158: size = dk_ndrive * sizeof(struct disk);
! 159: mib[0] = CTL_HW;
! 160: mib[1] = HW_DISKSTATS;
! 161: p = calloc(size, sizeof(char));
! 162: if (p == NULL)
! 163: err(1, NULL);
! 164: if (sysctl(mib, 2, p, &size, NULL, 0) < 0) {
! 165: warn("could not read hw.diskstats");
! 166: bzero(p, dk_ndrive * sizeof(struct disk));
! 167: }
! 168:
! 169: for (i = 0; i < dk_ndrive; i++) {
! 170: cur.dk_xfer[i] = p[i].dk_xfer;
! 171: cur.dk_seek[i] = p[i].dk_seek;
! 172: cur.dk_bytes[i] = p[i].dk_bytes;
! 173: timerset(&(p[i].dk_time), &(cur.dk_time[i]));
! 174: }
! 175:
! 176: size = sizeof(cur.cp_time);
! 177: mib[0] = CTL_KERN;
! 178: mib[1] = KERN_CPTIME;
! 179: if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
! 180: warnx("could not read kern.cp_time");
! 181: bzero(cur.cp_time, sizeof(cur.cp_time));
! 182: }
! 183: size = sizeof(cur.tk_nin);
! 184: mib[0] = CTL_KERN;
! 185: mib[1] = KERN_TTY;
! 186: mib[2] = KERN_TTY_TKNIN;
! 187: if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
! 188: warnx("could not read kern.tty.tk_nin");
! 189: cur.tk_nin = 0;
! 190: }
! 191: size = sizeof(cur.tk_nin);
! 192: mib[0] = CTL_KERN;
! 193: mib[1] = KERN_TTY;
! 194: mib[2] = KERN_TTY_TKNOUT;
! 195: if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
! 196: warnx("could not read kern.tty.tk_nout");
! 197: cur.tk_nout = 0;
! 198: }
! 199: } else {
! 200: p = dk_drivehead;
! 201:
! 202: for (i = 0; i < dk_ndrive; i++) {
! 203: deref_kptr(p, &cur_disk, sizeof(cur_disk));
! 204: cur.dk_xfer[i] = cur_disk.dk_xfer;
! 205: cur.dk_seek[i] = cur_disk.dk_seek;
! 206: cur.dk_bytes[i] = cur_disk.dk_bytes;
! 207: timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
! 208: p = cur_disk.dk_link.tqe_next;
! 209: }
! 210:
! 211: deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
! 212: deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
! 213: deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
1.1 tholo 214: }
215: }
216:
217: /*
218: * Perform all of the initialization and memory allocation needed to
219: * track disk statistics.
220: */
221: int
222: dkinit(select)
223: int select;
224: {
225: struct disklist_head disk_head;
226: struct disk cur_disk, *p;
227: char errbuf[_POSIX2_LINE_MAX];
228: static int once = 0;
229: extern int hz;
1.10 ! angelos 230: int i, mib[2];
! 231: size_t size;
! 232: struct clockinfo clkinfo;
! 233: char *disknames, *name, *bufpp;
1.1 tholo 234:
235: if (once)
236: return(1);
237:
1.10 ! angelos 238: if (nlistf != NULL || memf != NULL) {
! 239: /* Open the kernel. */
! 240: if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
! 241: errbuf)) == NULL)
! 242: errx(1, "kvm_openfiles: %s", errbuf);
! 243:
! 244: /* Obtain the namelist symbols from the kernel. */
! 245: if (kvm_nlist(kd, namelist))
! 246: KVM_ERROR("kvm_nlist failed to read symbols.");
! 247:
! 248: /* Get the number of attached drives. */
! 249: deref_nl(X_DISK_COUNT, &dk_ndrive, sizeof(dk_ndrive));
! 250:
! 251: if (dk_ndrive < 0)
! 252: errx(1, "invalid _disk_count %d.", dk_ndrive);
! 253:
1.1 tholo 254: /* Get a pointer to the first disk. */
255: deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
256: dk_drivehead = disk_head.tqh_first;
1.10 ! angelos 257:
! 258: /* Get ticks per second. */
! 259: deref_nl(X_STATHZ, &hz, sizeof(hz));
! 260: if (!hz)
! 261: deref_nl(X_HZ, &hz, sizeof(hz));
! 262: } else {
! 263: /* Get the number of attached drives. */
! 264: mib[0] = CTL_HW;
! 265: mib[1] = HW_DISKCOUNT;
! 266: size = sizeof(dk_ndrive);
! 267: if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
! 268: warnx("could not read hw.diskcount");
! 269: dk_ndrive = 0;
! 270: }
! 271:
! 272: /* Get ticks per second. */
! 273: mib[0] = CTL_KERN;
! 274: mib[1] = KERN_CLOCKRATE;
! 275: size = sizeof(clkinfo);
! 276: if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
! 277: warnx("could not read kern.clockrate");
! 278: hz = 0;
! 279: } else
! 280: hz = clkinfo.stathz;
1.1 tholo 281: }
282:
283: /* allocate space for the statistics */
284: cur.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
285: cur.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
286: cur.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
287: cur.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
288: last.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
289: last.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
290: last.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
291: last.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
292: cur.dk_select = calloc(dk_ndrive, sizeof(int));
293: cur.dk_name = calloc(dk_ndrive, sizeof(char *));
294:
1.9 deraadt 295: if (!cur.dk_time || !cur.dk_xfer || !cur.dk_seek || !cur.dk_bytes ||
296: !last.dk_time || !last.dk_xfer || !last.dk_seek ||
297: !last.dk_bytes || !cur.dk_select || !cur.dk_name)
1.1 tholo 298: errx(1, "Memory allocation failure.");
299:
300: /* Set up the compatibility interfaces. */
301: dk_select = cur.dk_select;
302: dr_name = cur.dk_name;
303:
304: /* Read the disk names and set intial selection. */
1.10 ! angelos 305: if (nlistf == NULL && memf == NULL) {
! 306: mib[0] = CTL_HW;
! 307: mib[1] = HW_DISKNAMES;
! 308: size = 0;
! 309: if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
! 310: err(1, "can't get hw.disknames");
! 311: disknames = calloc(size, sizeof(char));
! 312: if (disknames == NULL)
! 313: err(1, NULL);
! 314: if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
! 315: err(1, "can't get hw.disknames");
! 316: bufpp = disknames;
! 317: i = 0;
! 318: while ((name = strsep(&bufpp, ",")) != NULL) {
! 319: cur.dk_name[i] = name;
! 320: cur.dk_select[i++] = select;
! 321: }
! 322: } else {
! 323: p = dk_drivehead;
! 324: for (i = 0; i < dk_ndrive; i++) {
! 325: char buf[10];
! 326: deref_kptr(p, &cur_disk, sizeof(cur_disk));
! 327: deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
! 328: cur.dk_name[i] = strdup(buf);
! 329: if (!cur.dk_name[i])
! 330: errx(1, "Memory allocation failure.");
! 331: cur.dk_select[i] = select;
1.1 tholo 332:
1.10 ! angelos 333: p = cur_disk.dk_link.tqe_next;
! 334: }
1.1 tholo 335: }
336:
337: /* Never do this initalization again. */
338: once = 1;
339: return(1);
340: }
341:
342: /*
343: * Dereference the kernel pointer `kptr' and fill in the local copy
344: * pointed to by `ptr'. The storage space must be pre-allocated,
345: * and the size of the copy passed in `len'.
346: */
347: static void
348: deref_kptr(kptr, ptr, len)
349: void *kptr, *ptr;
350: size_t len;
351: {
352: char buf[128];
353:
1.6 art 354: if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
1.1 tholo 355: bzero(buf, sizeof(buf));
356: snprintf(buf, (sizeof(buf) - 1),
1.2 deraadt 357: "can't dereference kptr 0x%lx", (u_long)kptr);
1.1 tholo 358: KVM_ERROR(buf);
359: }
360: }