1 /*	$OpenBSD: dkstats.c,v 1.26 2005/07/04 01:54:10 djm Exp $	*/
2 /*	$NetBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996 John M. Vinopal
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/param.h>
37 #include <sys/dkstat.h>
38 #include <sys/time.h>
39 #include <sys/disk.h>
40 #include <sys/sysctl.h>
41 #include <sys/tty.h>
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 #if !defined(NOKVM)
55 static struct nlist namelist[] = {
56 #define	X_TK_NIN	0		/* sysctl */
57 	{ "_tk_nin" },
58 #define	X_TK_NOUT	1		/* sysctl */
59 	{ "_tk_nout" },
60 #define	X_CP_TIME	2		/* sysctl */
61 	{ "_cp_time" },
62 #define	X_HZ		3		/* sysctl */
63 	{ "_hz" },
64 #define	X_STATHZ	4		/* sysctl */
65 	{ "_stathz" },
66 #define X_DISK_COUNT	5		/* sysctl */
67 	{ "_disk_count" },
68 #define X_DISKLIST	6		/* sysctl */
69 	{ "_disklist" },
70 	{ NULL },
71 };
72 #define	KVM_ERROR(_string) {						\
73 	warnx("%s", (_string));						\
74 	errx(1, "%s", kvm_geterr(kd));					\
75 }
76 
77 /*
78  * Dereference the namelist pointer `v' and fill in the local copy
79  * 'p' which is of size 's'.
80  */
81 #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
82 static void deref_kptr(void *, void *, size_t);
83 #endif /* !defined(NOKVM) */
84 
85 /* Structures to hold the statistics. */
86 struct _disk	cur, last;
87 
88 /* Kernel pointers: nlistf and memf defined in calling program. */
89 #if !defined(NOKVM)
90 extern kvm_t	*kd;
91 #endif
92 extern char	*nlistf;
93 extern char	*memf;
94 
95 #if !defined(NOKVM)
96 /* Pointer to list of disks. */
97 static struct disk	*dk_drivehead = NULL;
98 #endif
99 
100 /* Backward compatibility references. */
101 int	  	dk_ndrive = 0;
102 int		*dk_select;
103 char		**dr_name;
104 
105 /* Missing from <sys/time.h> */
106 #define timerset(tvp, uvp) \
107 	((uvp)->tv_sec = (tvp)->tv_sec);		\
108 	((uvp)->tv_usec = (tvp)->tv_usec)
109 
110 #define SWAP(fld)	tmp = cur.fld;				\
111 			cur.fld -= last.fld;			\
112 			last.fld = tmp
113 
114 /*
115  * Take the delta between the present values and the last recorded
116  * values, storing the present values in the 'last' structure, and
117  * the delta values in the 'cur' structure.
118  */
119 void
dkswap(void)120 dkswap(void)
121 {
122 	u_int64_t tmp;
123 	int	i;
124 
125 	for (i = 0; i < cur.dk_ndrive; i++) {
126 		struct timeval	tmp_timer;
127 
128 		if (!cur.dk_select[i])
129 			continue;
130 
131 		/* Delta Values. */
132 		SWAP(dk_rxfer[i]);
133 		SWAP(dk_wxfer[i]);
134 		SWAP(dk_seek[i]);
135 		SWAP(dk_rbytes[i]);
136 		SWAP(dk_wbytes[i]);
137 
138 		/* Delta Time. */
139 		timerclear(&tmp_timer);
140 		timerset(&(cur.dk_time[i]), &tmp_timer);
141 		timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
142 		timerclear(&(last.dk_time[i]));
143 		timerset(&tmp_timer, &(last.dk_time[i]));
144 	}
145 	for (i = 0; i < CPUSTATES; i++) {
146 		SWAP(cp_time[i]);
147 	}
148 	SWAP(tk_nin);
149 	SWAP(tk_nout);
150 
151 #undef SWAP
152 }
153 
154 /*
155  * Read the disk statistics for each disk in the disk list.
156  * Also collect statistics for tty i/o and cpu ticks.
157  */
158 void
dkreadstats(void)159 dkreadstats(void)
160 {
161 #if !defined(NOKVM)
162 	struct disk	cur_disk, *p;
163 #endif
164 	int		i, j, mib[3];
165 	size_t		size;
166 	char		*disknames, *name, *bufpp, **dk_name;
167 	struct diskstats *q;
168 
169 	last.dk_ndrive = cur.dk_ndrive;
170 
171 	if (nlistf == NULL && memf == NULL) {
172 		/* Get the number of attached drives. */
173 		mib[0] = CTL_HW;
174 		mib[1] = HW_DISKCOUNT;
175 		size = sizeof(dk_ndrive);
176 		if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
177 			warn("could not read hw.diskcount");
178 			dk_ndrive = 0;
179 		}
180 
181 		if (cur.dk_ndrive != dk_ndrive) {
182 			/* Re-read the disk names. */
183 			dk_name = calloc(dk_ndrive, sizeof(char *));
184 			if (dk_name == NULL)
185 				err(1, NULL);
186 			mib[0] = CTL_HW;
187 			mib[1] = HW_DISKNAMES;
188 			size = 0;
189 			if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
190 				err(1, "can't get hw.disknames");
191 			disknames = malloc(size);
192 			if (disknames == NULL)
193 				err(1, NULL);
194 			if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
195 				err(1, "can't get hw.disknames");
196 			bufpp = disknames;
197 			for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++)
198 				dk_name[i] = name;
199 			disknames = cur.dk_name[0];	/* To free old names. */
200 
201 			if (dk_ndrive < cur.dk_ndrive) {
202 				for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
203 					while (j < cur.dk_ndrive &&
204 					    strcmp(cur.dk_name[j], dk_name[i]))
205 						j++;
206 					if (i == j) continue;
207 
208 					if (j >= cur.dk_ndrive) {
209 						cur.dk_select[i] = 1;
210 						last.dk_rxfer[i] = 0;
211 						last.dk_wxfer[i] = 0;
212 						last.dk_seek[i] = 0;
213 						last.dk_rbytes[i] = 0;
214 						last.dk_wbytes[i] = 0;
215 						bzero(&last.dk_time[i],
216 						    sizeof(struct timeval));
217 						continue;
218 					}
219 
220 					cur.dk_select[i] = cur.dk_select[j];
221 					last.dk_rxfer[i] = last.dk_rxfer[j];
222 					last.dk_wxfer[i] = last.dk_wxfer[j];
223 					last.dk_seek[i] = last.dk_seek[j];
224 					last.dk_rbytes[i] = last.dk_rbytes[j];
225 					last.dk_wbytes[i] = last.dk_wbytes[j];
226 					last.dk_time[i] = last.dk_time[j];
227 				}
228 
229 				cur.dk_select = realloc(cur.dk_select,
230 				    dk_ndrive * sizeof(*cur.dk_select));
231 				cur.dk_rxfer = realloc(cur.dk_rxfer,
232 				    dk_ndrive * sizeof(*cur.dk_rxfer));
233 				cur.dk_wxfer = realloc(cur.dk_wxfer,
234 				    dk_ndrive * sizeof(*cur.dk_wxfer));
235 				cur.dk_seek = realloc(cur.dk_seek,
236 				    dk_ndrive * sizeof(*cur.dk_seek));
237 				cur.dk_rbytes = realloc(cur.dk_rbytes,
238 				    dk_ndrive * sizeof(*cur.dk_rbytes));
239 				cur.dk_wbytes = realloc(cur.dk_wbytes,
240 				    dk_ndrive * sizeof(*cur.dk_wbytes));
241 				cur.dk_time = realloc(cur.dk_time,
242 				    dk_ndrive * sizeof(*cur.dk_time));
243 				last.dk_rxfer = realloc(last.dk_rxfer,
244 				    dk_ndrive * sizeof(*last.dk_rxfer));
245 				last.dk_wxfer = realloc(last.dk_wxfer,
246 				    dk_ndrive * sizeof(*last.dk_wxfer));
247 				last.dk_seek = realloc(last.dk_seek,
248 				    dk_ndrive * sizeof(*last.dk_seek));
249 				last.dk_rbytes = realloc(last.dk_rbytes,
250 				    dk_ndrive * sizeof(*last.dk_rbytes));
251 				last.dk_wbytes = realloc(last.dk_wbytes,
252 				    dk_ndrive * sizeof(*last.dk_wbytes));
253 				last.dk_time = realloc(last.dk_time,
254 				    dk_ndrive * sizeof(*last.dk_time));
255 			} else {
256 				cur.dk_select = realloc(cur.dk_select,
257 				    dk_ndrive * sizeof(*cur.dk_select));
258 				cur.dk_rxfer = realloc(cur.dk_rxfer,
259 				    dk_ndrive * sizeof(*cur.dk_rxfer));
260 				cur.dk_wxfer = realloc(cur.dk_wxfer,
261 				    dk_ndrive * sizeof(*cur.dk_wxfer));
262 				cur.dk_seek = realloc(cur.dk_seek,
263 				    dk_ndrive * sizeof(*cur.dk_seek));
264 				cur.dk_rbytes = realloc(cur.dk_rbytes,
265 				    dk_ndrive * sizeof(*cur.dk_rbytes));
266 				cur.dk_wbytes = realloc(cur.dk_wbytes,
267 				    dk_ndrive * sizeof(*cur.dk_wbytes));
268 				cur.dk_time = realloc(cur.dk_time,
269 				    dk_ndrive * sizeof(*cur.dk_time));
270 				last.dk_rxfer = realloc(last.dk_rxfer,
271 				    dk_ndrive * sizeof(*last.dk_rxfer));
272 				last.dk_wxfer = realloc(last.dk_wxfer,
273 				    dk_ndrive * sizeof(*last.dk_wxfer));
274 				last.dk_seek = realloc(last.dk_seek,
275 				    dk_ndrive * sizeof(*last.dk_seek));
276 				last.dk_rbytes = realloc(last.dk_rbytes,
277 				    dk_ndrive * sizeof(*last.dk_rbytes));
278 				last.dk_wbytes = realloc(last.dk_wbytes,
279 				    dk_ndrive * sizeof(*last.dk_wbytes));
280 				last.dk_time = realloc(last.dk_time,
281 				    dk_ndrive * sizeof(*last.dk_time));
282 
283 				for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
284 				     i >= 0; i--) {
285 
286 					if (j < 0 ||
287 					    strcmp(cur.dk_name[j], dk_name[i]))
288 					{
289 						cur.dk_select[i] = 1;
290 						last.dk_rxfer[i] = 0;
291 						last.dk_wxfer[i] = 0;
292 						last.dk_seek[i] = 0;
293 						last.dk_rbytes[i] = 0;
294 						last.dk_wbytes[i] = 0;
295 						bzero(&last.dk_time[i],
296 						    sizeof(struct timeval));
297 						continue;
298 					}
299 
300 					if (i > j) {
301 						cur.dk_select[i] =
302 						    cur.dk_select[j];
303 						last.dk_rxfer[i] =
304 						    last.dk_rxfer[j];
305 						last.dk_wxfer[i] =
306 						    last.dk_wxfer[j];
307 						last.dk_seek[i] =
308 						    last.dk_seek[j];
309 						last.dk_rbytes[i] =
310 						    last.dk_rbytes[j];
311 						last.dk_wbytes[i] =
312 						    last.dk_wbytes[j];
313 						last.dk_time[i] =
314 						    last.dk_time[j];
315 					}
316 					j--;
317 				}
318 			}
319 
320 			cur.dk_ndrive = dk_ndrive;
321 			free(disknames);
322 			cur.dk_name = dk_name;
323 			dr_name = cur.dk_name;
324 			dk_select = cur.dk_select;
325 		}
326 
327 		size = cur.dk_ndrive * sizeof(struct diskstats);
328 		mib[0] = CTL_HW;
329 		mib[1] = HW_DISKSTATS;
330 		q = malloc(size);
331 		if (q == NULL)
332 			err(1, NULL);
333 		if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
334 #ifdef	DEBUG
335 			warn("could not read hw.diskstats");
336 #endif	/* DEBUG */
337 			bzero(q, cur.dk_ndrive * sizeof(struct diskstats));
338 		}
339 
340 		for (i = 0; i < cur.dk_ndrive; i++)	{
341 			cur.dk_rxfer[i] = q[i].ds_rxfer;
342 			cur.dk_wxfer[i] = q[i].ds_wxfer;
343 			cur.dk_seek[i] = q[i].ds_seek;
344 			cur.dk_rbytes[i] = q[i].ds_rbytes;
345 			cur.dk_wbytes[i] = q[i].ds_wbytes;
346 			timerset(&(q[i].ds_time), &(cur.dk_time[i]));
347 		}
348 		free(q);
349 
350 	 	size = sizeof(cur.cp_time);
351 		mib[0] = CTL_KERN;
352 		mib[1] = KERN_CPTIME;
353 		if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
354 			warn("could not read kern.cp_time");
355 			bzero(cur.cp_time, sizeof(cur.cp_time));
356 		}
357 		size = sizeof(cur.tk_nin);
358 		mib[0] = CTL_KERN;
359 		mib[1] = KERN_TTY;
360 		mib[2] = KERN_TTY_TKNIN;
361 		if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
362 			warn("could not read kern.tty.tk_nin");
363 			cur.tk_nin = 0;
364 		}
365 		size = sizeof(cur.tk_nin);
366 		mib[0] = CTL_KERN;
367 		mib[1] = KERN_TTY;
368 		mib[2] = KERN_TTY_TKNOUT;
369 		if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
370 			warn("could not read kern.tty.tk_nout");
371 			cur.tk_nout = 0;
372 		}
373 	} else {
374 #if !defined(NOKVM)
375 		p = dk_drivehead;
376 
377 		for (i = 0; i < cur.dk_ndrive; i++) {
378 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
379 			cur.dk_rxfer[i] = cur_disk.dk_rxfer;
380 			cur.dk_wxfer[i] = cur_disk.dk_wxfer;
381 			cur.dk_seek[i] = cur_disk.dk_seek;
382 			cur.dk_rbytes[i] = cur_disk.dk_rbytes;
383 			cur.dk_wbytes[i] = cur_disk.dk_wbytes;
384 			timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
385 			p = cur_disk.dk_link.tqe_next;
386 		}
387 		deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
388 		deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
389 		deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
390 #endif /* !defined(NOKVM) */
391 	}
392 }
393 
394 /*
395  * Perform all of the initialization and memory allocation needed to
396  * track disk statistics.
397  */
398 int
dkinit(int select)399 dkinit(int select)
400 {
401 #if !defined(NOKVM)
402 	struct disklist_head disk_head;
403 	struct disk	cur_disk, *p;
404         char		errbuf[_POSIX2_LINE_MAX];
405 #endif
406 	static int	once = 0;
407 	extern int	hz;
408 	int		i, mib[2];
409 	size_t		size;
410 	struct clockinfo clkinfo;
411 	char		*disknames, *name, *bufpp;
412 	gid_t		gid;
413 
414 	if (once)
415 		return(1);
416 
417 	gid = getgid();
418 	if (nlistf != NULL || memf != NULL) {
419 #if !defined(NOKVM)
420 		if (memf != NULL)
421 			if (setresgid(gid, gid, gid) == -1)
422 				err(1, "setresgid");
423 
424 		/* Open the kernel. */
425 		if (kd == NULL &&
426 		    (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
427 		    errbuf)) == NULL)
428 			errx(1, "kvm_openfiles: %s", errbuf);
429 
430 		if (memf == NULL)
431 			if (setresgid(gid, gid, gid) == -1)
432 				err(1, "setresgid");
433 
434 		/* Obtain the namelist symbols from the kernel. */
435 		if (kvm_nlist(kd, namelist))
436 			KVM_ERROR("kvm_nlist failed to read symbols.");
437 
438 		/* Get the number of attached drives. */
439 		deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
440 
441 		if (cur.dk_ndrive < 0)
442 			errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
443 
444 		/* Get a pointer to the first disk. */
445 		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
446 		dk_drivehead = disk_head.tqh_first;
447 
448 		/* Get ticks per second. */
449 		deref_nl(X_STATHZ, &hz, sizeof(hz));
450 		if (!hz)
451 		  deref_nl(X_HZ, &hz, sizeof(hz));
452 #endif /* !defined(NOKVM) */
453 	} else {
454 		/* Get the number of attached drives. */
455 		mib[0] = CTL_HW;
456 		mib[1] = HW_DISKCOUNT;
457 		size = sizeof(cur.dk_ndrive);
458 		if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) {
459 			warn("could not read hw.diskcount");
460 			cur.dk_ndrive = 0;
461 		}
462 
463 		/* Get ticks per second. */
464 		mib[0] = CTL_KERN;
465 		mib[1] = KERN_CLOCKRATE;
466 		size = sizeof(clkinfo);
467 		if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
468 			warn("could not read kern.clockrate");
469 			hz = 0;
470 		} else
471 			hz = clkinfo.stathz;
472 	}
473 
474 	/* allocate space for the statistics */
475 	cur.dk_time = calloc(cur.dk_ndrive, sizeof(struct timeval));
476 	cur.dk_rxfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
477 	cur.dk_wxfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
478 	cur.dk_seek = calloc(cur.dk_ndrive, sizeof(u_int64_t));
479 	cur.dk_rbytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
480 	cur.dk_wbytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
481 	last.dk_time = calloc(cur.dk_ndrive, sizeof(struct timeval));
482 	last.dk_rxfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
483 	last.dk_wxfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
484 	last.dk_seek = calloc(cur.dk_ndrive, sizeof(u_int64_t));
485 	last.dk_rbytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
486 	last.dk_wbytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
487 	cur.dk_select = calloc(cur.dk_ndrive, sizeof(int));
488 	cur.dk_name = calloc(cur.dk_ndrive, sizeof(char *));
489 
490 	if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek ||
491 	    !cur.dk_rbytes || !cur.dk_wbytes || !last.dk_time ||
492 	    !last.dk_rxfer || !last.dk_wxfer || !last.dk_seek ||
493 	    !cur.dk_select || !cur.dk_name)
494 		errx(1, "Memory allocation failure.");
495 
496 	/* Set up the compatibility interfaces. */
497 	dk_ndrive = cur.dk_ndrive;
498 	dk_select = cur.dk_select;
499 	dr_name = cur.dk_name;
500 
501 	/* Read the disk names and set initial selection. */
502 	if (nlistf == NULL && memf == NULL) {
503 		mib[0] = CTL_HW;
504 		mib[1] = HW_DISKNAMES;
505 		size = 0;
506 		if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
507 			err(1, "can't get hw.disknames");
508 		disknames = malloc(size);
509 		if (disknames == NULL)
510 			err(1, NULL);
511 		if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
512 			err(1, "can't get hw.disknames");
513 		bufpp = disknames;
514 		for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
515 			cur.dk_name[i] = name;
516 			cur.dk_select[i] = select;
517 		}
518 	} else {
519 #if !defined(NOKVM)
520 		p = dk_drivehead;
521 		for (i = 0; i < cur.dk_ndrive; i++) {
522 			char	buf[10];
523 
524 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
525 			deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
526 			cur.dk_name[i] = strdup(buf);
527 			if (!cur.dk_name[i])
528 				errx(1, "Memory allocation failure.");
529 			cur.dk_select[i] = select;
530 
531 			p = cur_disk.dk_link.tqe_next;
532 		}
533 #endif /* !defined(NOKVM) */
534 	}
535 
536 	/* Never do this initalization again. */
537 	once = 1;
538 	return(1);
539 }
540 
541 #if !defined(NOKVM)
542 /*
543  * Dereference the kernel pointer `kptr' and fill in the local copy
544  * pointed to by `ptr'.  The storage space must be pre-allocated,
545  * and the size of the copy passed in `len'.
546  */
547 static void
deref_kptr(void * kptr,void * ptr,size_t len)548 deref_kptr(void *kptr, void *ptr, size_t len)
549 {
550 	char buf[128];
551 
552 	if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
553 		bzero(buf, sizeof(buf));
554 		snprintf(buf, (sizeof(buf) - 1),
555 		     "can't dereference kptr 0x%lx", (u_long)kptr);
556 		KVM_ERROR(buf);
557 	}
558 }
559 #endif /* !defined(NOKVM) */
560