Annotation of src/usr.bin/vmstat/dkstats.c, Revision 1.2
1.2 ! deraadt 1: /* $NetBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej Exp $ */
1.1 tholo 2:
3: /*
1.2 ! deraadt 4: * Copyright (c) 1996 John M. Vinopal
1.1 tholo 5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
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
18: * by John M. Vinopal.
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.
21: *
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
32: * SUCH DAMAGE.
33: */
34:
35: #include <sys/dkstat.h>
36: #include <sys/time.h>
37: #include <sys/disk.h>
38:
39: #include <err.h>
40: #include <fcntl.h>
41: #include <kvm.h>
42: #include <limits.h>
43: #include <nlist.h>
44: #include <stdio.h>
45: #include <stdlib.h>
46: #include <string.h>
47: #include <unistd.h>
48: #include "dkstats.h"
49:
50: static struct nlist namelist[] = {
51: #define X_TK_NIN 0
52: { "_tk_nin" }, /* tty characters in */
53: #define X_TK_NOUT 1
54: { "_tk_nout" }, /* tty characters out */
55: #define X_CP_TIME 2
56: { "_cp_time" }, /* system timer ticks */
57: #define X_HZ 3
58: { "_hz" }, /* ticks per second */
59: #define X_STATHZ 4
60: { "_stathz" },
61: #define X_DISK_COUNT 5
62: { "_disk_count" }, /* number of disks */
63: #define X_DISKLIST 6
64: { "_disklist" }, /* TAILQ of disks */
65: { NULL },
66: };
67:
68: /* Structures to hold the statistics. */
69: struct _disk cur, last;
70:
71: /* Kernel pointers: nlistf and memf defined in calling program. */
72: static kvm_t *kd = NULL;
73: extern char *nlistf;
74: extern char *memf;
75:
76: /* Pointer to list of disks. */
77: static struct disk *dk_drivehead = NULL;
78:
79: /* Backward compatibility references. */
80: int dk_ndrive = 0;
81: int *dk_select;
82: char **dr_name;
83:
84: #define KVM_ERROR(_string) { \
85: warnx((_string)); \
86: errx(1, kvm_geterr(kd)); \
87: }
88:
89: /*
90: * Dereference the namelist pointer `v' and fill in the local copy
91: * 'p' which is of size 's'.
92: */
93: #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
94:
95: /* Missing from <sys/time.h> */
96: #define timerset(tvp, uvp) ((uvp)->tv_sec = (tvp)->tv_sec); \
97: ((uvp)->tv_usec = (tvp)->tv_usec)
98:
99: void dkswap __P((void));
100: void dkreadstats __P((void));
101: int dkinit __P((int));
1.2 ! deraadt 102: static void deref_kptr __P((void *, void *, size_t));
1.1 tholo 103:
104: /*
105: * Take the delta between the present values and the last recorded
106: * values, storing the present values in the 'last' structure, and
107: * the delta values in the 'cur' structure.
108: */
109: void
110: dkswap()
111: {
112: #define SWAP(fld) tmp = cur.fld; \
113: cur.fld -= last.fld; \
114: last.fld = tmp
115: u_int64_t tmp;
116: int i;
117:
118: for (i = 0; i < dk_ndrive; i++) {
119: struct timeval tmp_timer;
120:
121: if (!cur.dk_select[i])
122: continue;
123:
124: /* Delta Values. */
125: SWAP(dk_xfer[i]);
126: SWAP(dk_seek[i]);
127: SWAP(dk_bytes[i]);
128:
129: /* Delta Time. */
130: timerclear(&tmp_timer);
131: timerset(&(cur.dk_time[i]), &tmp_timer);
132: timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
133: timerclear(&(last.dk_time[i]));
134: timerset(&tmp_timer, &(last.dk_time[i]));
135: }
136: for (i = 0; i < CPUSTATES; i++) {
137: SWAP(cp_time[i]);
138: }
139: SWAP(tk_nin);
140: SWAP(tk_nout);
141:
142: #undef SWAP
143: }
144:
145: /*
146: * Read the disk statistics for each disk in the disk list.
147: * Also collect statistics for tty i/o and cpu ticks.
148: */
149: void
150: dkreadstats()
151: {
152: struct disk cur_disk, *p;
153: int i;
154:
155: p = dk_drivehead;
156:
157: for (i = 0; i < dk_ndrive; i++) {
158: deref_kptr(p, &cur_disk, sizeof(cur_disk));
159: cur.dk_xfer[i] = cur_disk.dk_xfer;
160: cur.dk_seek[i] = cur_disk.dk_seek;
161: cur.dk_bytes[i] = cur_disk.dk_bytes;
162: timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
163: p = cur_disk.dk_link.tqe_next;
164: }
165: deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
166: deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
167: deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
168: }
169:
170: /*
171: * Perform all of the initialization and memory allocation needed to
172: * track disk statistics.
173: */
174: int
175: dkinit(select)
176: int select;
177: {
178: struct disklist_head disk_head;
179: struct disk cur_disk, *p;
180: char errbuf[_POSIX2_LINE_MAX];
181: static int once = 0;
182: extern int hz;
183: int i;
184:
185: if (once)
186: return(1);
187:
188: /* Open the kernel. */
189: if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
190: errx(1, "kvm_openfiles: %s", errbuf);
191:
192: /* Obtain the namelist symbols from the kernel. */
193: if (kvm_nlist(kd, namelist))
194: KVM_ERROR("kvm_nlist failed to read symbols.");
195:
196: /* Get the number of attached drives. */
197: deref_nl(X_DISK_COUNT, &dk_ndrive, sizeof(dk_ndrive));
198:
199: if (dk_ndrive < 0)
200: errx(1, "invalid _disk_count %d.", dk_ndrive);
201: else if (dk_ndrive == 0) {
202: warnx("No drives attached.");
203: }
204: else {
205: /* Get a pointer to the first disk. */
206: deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
207: dk_drivehead = disk_head.tqh_first;
208: }
209:
210: /* Get ticks per second. */
211: deref_nl(X_STATHZ, &hz, sizeof(hz));
212: if (!hz)
213: deref_nl(X_HZ, &hz, sizeof(hz));
214:
215: /* allocate space for the statistics */
216: cur.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
217: cur.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
218: cur.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
219: cur.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
220: last.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
221: last.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
222: last.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
223: last.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
224: cur.dk_select = calloc(dk_ndrive, sizeof(int));
225: cur.dk_name = calloc(dk_ndrive, sizeof(char *));
226:
227: if (!cur.dk_time || !cur.dk_xfer || !cur.dk_seek || !cur.dk_bytes
228: || !last.dk_time || !last.dk_xfer || !last.dk_seek || !last.dk_bytes
229: || !cur.dk_select || !cur.dk_name)
230: errx(1, "Memory allocation failure.");
231:
232: /* Set up the compatibility interfaces. */
233: dk_select = cur.dk_select;
234: dr_name = cur.dk_name;
235:
236: /* Read the disk names and set intial selection. */
237: p = dk_drivehead;
238: for (i = 0; i < dk_ndrive; i++) {
239: char buf[10];
240: deref_kptr(p, &cur_disk, sizeof(cur_disk));
241: deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
242: cur.dk_name[i] = strdup(buf);
243: cur.dk_select[i] = select;
244:
245: p = cur_disk.dk_link.tqe_next;
246: }
247:
248: /* Never do this initalization again. */
249: once = 1;
250: return(1);
251: }
252:
253: /*
254: * Dereference the kernel pointer `kptr' and fill in the local copy
255: * pointed to by `ptr'. The storage space must be pre-allocated,
256: * and the size of the copy passed in `len'.
257: */
258: static void
259: deref_kptr(kptr, ptr, len)
260: void *kptr, *ptr;
261: size_t len;
262: {
263: char buf[128];
264:
265: if (kvm_read(kd, (u_long)kptr, (char *)ptr, len) != len) {
266: bzero(buf, sizeof(buf));
267: snprintf(buf, (sizeof(buf) - 1),
1.2 ! deraadt 268: "can't dereference kptr 0x%lx", (u_long)kptr);
1.1 tholo 269: KVM_ERROR(buf);
270: }
271: }