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