Annotation of src/usr.bin/vmstat/dkstats.c, Revision 1.3
1.3 ! deraadt 1: /* $OpenBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej 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:
36: #include <sys/dkstat.h>
37: #include <sys/time.h>
38: #include <sys/disk.h>
39:
40: #include <err.h>
41: #include <fcntl.h>
42: #include <kvm.h>
43: #include <limits.h>
44: #include <nlist.h>
45: #include <stdio.h>
46: #include <stdlib.h>
47: #include <string.h>
48: #include <unistd.h>
49: #include "dkstats.h"
50:
51: static struct nlist namelist[] = {
52: #define X_TK_NIN 0
53: { "_tk_nin" }, /* tty characters in */
54: #define X_TK_NOUT 1
55: { "_tk_nout" }, /* tty characters out */
56: #define X_CP_TIME 2
57: { "_cp_time" }, /* system timer ticks */
58: #define X_HZ 3
59: { "_hz" }, /* ticks per second */
60: #define X_STATHZ 4
61: { "_stathz" },
62: #define X_DISK_COUNT 5
63: { "_disk_count" }, /* number of disks */
64: #define X_DISKLIST 6
65: { "_disklist" }, /* TAILQ of disks */
66: { NULL },
67: };
68:
69: /* Structures to hold the statistics. */
70: struct _disk cur, last;
71:
72: /* Kernel pointers: nlistf and memf defined in calling program. */
73: static kvm_t *kd = NULL;
74: extern char *nlistf;
75: extern char *memf;
76:
77: /* Pointer to list of disks. */
78: static struct disk *dk_drivehead = NULL;
79:
80: /* Backward compatibility references. */
81: int dk_ndrive = 0;
82: int *dk_select;
83: char **dr_name;
84:
85: #define KVM_ERROR(_string) { \
86: warnx((_string)); \
87: errx(1, kvm_geterr(kd)); \
88: }
89:
90: /*
91: * Dereference the namelist pointer `v' and fill in the local copy
92: * 'p' which is of size 's'.
93: */
94: #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
95:
96: /* Missing from <sys/time.h> */
97: #define timerset(tvp, uvp) ((uvp)->tv_sec = (tvp)->tv_sec); \
98: ((uvp)->tv_usec = (tvp)->tv_usec)
99:
100: void dkswap __P((void));
101: void dkreadstats __P((void));
102: int dkinit __P((int));
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;
154: int i;
155:
156: p = dk_drivehead;
157:
158: for (i = 0; i < dk_ndrive; i++) {
159: deref_kptr(p, &cur_disk, sizeof(cur_disk));
160: cur.dk_xfer[i] = cur_disk.dk_xfer;
161: cur.dk_seek[i] = cur_disk.dk_seek;
162: cur.dk_bytes[i] = cur_disk.dk_bytes;
163: timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
164: p = cur_disk.dk_link.tqe_next;
165: }
166: deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
167: deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
168: deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
169: }
170:
171: /*
172: * Perform all of the initialization and memory allocation needed to
173: * track disk statistics.
174: */
175: int
176: dkinit(select)
177: int select;
178: {
179: struct disklist_head disk_head;
180: struct disk cur_disk, *p;
181: char errbuf[_POSIX2_LINE_MAX];
182: static int once = 0;
183: extern int hz;
184: int i;
185:
186: if (once)
187: return(1);
188:
189: /* Open the kernel. */
190: if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
191: errx(1, "kvm_openfiles: %s", errbuf);
192:
193: /* Obtain the namelist symbols from the kernel. */
194: if (kvm_nlist(kd, namelist))
195: KVM_ERROR("kvm_nlist failed to read symbols.");
196:
197: /* Get the number of attached drives. */
198: deref_nl(X_DISK_COUNT, &dk_ndrive, sizeof(dk_ndrive));
199:
200: if (dk_ndrive < 0)
201: errx(1, "invalid _disk_count %d.", dk_ndrive);
202: else if (dk_ndrive == 0) {
203: warnx("No drives attached.");
204: }
205: else {
206: /* Get a pointer to the first disk. */
207: deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
208: dk_drivehead = disk_head.tqh_first;
209: }
210:
211: /* Get ticks per second. */
212: deref_nl(X_STATHZ, &hz, sizeof(hz));
213: if (!hz)
214: deref_nl(X_HZ, &hz, sizeof(hz));
215:
216: /* allocate space for the statistics */
217: cur.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
218: cur.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
219: cur.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
220: cur.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
221: last.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
222: last.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
223: last.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
224: last.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
225: cur.dk_select = calloc(dk_ndrive, sizeof(int));
226: cur.dk_name = calloc(dk_ndrive, sizeof(char *));
227:
228: if (!cur.dk_time || !cur.dk_xfer || !cur.dk_seek || !cur.dk_bytes
229: || !last.dk_time || !last.dk_xfer || !last.dk_seek || !last.dk_bytes
230: || !cur.dk_select || !cur.dk_name)
231: errx(1, "Memory allocation failure.");
232:
233: /* Set up the compatibility interfaces. */
234: dk_select = cur.dk_select;
235: dr_name = cur.dk_name;
236:
237: /* Read the disk names and set intial selection. */
238: p = dk_drivehead;
239: for (i = 0; i < dk_ndrive; i++) {
240: char buf[10];
241: deref_kptr(p, &cur_disk, sizeof(cur_disk));
242: deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
243: cur.dk_name[i] = strdup(buf);
244: cur.dk_select[i] = select;
245:
246: p = cur_disk.dk_link.tqe_next;
247: }
248:
249: /* Never do this initalization again. */
250: once = 1;
251: return(1);
252: }
253:
254: /*
255: * Dereference the kernel pointer `kptr' and fill in the local copy
256: * pointed to by `ptr'. The storage space must be pre-allocated,
257: * and the size of the copy passed in `len'.
258: */
259: static void
260: deref_kptr(kptr, ptr, len)
261: void *kptr, *ptr;
262: size_t len;
263: {
264: char buf[128];
265:
266: if (kvm_read(kd, (u_long)kptr, (char *)ptr, len) != len) {
267: bzero(buf, sizeof(buf));
268: snprintf(buf, (sizeof(buf) - 1),
1.2 deraadt 269: "can't dereference kptr 0x%lx", (u_long)kptr);
1.1 tholo 270: KVM_ERROR(buf);
271: }
272: }