1 /*- 2 * Copyright (c) 2002 Jake Burkholder 3 * Copyright (c) 2004 Robert Watson 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/usr.bin/ktrdump/ktrdump.c,v 1.10 2005/05/21 09:55:06 ru Exp $ 28 * $DragonFly: src/usr.bin/ktrdump/ktrdump.c,v 1.13 2008/11/10 02:05:31 swildner Exp $ 29 */ 30 31 #include <sys/cdefs.h> 32 33 #include <sys/types.h> 34 #include <sys/ktr.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/queue.h> 38 39 #include <err.h> 40 #include <fcntl.h> 41 #include <kvm.h> 42 #include <limits.h> 43 #include <nlist.h> 44 #include <stdint.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <stdarg.h> 50 51 #define SBUFLEN 256 52 #define SBUFMASK (SBUFLEN - 1) 53 54 struct ktr_buffer { 55 struct ktr_entry *ents; 56 int modified; 57 int reset; 58 int beg_idx; /* Beginning index */ 59 int end_idx; /* Ending index */ 60 }; 61 62 static struct nlist nl1[] = { 63 { .n_name = "_ktr_version" }, 64 { .n_name = "_ktr_entries" }, 65 { .n_name = "_ncpus" }, 66 { .n_name = NULL } 67 }; 68 69 static struct nlist nl2[] = { 70 { .n_name = "_tsc_frequency" }, 71 { .n_name = NULL } 72 }; 73 74 static struct nlist nl_version_ktr_idx[] = { 75 { .n_name = "_ktr_idx" }, 76 { .n_name = "_ktr_buf" }, 77 { .n_name = NULL } 78 }; 79 80 static struct nlist nl_version_ktr_cpu[] = { 81 { .n_name = "_ktr_cpu" }, 82 { .n_name = NULL } 83 }; 84 85 static int cflag; 86 static int fflag; 87 static int iflag; 88 static int lflag; 89 static int nflag; 90 static int qflag; 91 static int rflag; 92 static int sflag; 93 static int tflag; 94 static int xflag; 95 static int pflag; 96 static int Mflag; 97 static int Nflag; 98 static double tsc_frequency; 99 static double correction_factor = 0.0; 100 101 static char corefile[PATH_MAX]; 102 static char execfile[PATH_MAX]; 103 104 static char errbuf[_POSIX2_LINE_MAX]; 105 static int ncpus; 106 static kvm_t *kd; 107 static int entries_per_buf; 108 static int fifo_mask; 109 static int ktr_version; 110 111 static void usage(void); 112 static int earliest_ts(struct ktr_buffer *); 113 static void print_header(FILE *, int); 114 static void print_entry(FILE *, int, int, struct ktr_entry *, u_int64_t *); 115 static struct ktr_info *kvm_ktrinfo(void *); 116 static const char *kvm_string(char *buf, const char *); 117 static const char *trunc_path(const char *, int); 118 static void read_symbols(const char *); 119 static const char *address_to_symbol(void *); 120 static struct ktr_buffer *ktr_bufs_init(void); 121 static void get_indices(struct ktr_entry **, int *); 122 static void load_bufs(struct ktr_buffer *, struct ktr_entry **, int *); 123 static void print_buf(FILE *, struct ktr_buffer *, int, u_int64_t *); 124 static void print_bufs_timesorted(FILE *, struct ktr_buffer *, u_int64_t *); 125 static void kvmfprintf(FILE *fp, const char *ctl, va_list va); 126 127 /* 128 * Reads the ktr trace buffer from kernel memory and prints the trace entries. 129 */ 130 int 131 main(int ac, char **av) 132 { 133 struct ktr_buffer *ktr_bufs; 134 struct ktr_entry **ktr_kbuf; 135 int *ktr_idx; 136 FILE *fo; 137 int64_t tts; 138 int *ktr_start_index; 139 int c; 140 int n; 141 142 /* 143 * Parse commandline arguments. 144 */ 145 fo = stdout; 146 while ((c = getopt(ac, av, "acfinqrtxpslA:N:M:o:")) != -1) { 147 switch (c) { 148 case 'a': 149 cflag = 1; 150 iflag = 1; 151 rflag = 1; 152 xflag = 1; 153 pflag = 1; 154 rflag = 1; 155 sflag = 1; 156 break; 157 case 'c': 158 cflag = 1; 159 break; 160 case 'N': 161 if (strlcpy(execfile, optarg, sizeof(execfile)) 162 >= sizeof(execfile)) 163 errx(1, "%s: File name too long", optarg); 164 Nflag = 1; 165 break; 166 case 'f': 167 fflag = 1; 168 break; 169 case 'l': 170 lflag = 1; 171 break; 172 case 'i': 173 iflag = 1; 174 break; 175 case 'A': 176 correction_factor = strtod(optarg, NULL); 177 break; 178 case 'M': 179 if (strlcpy(corefile, optarg, sizeof(corefile)) 180 >= sizeof(corefile)) 181 errx(1, "%s: File name too long", optarg); 182 Mflag = 1; 183 break; 184 case 'n': 185 nflag = 1; 186 break; 187 case 'o': 188 if ((fo = fopen(optarg, "w")) == NULL) 189 err(1, "%s", optarg); 190 break; 191 case 'p': 192 pflag++; 193 break; 194 case 'q': 195 qflag++; 196 break; 197 case 'r': 198 rflag = 1; 199 break; 200 case 's': 201 sflag = 1; /* sort across the cpus */ 202 break; 203 case 't': 204 tflag = 1; 205 break; 206 case 'x': 207 xflag = 1; 208 break; 209 case '?': 210 default: 211 usage(); 212 } 213 } 214 if (cflag + iflag + tflag + xflag + fflag + pflag == 0) { 215 cflag = 1; 216 iflag = 1; 217 tflag = 1; 218 pflag = 1; 219 } 220 if (correction_factor != 0.0 && (rflag == 0 || nflag)) { 221 fprintf(stderr, "Correction factor can only be applied with -r and without -n\n"); 222 exit(1); 223 } 224 ac -= optind; 225 av += optind; 226 if (ac != 0) 227 usage(); 228 229 /* 230 * Open our execfile and corefile, resolve needed symbols and read in 231 * the trace buffer. 232 */ 233 if ((kd = kvm_openfiles(Nflag ? execfile : NULL, 234 Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) 235 errx(1, "%s", errbuf); 236 if (kvm_nlist(kd, nl1) != 0) 237 errx(1, "%s", kvm_geterr(kd)); 238 if (kvm_read(kd, nl1[0].n_value, &ktr_version, sizeof(ktr_version)) == -1) 239 errx(1, "%s", kvm_geterr(kd)); 240 if (kvm_read(kd, nl1[2].n_value, &ncpus, sizeof(ncpus)) == -1) 241 errx(1, "%s", kvm_geterr(kd)); 242 ktr_start_index = malloc(sizeof(*ktr_start_index) * ncpus); 243 if (ktr_version >= KTR_VERSION_WITH_FREQ && kvm_nlist(kd, nl2) == 0) { 244 if (kvm_read(kd, nl2[0].n_value, &tts, sizeof(tts)) == -1) 245 errx(1, "%s", kvm_geterr(kd)); 246 tsc_frequency = (double)tts; 247 } 248 if (ktr_version > KTR_VERSION) 249 errx(1, "ktr version too high for us to handle"); 250 if (kvm_read(kd, nl1[1].n_value, &entries_per_buf, 251 sizeof(entries_per_buf)) == -1) 252 errx(1, "%s", kvm_geterr(kd)); 253 fifo_mask = entries_per_buf - 1; 254 255 printf("TSC frequency is %6.3f MHz\n", tsc_frequency / 1000000.0); 256 257 ktr_kbuf = calloc(ncpus, sizeof(*ktr_kbuf)); 258 ktr_idx = calloc(ncpus, sizeof(*ktr_idx)); 259 260 if (nflag == 0) 261 read_symbols(Nflag ? execfile : NULL); 262 263 if (ktr_version < KTR_VERSION_KTR_CPU) { 264 if (kvm_nlist(kd, nl_version_ktr_idx)) 265 errx(1, "%s", kvm_geterr(kd)); 266 } else { 267 if (kvm_nlist(kd, nl_version_ktr_cpu)) 268 errx(1, "%s", kvm_geterr(kd)); 269 } 270 271 get_indices(ktr_kbuf, ktr_idx); 272 273 ktr_bufs = ktr_bufs_init(); 274 275 if (sflag) { 276 u_int64_t last_timestamp = 0; 277 do { 278 load_bufs(ktr_bufs, ktr_kbuf, ktr_idx); 279 print_bufs_timesorted(fo, ktr_bufs, &last_timestamp); 280 if (lflag) 281 usleep(1000000 / 10); 282 } while (lflag); 283 } else { 284 u_int64_t *last_timestamp = calloc(sizeof(u_int64_t), ncpus); 285 do { 286 load_bufs(ktr_bufs, ktr_kbuf, ktr_idx); 287 for (n = 0; n < ncpus; ++n) 288 print_buf(fo, ktr_bufs, n, &last_timestamp[n]); 289 if (lflag) 290 usleep(1000000 / 10); 291 } while (lflag); 292 } 293 return (0); 294 } 295 296 static void 297 print_header(FILE *fo, int row) 298 { 299 if (qflag == 0 && (u_int32_t)row % 20 == 0) { 300 fprintf(fo, "%-6s ", "index"); 301 if (cflag) 302 fprintf(fo, "%-3s ", "cpu"); 303 if (tflag || rflag) 304 fprintf(fo, "%-16s ", "timestamp"); 305 if (xflag) { 306 if (nflag) 307 fprintf(fo, "%-10s %-10s", "caller2", "caller1"); 308 else 309 fprintf(fo, "%-20s %-20s", "caller2", "caller1"); 310 } 311 if (iflag) 312 fprintf(fo, "%-20s ", "ID"); 313 if (fflag) 314 fprintf(fo, "%10s%-30s ", "", "file and line"); 315 if (pflag) 316 fprintf(fo, "%s", "trace"); 317 fprintf(fo, "\n"); 318 } 319 } 320 321 static void 322 print_entry(FILE *fo, int n, int row, struct ktr_entry *entry, 323 u_int64_t *last_timestamp) 324 { 325 struct ktr_info *info = NULL; 326 char buf[SBUFLEN]; 327 328 fprintf(fo, " %06x ", row & 0x00FFFFFF); 329 if (cflag) 330 fprintf(fo, "%-3d ", n); 331 if (tflag || rflag) { 332 if (rflag && !nflag && tsc_frequency != 0.0) { 333 fprintf(fo, "%13.3f uS ", 334 (double)(entry->ktr_timestamp - *last_timestamp) * 1000000.0 / tsc_frequency - correction_factor); 335 } else if (rflag) { 336 fprintf(fo, "%-16ju ", 337 (uintmax_t)(entry->ktr_timestamp - *last_timestamp)); 338 } else { 339 fprintf(fo, "%-16ju ", 340 (uintmax_t)entry->ktr_timestamp); 341 } 342 } 343 if (xflag) { 344 if (nflag) { 345 fprintf(fo, "%p %p ", 346 entry->ktr_caller2, entry->ktr_caller1); 347 } else { 348 fprintf(fo, "%-25s ", 349 address_to_symbol(entry->ktr_caller2)); 350 fprintf(fo, "%-25s ", 351 address_to_symbol(entry->ktr_caller1)); 352 } 353 } 354 if (iflag) { 355 info = kvm_ktrinfo(entry->ktr_info); 356 if (info) 357 fprintf(fo, "%-20s ", kvm_string(buf, info->kf_name)); 358 else 359 fprintf(fo, "%-20s ", "<empty>"); 360 } 361 if (fflag) 362 fprintf(fo, "%34s:%-4d ", trunc_path(kvm_string(buf, entry->ktr_file), 34), entry->ktr_line); 363 if (pflag) { 364 if (info == NULL) 365 info = kvm_ktrinfo(entry->ktr_info); 366 if (info) 367 kvmfprintf(fo, kvm_string(buf, info->kf_format), (void *)&entry->ktr_data); 368 } 369 fprintf(fo, "\n"); 370 *last_timestamp = entry->ktr_timestamp; 371 } 372 373 static 374 struct ktr_info * 375 kvm_ktrinfo(void *kptr) 376 { 377 static struct ktr_info save_info; 378 static void *save_kptr; 379 380 if (kptr == NULL) 381 return(NULL); 382 if (save_kptr != kptr) { 383 if (kvm_read(kd, (uintptr_t)kptr, &save_info, sizeof(save_info)) == -1) { 384 bzero(&save_info, sizeof(save_info)); 385 } else { 386 save_kptr = kptr; 387 } 388 } 389 return(&save_info); 390 } 391 392 static 393 const char * 394 kvm_string(char *save_str, const char *kptr) 395 { 396 static const char *save_kptr; 397 u_int l; 398 u_int n; 399 400 if (kptr == NULL) 401 return("?"); 402 if (save_kptr != kptr) { 403 save_kptr = kptr; 404 l = 0; 405 while (l < SBUFLEN - 1) { 406 n = SBUFLEN - 407 ((intptr_t)(kptr + l) & SBUFMASK); 408 if (n > SBUFLEN - l - 1) 409 n = SBUFLEN - l - 1; 410 if (kvm_read(kd, (uintptr_t)(kptr + l), save_str + l, n) < 0) 411 break; 412 while (l < SBUFLEN && n) { 413 if (save_str[l] == 0) 414 break; 415 --n; 416 ++l; 417 } 418 if (n) 419 break; 420 } 421 save_str[l] = 0; 422 } 423 return(save_str); 424 } 425 426 static 427 const char * 428 trunc_path(const char *str, int maxlen) 429 { 430 int len = strlen(str); 431 432 if (len > maxlen) 433 return(str + len - maxlen); 434 else 435 return(str); 436 } 437 438 struct symdata { 439 TAILQ_ENTRY(symdata) link; 440 const char *symname; 441 char *symaddr; 442 char symtype; 443 }; 444 445 static TAILQ_HEAD(symlist, symdata) symlist; 446 static struct symdata *symcache; 447 static char *symbegin; 448 static char *symend; 449 450 static 451 void 452 read_symbols(const char *file) 453 { 454 char buf[256]; 455 char cmd[256]; 456 size_t buflen = sizeof(buf); 457 FILE *fp; 458 struct symdata *sym; 459 char *s1; 460 char *s2; 461 char *s3; 462 463 TAILQ_INIT(&symlist); 464 465 if (file == NULL) { 466 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0) 467 file = "/boot/kernel"; 468 else 469 file = buf; 470 } 471 snprintf(cmd, sizeof(cmd), "nm -n %s", file); 472 if ((fp = popen(cmd, "r")) != NULL) { 473 while (fgets(buf, sizeof(buf), fp) != NULL) { 474 s1 = strtok(buf, " \t\n"); 475 s2 = strtok(NULL, " \t\n"); 476 s3 = strtok(NULL, " \t\n"); 477 if (s1 && s2 && s3) { 478 sym = malloc(sizeof(struct symdata)); 479 sym->symaddr = (char *)strtoul(s1, NULL, 16); 480 sym->symtype = s2[0]; 481 sym->symname = strdup(s3); 482 if (strcmp(s3, "kernbase") == 0) 483 symbegin = sym->symaddr; 484 if (strcmp(s3, "end") == 0) 485 symend = sym->symaddr; 486 TAILQ_INSERT_TAIL(&symlist, sym, link); 487 } 488 } 489 pclose(fp); 490 } 491 symcache = TAILQ_FIRST(&symlist); 492 } 493 494 static 495 const char * 496 address_to_symbol(void *kptr) 497 { 498 static char buf[64]; 499 500 if (symcache == NULL || 501 (char *)kptr < symbegin || (char *)kptr >= symend 502 ) { 503 snprintf(buf, sizeof(buf), "%p", kptr); 504 return(buf); 505 } 506 while ((char *)symcache->symaddr < (char *)kptr) { 507 if (TAILQ_NEXT(symcache, link) == NULL) 508 break; 509 symcache = TAILQ_NEXT(symcache, link); 510 } 511 while ((char *)symcache->symaddr > (char *)kptr) { 512 if (symcache != TAILQ_FIRST(&symlist)) 513 symcache = TAILQ_PREV(symcache, symlist, link); 514 } 515 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname, 516 (int)((char *)kptr - symcache->symaddr)); 517 return(buf); 518 } 519 520 static 521 struct ktr_buffer * 522 ktr_bufs_init(void) 523 { 524 struct ktr_buffer *ktr_bufs, *it; 525 int i; 526 527 ktr_bufs = malloc(sizeof(*ktr_bufs) * ncpus); 528 if (!ktr_bufs) 529 err(1, "can't allocate data structures\n"); 530 for (i = 0; i < ncpus; ++i) { 531 it = ktr_bufs + i; 532 it->ents = malloc(sizeof(struct ktr_entry) * entries_per_buf); 533 if (it->ents == NULL) 534 err(1, "can't allocate data structures\n"); 535 it->reset = 1; 536 it->beg_idx = -1; 537 it->end_idx = -1; 538 } 539 return ktr_bufs; 540 } 541 542 static 543 void 544 get_indices(struct ktr_entry **ktr_kbuf, int *ktr_idx) 545 { 546 static struct ktr_cpu *ktr_cpus; 547 int i; 548 549 if (ktr_cpus == NULL) 550 ktr_cpus = malloc(sizeof(*ktr_cpus) * ncpus); 551 552 if (ktr_version < KTR_VERSION_KTR_CPU) { 553 if (kvm_read(kd, nl_version_ktr_idx[0].n_value, ktr_idx, 554 sizeof(*ktr_idx) * ncpus) == -1) { 555 errx(1, "%s", kvm_geterr(kd)); 556 } 557 if (ktr_kbuf[0] == NULL) { 558 if (kvm_read(kd, nl_version_ktr_idx[1].n_value, 559 ktr_kbuf, sizeof(*ktr_kbuf) * ncpus) == -1) { 560 errx(1, "%s", kvm_geterr(kd)); 561 } 562 } 563 } else { 564 if (kvm_read(kd, nl_version_ktr_cpu[0].n_value, 565 ktr_cpus, sizeof(*ktr_cpus) * ncpus) == -1) { 566 errx(1, "%s", kvm_geterr(kd)); 567 } 568 for (i = 0; i < ncpus; ++i) { 569 ktr_idx[i] = ktr_cpus[i].core.ktr_idx; 570 ktr_kbuf[i] = ktr_cpus[i].core.ktr_buf; 571 } 572 } 573 } 574 575 /* 576 * Get the trace buffer data from the kernel 577 */ 578 static 579 void 580 load_bufs(struct ktr_buffer *ktr_bufs, struct ktr_entry **kbufs, int *ktr_idx) 581 { 582 struct ktr_buffer *kbuf; 583 int i; 584 585 get_indices(kbufs, ktr_idx); 586 for (i = 0; i < ncpus; ++i) { 587 kbuf = &ktr_bufs[i]; 588 if (ktr_idx[i] == kbuf->end_idx) 589 continue; 590 kbuf->end_idx = ktr_idx[i]; 591 592 /* 593 * If we do not have a notion of the beginning index, assume 594 * it is entries_per_buf before the ending index. Don't 595 * worry about underflows/negative numbers, the indices will 596 * be masked. 597 */ 598 if (kbuf->reset) { 599 kbuf->beg_idx = kbuf->end_idx - entries_per_buf + 1; 600 kbuf->reset = 0; 601 } 602 if (kvm_read(kd, (uintptr_t)kbufs[i], ktr_bufs[i].ents, 603 sizeof(struct ktr_entry) * entries_per_buf) 604 == -1) 605 errx(1, "%s", kvm_geterr(kd)); 606 kbuf->modified = 1; 607 kbuf->beg_idx = earliest_ts(kbuf); 608 } 609 610 } 611 612 /* 613 * Locate the earliest timestamp iterating backwards from end_idx, but 614 * not going further back then beg_idx. We have to do this because 615 * the kernel uses a circulating buffer. 616 */ 617 static 618 int 619 earliest_ts(struct ktr_buffer *buf) 620 { 621 struct ktr_entry *save; 622 int count, scan, i, earliest; 623 624 count = 0; 625 earliest = buf->end_idx - 1; 626 save = &buf->ents[earliest & fifo_mask]; 627 for (scan = buf->end_idx - 1; scan != buf->beg_idx -1; --scan) { 628 i = scan & fifo_mask; 629 if (buf->ents[i].ktr_timestamp <= save->ktr_timestamp && 630 buf->ents[i].ktr_timestamp > 0) 631 earliest = scan; 632 /* 633 * We may have gotten so far behind that beg_idx wrapped 634 * more then once around the buffer. Just stop 635 */ 636 if (++count == entries_per_buf) 637 break; 638 } 639 return earliest; 640 } 641 642 static 643 void 644 print_buf(FILE *fo, struct ktr_buffer *ktr_bufs, int cpu, 645 u_int64_t *last_timestamp) 646 { 647 struct ktr_buffer *buf = ktr_bufs + cpu; 648 649 if (buf->modified == 0) 650 return; 651 if (*last_timestamp == 0) { 652 *last_timestamp = 653 buf->ents[buf->beg_idx & fifo_mask].ktr_timestamp; 654 } 655 while (buf->beg_idx != buf->end_idx) { 656 print_header(fo, buf->beg_idx); 657 print_entry(fo, cpu, buf->beg_idx, 658 &buf->ents[buf->beg_idx & fifo_mask], 659 last_timestamp); 660 ++buf->beg_idx; 661 } 662 buf->modified = 0; 663 } 664 665 static 666 void 667 print_bufs_timesorted(FILE *fo, struct ktr_buffer *ktr_bufs, 668 u_int64_t *last_timestamp) 669 { 670 struct ktr_entry *ent; 671 struct ktr_buffer *buf; 672 int n, bestn; 673 u_int64_t ts; 674 static int row = 0; 675 676 for (;;) { 677 ts = 0; 678 bestn = -1; 679 for (n = 0; n < ncpus; ++n) { 680 buf = ktr_bufs + n; 681 if (buf->beg_idx == buf->end_idx) 682 continue; 683 ent = &buf->ents[buf->beg_idx & fifo_mask]; 684 if (ts == 0 || (ts >= ent->ktr_timestamp)) { 685 ts = ent->ktr_timestamp; 686 bestn = n; 687 } 688 } 689 if ((bestn < 0) || (ts < *last_timestamp)) 690 break; 691 buf = ktr_bufs + bestn; 692 print_header(fo, row); 693 print_entry(fo, bestn, row, 694 &buf->ents[buf->beg_idx & fifo_mask], 695 last_timestamp); 696 ++buf->beg_idx; 697 *last_timestamp = ts; 698 ++row; 699 } 700 } 701 702 static 703 void 704 kvmfprintf(FILE *fp, const char *ctl, va_list va) 705 { 706 int n; 707 int is_long; 708 int is_done; 709 char fmt[256]; 710 char buf[256]; 711 712 while (*ctl) { 713 for (n = 0; ctl[n]; ++n) { 714 fmt[n] = ctl[n]; 715 if (ctl[n] == '%') 716 break; 717 } 718 if (n == 0) { 719 is_long = 0; 720 is_done = 0; 721 n = 1; 722 while (n < (int)sizeof(fmt)) { 723 fmt[n] = ctl[n]; 724 fmt[n+1] = 0; 725 726 switch(ctl[n]) { 727 case 'p': 728 is_long = 1; 729 /* fall through */ 730 case 'd': 731 case 'u': 732 case 'x': 733 case 'o': 734 case 'X': 735 /* 736 * Integral 737 */ 738 switch(is_long) { 739 case 0: 740 fprintf(fp, fmt, 741 va_arg(va, int)); 742 break; 743 case 1: 744 fprintf(fp, fmt, 745 va_arg(va, long)); 746 break; 747 case 2: 748 fprintf(fp, fmt, 749 va_arg(va, long long)); 750 break; 751 case 3: 752 fprintf(fp, fmt, 753 va_arg(va, size_t)); 754 break; 755 } 756 ++n; 757 is_done = 1; 758 break; 759 case 's': 760 /* 761 * String 762 */ 763 kvm_string(buf, va_arg(va, char *)); 764 fwrite(buf, 1, strlen(buf), fp); 765 ++n; 766 is_done = 1; 767 break; 768 case 'f': 769 /* 770 * Floating 771 */ 772 fprintf(fp, fmt, 773 va_arg(va, double)); 774 ++n; 775 break; 776 case 'j': 777 is_long = 3; 778 break; 779 case 'l': 780 if (is_long) 781 is_long = 2; 782 else 783 is_long = 1; 784 break; 785 case '.': 786 case '-': 787 case '+': 788 case '0': 789 case '1': 790 case '2': 791 case '3': 792 case '4': 793 case '5': 794 case '6': 795 case '7': 796 case '8': 797 case '9': 798 break; 799 default: 800 is_done = 1; 801 break; 802 } 803 if (is_done) 804 break; 805 ++n; 806 } 807 } else { 808 fmt[n] = 0; 809 fprintf(fp, fmt, NULL); 810 } 811 ctl += n; 812 } 813 } 814 815 static void 816 usage(void) 817 { 818 fprintf(stderr, "usage: ktrdump [-acfilnpqrstx] [-A factor] " 819 "[-N execfile] [-M corefile] [-o outfile]\n"); 820 exit(1); 821 } 822