1 /* $OpenBSD: dkstats.c,v 1.33 2007/11/26 09:28:34 martynas 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 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 long ltmp; 147 148 ltmp = cur.cp_time[i]; 149 cur.cp_time[i] -= last.cp_time[i]; 150 last.cp_time[i] = ltmp; 151 } 152 SWAP(tk_nin); 153 SWAP(tk_nout); 154 155 #undef SWAP 156 } 157 158 /* 159 * Read the disk statistics for each disk in the disk list. 160 * Also collect statistics for tty i/o and cpu ticks. 161 */ 162 void 163 dkreadstats(void) 164 { 165 #if !defined(NOKVM) 166 struct disk cur_disk, *p; 167 #endif 168 int i, j, mib[3]; 169 size_t size; 170 char *disknames, *name, *bufpp, **dk_name; 171 struct diskstats *q; 172 173 last.dk_ndrive = cur.dk_ndrive; 174 175 if (nlistf == NULL && memf == NULL) { 176 /* Get the number of attached drives. */ 177 mib[0] = CTL_HW; 178 mib[1] = HW_DISKCOUNT; 179 size = sizeof(dk_ndrive); 180 if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) { 181 warn("could not read hw.diskcount"); 182 dk_ndrive = 0; 183 } 184 185 if (cur.dk_ndrive != dk_ndrive) { 186 /* Re-read the disk names. */ 187 dk_name = calloc((size_t)dk_ndrive, sizeof(char *)); 188 if (dk_name == NULL) 189 err(1, NULL); 190 mib[0] = CTL_HW; 191 mib[1] = HW_DISKNAMES; 192 size = 0; 193 if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) 194 err(1, "can't get hw.disknames"); 195 disknames = malloc(size); 196 if (disknames == NULL) 197 err(1, NULL); 198 if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0) 199 err(1, "can't get hw.disknames"); 200 bufpp = disknames; 201 for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) 202 dk_name[i] = name; 203 disknames = cur.dk_name[0]; /* To free old names. */ 204 205 if (dk_ndrive < cur.dk_ndrive) { 206 for (i = 0, j = 0; i < dk_ndrive; i++, j++) { 207 while (j < cur.dk_ndrive && 208 strcmp(cur.dk_name[j], dk_name[i])) 209 j++; 210 if (i == j) continue; 211 212 if (j >= cur.dk_ndrive) { 213 cur.dk_select[i] = 1; 214 last.dk_rxfer[i] = 0; 215 last.dk_wxfer[i] = 0; 216 last.dk_seek[i] = 0; 217 last.dk_rbytes[i] = 0; 218 last.dk_wbytes[i] = 0; 219 bzero(&last.dk_time[i], 220 sizeof(struct timeval)); 221 continue; 222 } 223 224 cur.dk_select[i] = cur.dk_select[j]; 225 last.dk_rxfer[i] = last.dk_rxfer[j]; 226 last.dk_wxfer[i] = last.dk_wxfer[j]; 227 last.dk_seek[i] = last.dk_seek[j]; 228 last.dk_rbytes[i] = last.dk_rbytes[j]; 229 last.dk_wbytes[i] = last.dk_wbytes[j]; 230 last.dk_time[i] = last.dk_time[j]; 231 } 232 233 cur.dk_select = realloc(cur.dk_select, 234 dk_ndrive * sizeof(*cur.dk_select)); 235 cur.dk_rxfer = realloc(cur.dk_rxfer, 236 dk_ndrive * sizeof(*cur.dk_rxfer)); 237 cur.dk_wxfer = realloc(cur.dk_wxfer, 238 dk_ndrive * sizeof(*cur.dk_wxfer)); 239 cur.dk_seek = realloc(cur.dk_seek, 240 dk_ndrive * sizeof(*cur.dk_seek)); 241 cur.dk_rbytes = realloc(cur.dk_rbytes, 242 dk_ndrive * sizeof(*cur.dk_rbytes)); 243 cur.dk_wbytes = realloc(cur.dk_wbytes, 244 dk_ndrive * sizeof(*cur.dk_wbytes)); 245 cur.dk_time = realloc(cur.dk_time, 246 dk_ndrive * sizeof(*cur.dk_time)); 247 last.dk_rxfer = realloc(last.dk_rxfer, 248 dk_ndrive * sizeof(*last.dk_rxfer)); 249 last.dk_wxfer = realloc(last.dk_wxfer, 250 dk_ndrive * sizeof(*last.dk_wxfer)); 251 last.dk_seek = realloc(last.dk_seek, 252 dk_ndrive * sizeof(*last.dk_seek)); 253 last.dk_rbytes = realloc(last.dk_rbytes, 254 dk_ndrive * sizeof(*last.dk_rbytes)); 255 last.dk_wbytes = realloc(last.dk_wbytes, 256 dk_ndrive * sizeof(*last.dk_wbytes)); 257 last.dk_time = realloc(last.dk_time, 258 dk_ndrive * sizeof(*last.dk_time)); 259 260 if (!cur.dk_select || !cur.dk_rxfer || 261 !cur.dk_wxfer || !cur.dk_seek || 262 !cur.dk_rbytes || !cur.dk_wbytes || 263 !cur.dk_time || !last.dk_rxfer || 264 !last.dk_wxfer || !last.dk_seek || 265 !last.dk_rbytes || !last.dk_wbytes || 266 !last.dk_time) 267 errx(1, "Memory allocation failure."); 268 } else { 269 cur.dk_select = realloc(cur.dk_select, 270 dk_ndrive * sizeof(*cur.dk_select)); 271 cur.dk_rxfer = realloc(cur.dk_rxfer, 272 dk_ndrive * sizeof(*cur.dk_rxfer)); 273 cur.dk_wxfer = realloc(cur.dk_wxfer, 274 dk_ndrive * sizeof(*cur.dk_wxfer)); 275 cur.dk_seek = realloc(cur.dk_seek, 276 dk_ndrive * sizeof(*cur.dk_seek)); 277 cur.dk_rbytes = realloc(cur.dk_rbytes, 278 dk_ndrive * sizeof(*cur.dk_rbytes)); 279 cur.dk_wbytes = realloc(cur.dk_wbytes, 280 dk_ndrive * sizeof(*cur.dk_wbytes)); 281 cur.dk_time = realloc(cur.dk_time, 282 dk_ndrive * sizeof(*cur.dk_time)); 283 last.dk_rxfer = realloc(last.dk_rxfer, 284 dk_ndrive * sizeof(*last.dk_rxfer)); 285 last.dk_wxfer = realloc(last.dk_wxfer, 286 dk_ndrive * sizeof(*last.dk_wxfer)); 287 last.dk_seek = realloc(last.dk_seek, 288 dk_ndrive * sizeof(*last.dk_seek)); 289 last.dk_rbytes = realloc(last.dk_rbytes, 290 dk_ndrive * sizeof(*last.dk_rbytes)); 291 last.dk_wbytes = realloc(last.dk_wbytes, 292 dk_ndrive * sizeof(*last.dk_wbytes)); 293 last.dk_time = realloc(last.dk_time, 294 dk_ndrive * sizeof(*last.dk_time)); 295 296 if (!cur.dk_select || !cur.dk_rxfer || 297 !cur.dk_wxfer || !cur.dk_seek || 298 !cur.dk_rbytes || !cur.dk_wbytes || 299 !cur.dk_time || !last.dk_rxfer || 300 !last.dk_wxfer || !last.dk_seek || 301 !last.dk_rbytes || !last.dk_wbytes || 302 !last.dk_time) 303 errx(1, "Memory allocation failure."); 304 305 for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1; 306 i >= 0; i--) { 307 308 if (j < 0 || 309 strcmp(cur.dk_name[j], dk_name[i])) 310 { 311 cur.dk_select[i] = 1; 312 last.dk_rxfer[i] = 0; 313 last.dk_wxfer[i] = 0; 314 last.dk_seek[i] = 0; 315 last.dk_rbytes[i] = 0; 316 last.dk_wbytes[i] = 0; 317 bzero(&last.dk_time[i], 318 sizeof(struct timeval)); 319 continue; 320 } 321 322 if (i > j) { 323 cur.dk_select[i] = 324 cur.dk_select[j]; 325 last.dk_rxfer[i] = 326 last.dk_rxfer[j]; 327 last.dk_wxfer[i] = 328 last.dk_wxfer[j]; 329 last.dk_seek[i] = 330 last.dk_seek[j]; 331 last.dk_rbytes[i] = 332 last.dk_rbytes[j]; 333 last.dk_wbytes[i] = 334 last.dk_wbytes[j]; 335 last.dk_time[i] = 336 last.dk_time[j]; 337 } 338 j--; 339 } 340 } 341 342 cur.dk_ndrive = dk_ndrive; 343 free(disknames); 344 cur.dk_name = dk_name; 345 dr_name = cur.dk_name; 346 dk_select = cur.dk_select; 347 } 348 349 size = cur.dk_ndrive * sizeof(struct diskstats); 350 mib[0] = CTL_HW; 351 mib[1] = HW_DISKSTATS; 352 q = malloc(size); 353 if (q == NULL) 354 err(1, NULL); 355 if (sysctl(mib, 2, q, &size, NULL, 0) < 0) { 356 #ifdef DEBUG 357 warn("could not read hw.diskstats"); 358 #endif /* DEBUG */ 359 bzero(q, cur.dk_ndrive * sizeof(struct diskstats)); 360 } 361 362 for (i = 0; i < cur.dk_ndrive; i++) { 363 cur.dk_rxfer[i] = q[i].ds_rxfer; 364 cur.dk_wxfer[i] = q[i].ds_wxfer; 365 cur.dk_seek[i] = q[i].ds_seek; 366 cur.dk_rbytes[i] = q[i].ds_rbytes; 367 cur.dk_wbytes[i] = q[i].ds_wbytes; 368 timerset(&(q[i].ds_time), &(cur.dk_time[i])); 369 } 370 free(q); 371 372 size = sizeof(cur.cp_time); 373 mib[0] = CTL_KERN; 374 mib[1] = KERN_CPTIME; 375 if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) { 376 warn("could not read kern.cp_time"); 377 bzero(cur.cp_time, sizeof(cur.cp_time)); 378 } 379 size = sizeof(cur.tk_nin); 380 mib[0] = CTL_KERN; 381 mib[1] = KERN_TTY; 382 mib[2] = KERN_TTY_TKNIN; 383 if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) { 384 warn("could not read kern.tty.tk_nin"); 385 cur.tk_nin = 0; 386 } 387 size = sizeof(cur.tk_nin); 388 mib[0] = CTL_KERN; 389 mib[1] = KERN_TTY; 390 mib[2] = KERN_TTY_TKNOUT; 391 if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) { 392 warn("could not read kern.tty.tk_nout"); 393 cur.tk_nout = 0; 394 } 395 } else { 396 #if !defined(NOKVM) 397 p = dk_drivehead; 398 399 for (i = 0; i < cur.dk_ndrive; i++) { 400 deref_kptr(p, &cur_disk, sizeof(cur_disk)); 401 cur.dk_rxfer[i] = cur_disk.dk_rxfer; 402 cur.dk_wxfer[i] = cur_disk.dk_wxfer; 403 cur.dk_seek[i] = cur_disk.dk_seek; 404 cur.dk_rbytes[i] = cur_disk.dk_rbytes; 405 cur.dk_wbytes[i] = cur_disk.dk_wbytes; 406 timerset(&(cur_disk.dk_time), &(cur.dk_time[i])); 407 p = TAILQ_NEXT(&cur_disk, dk_link); 408 } 409 deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time)); 410 deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin)); 411 deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout)); 412 #endif /* !defined(NOKVM) */ 413 } 414 } 415 416 /* 417 * Perform all of the initialization and memory allocation needed to 418 * track disk statistics. 419 */ 420 int 421 dkinit(int sel) 422 { 423 #if !defined(NOKVM) 424 struct disklist_head disk_head; 425 struct disk cur_disk, *p; 426 char errbuf[_POSIX2_LINE_MAX]; 427 #endif 428 static int once = 0; 429 extern int hz; 430 int i, mib[2]; 431 size_t size; 432 struct clockinfo clkinfo; 433 char *disknames, *name, *bufpp; 434 gid_t gid; 435 436 if (once) 437 return(1); 438 439 gid = getgid(); 440 if (nlistf != NULL || memf != NULL) { 441 #if !defined(NOKVM) 442 if (memf != NULL) 443 if (setresgid(gid, gid, gid) == -1) 444 err(1, "setresgid"); 445 446 /* Open the kernel. */ 447 if (kd == NULL && 448 (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, 449 errbuf)) == NULL) 450 errx(1, "kvm_openfiles: %s", errbuf); 451 452 if (memf == NULL) 453 if (setresgid(gid, gid, gid) == -1) 454 err(1, "setresgid"); 455 456 /* Obtain the namelist symbols from the kernel. */ 457 if (kvm_nlist(kd, namelist)) 458 KVM_ERROR("kvm_nlist failed to read symbols."); 459 460 /* Get the number of attached drives. */ 461 deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive)); 462 463 if (cur.dk_ndrive < 0) 464 errx(1, "invalid _disk_count %d.", cur.dk_ndrive); 465 466 /* Get a pointer to the first disk. */ 467 deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head)); 468 dk_drivehead = TAILQ_FIRST(&disk_head); 469 470 /* Get ticks per second. */ 471 deref_nl(X_STATHZ, &hz, sizeof(hz)); 472 if (!hz) 473 deref_nl(X_HZ, &hz, sizeof(hz)); 474 #endif /* !defined(NOKVM) */ 475 } else { 476 /* Get the number of attached drives. */ 477 mib[0] = CTL_HW; 478 mib[1] = HW_DISKCOUNT; 479 size = sizeof(cur.dk_ndrive); 480 if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) { 481 warn("could not read hw.diskcount"); 482 cur.dk_ndrive = 0; 483 } 484 485 /* Get ticks per second. */ 486 mib[0] = CTL_KERN; 487 mib[1] = KERN_CLOCKRATE; 488 size = sizeof(clkinfo); 489 if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) { 490 warn("could not read kern.clockrate"); 491 hz = 0; 492 } else 493 hz = clkinfo.stathz; 494 } 495 496 /* allocate space for the statistics */ 497 cur.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval)); 498 cur.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 499 cur.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 500 cur.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 501 cur.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 502 cur.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 503 cur.dk_select = calloc((size_t)cur.dk_ndrive, sizeof(int)); 504 cur.dk_name = calloc((size_t)cur.dk_ndrive, sizeof(char *)); 505 last.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval)); 506 last.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 507 last.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 508 last.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 509 last.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 510 last.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t)); 511 512 if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek || 513 !cur.dk_rbytes || !cur.dk_wbytes || !cur.dk_select || 514 !cur.dk_name || !last.dk_time || !last.dk_rxfer || 515 !last.dk_wxfer || !last.dk_seek || !last.dk_rbytes || 516 !last.dk_wbytes) 517 errx(1, "Memory allocation failure."); 518 519 /* Set up the compatibility interfaces. */ 520 dk_ndrive = cur.dk_ndrive; 521 dk_select = cur.dk_select; 522 dr_name = cur.dk_name; 523 524 /* Read the disk names and set initial selection. */ 525 if (nlistf == NULL && memf == NULL) { 526 mib[0] = CTL_HW; 527 mib[1] = HW_DISKNAMES; 528 size = 0; 529 if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0) 530 err(1, "can't get hw.disknames"); 531 disknames = malloc(size); 532 if (disknames == NULL) 533 err(1, NULL); 534 if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0) 535 err(1, "can't get hw.disknames"); 536 bufpp = disknames; 537 for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) { 538 cur.dk_name[i] = name; 539 cur.dk_select[i] = sel; 540 } 541 } else { 542 #if !defined(NOKVM) 543 p = dk_drivehead; 544 for (i = 0; i < cur.dk_ndrive; i++) { 545 char buf[10]; 546 547 deref_kptr(p, &cur_disk, sizeof(cur_disk)); 548 deref_kptr(cur_disk.dk_name, buf, sizeof(buf)); 549 cur.dk_name[i] = strdup(buf); 550 if (!cur.dk_name[i]) 551 errx(1, "Memory allocation failure."); 552 cur.dk_select[i] = sel; 553 554 p = TAILQ_NEXT(&cur_disk, dk_link); 555 } 556 #endif /* !defined(NOKVM) */ 557 } 558 559 /* Never do this initialization again. */ 560 once = 1; 561 return(1); 562 } 563 564 #if !defined(NOKVM) 565 /* 566 * Dereference the kernel pointer `kptr' and fill in the local copy 567 * pointed to by `ptr'. The storage space must be pre-allocated, 568 * and the size of the copy passed in `len'. 569 */ 570 static void 571 deref_kptr(void *kptr, void *ptr, size_t len) 572 { 573 char buf[128]; 574 575 if (kvm_read(kd, (u_long)kptr, ptr, len) != len) { 576 bzero(buf, sizeof(buf)); 577 snprintf(buf, (sizeof(buf) - 1), 578 "can't dereference kptr 0x%lx", (u_long)kptr); 579 KVM_ERROR(buf); 580 } 581 } 582 #endif /* !defined(NOKVM) */ 583