0f8fe2687e83f23a5e9edb67332b2fa4587b52f1
[dragonfly.git] / usr.bin / ktrdump / ktrdump.c
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 <ctype.h>
40 #include <err.h>
41 #include <fcntl.h>
42 #include <kvm.h>
43 #include <limits.h>
44 #include <nlist.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <evtr.h>
51 #include <stdarg.h>
52
53 struct ktr_buffer {
54         struct ktr_entry *ents;
55         int modified;
56         int reset;
57         int beg_idx;            /* Beginning index */
58         int end_idx;            /* Ending index */
59 };
60
61 static struct nlist nl1[] = {
62         { .n_name = "_ktr_version" },
63         { .n_name = "_ktr_entries" },
64         { .n_name = "_ncpus" },
65         { .n_name = NULL }
66 };
67
68 static struct nlist nl2[] = {
69         { .n_name = "_tsc_frequency" },
70         { .n_name = NULL }
71 };
72
73 static struct nlist nl_version_ktr_idx[] = {
74         { .n_name = "_ktr_idx" },
75         { .n_name = "_ktr_buf" },
76         { .n_name = NULL }
77 };
78
79 static struct nlist nl_version_ktr_cpu[] = {
80         { .n_name = "_ktr_cpu" },
81         { .n_name = NULL }
82 };
83
84 struct save_ctx {
85         char save_buf[512];
86         const void *save_kptr;
87 };
88
89 typedef void (*ktr_iter_cb_t)(void *, int, int, struct ktr_entry *, uint64_t *);
90
91 static int cflag;
92 static int dflag;
93 static int fflag;
94 static int iflag;
95 static int lflag;
96 static int nflag;
97 static int qflag;
98 static int rflag;
99 static int sflag;
100 static int tflag;
101 static int xflag;
102 static int pflag;
103 static int Mflag;
104 static int Nflag;
105 static double tsc_frequency;
106 static double correction_factor = 0.0;
107
108 static char corefile[PATH_MAX];
109 static char execfile[PATH_MAX];
110
111 static char errbuf[_POSIX2_LINE_MAX];
112 static int ncpus;
113 static kvm_t *kd;
114 static int entries_per_buf;
115 static int fifo_mask;
116 static int ktr_version;
117
118 static void usage(void);
119 static int earliest_ts(struct ktr_buffer *);
120 static void dump_machine_info(evtr_t);
121 static void print_header(FILE *, int);
122 static void print_entry(FILE *, int, int, struct ktr_entry *, u_int64_t *);
123 static void print_callback(void *, int, int, struct ktr_entry *, uint64_t *);
124 static void dump_callback(void *, int, int, struct ktr_entry *, uint64_t *);
125 static struct ktr_info *kvm_ktrinfo(void *, struct save_ctx *);
126 static const char *kvm_string(const char *, struct save_ctx *);
127 static const char *trunc_path(const char *, int);
128 static void read_symbols(const char *);
129 static const char *address_to_symbol(void *, struct save_ctx *);
130 static struct ktr_buffer *ktr_bufs_init(void);
131 static void get_indices(struct ktr_entry **, int *);
132 static void load_bufs(struct ktr_buffer *, struct ktr_entry **, int *);
133 static void iterate_buf(FILE *, struct ktr_buffer *, int, u_int64_t *, ktr_iter_cb_t);
134 static void iterate_bufs_timesorted(FILE *, struct ktr_buffer *, u_int64_t *, ktr_iter_cb_t);
135 static void kvmfprintf(FILE *fp, const char *ctl, va_list va);
136
137 /*
138  * Reads the ktr trace buffer from kernel memory and prints the trace entries.
139  */
140 int
141 main(int ac, char **av)
142 {
143         struct ktr_buffer *ktr_bufs;
144         struct ktr_entry **ktr_kbuf;
145         ktr_iter_cb_t callback = &print_callback;
146         int *ktr_idx;
147         FILE *fo;
148         void *ctx;
149         int64_t tts;
150         int *ktr_start_index;
151         int c;
152         int n;
153
154         /*
155          * Parse commandline arguments.
156          */
157         fo = stdout;
158         while ((c = getopt(ac, av, "acfinqrtxpslA:N:M:o:d")) != -1) {
159                 switch (c) {
160                 case 'a':
161                         cflag = 1;
162                         iflag = 1;
163                         rflag = 1;
164                         xflag = 1;
165                         pflag = 1;
166                         rflag = 1;
167                         sflag = 1;
168                         break;
169                 case 'c':
170                         cflag = 1;
171                         break;
172                 case 'd':
173                         dflag = 1;
174                         callback = &dump_callback;
175                         break;
176                 case 'N':
177                         if (strlcpy(execfile, optarg, sizeof(execfile))
178                             >= sizeof(execfile))
179                                 errx(1, "%s: File name too long", optarg);
180                         Nflag = 1;
181                         break;
182                 case 'f':
183                         fflag = 1;
184                         break;
185                 case 'l':
186                         lflag = 1;
187                         break;
188                 case 'i':
189                         iflag = 1;
190                         break;
191                 case 'A':
192                         correction_factor = strtod(optarg, NULL);
193                         break;
194                 case 'M':
195                         if (strlcpy(corefile, optarg, sizeof(corefile))
196                             >= sizeof(corefile))
197                                 errx(1, "%s: File name too long", optarg);
198                         Mflag = 1;
199                         break;
200                 case 'n':
201                         nflag = 1;
202                         break;
203                 case 'o':
204                         if ((fo = fopen(optarg, "w")) == NULL)
205                                 err(1, "%s", optarg);
206                         break;
207                 case 'p':
208                         pflag++;
209                         break;
210                 case 'q':
211                         qflag++;
212                         break;
213                 case 'r':
214                         rflag = 1;
215                         break;
216                 case 's':
217                         sflag = 1;      /* sort across the cpus */
218                         break;
219                 case 't':
220                         tflag = 1;
221                         break;
222                 case 'x':
223                         xflag = 1;
224                         break;
225                 case '?':
226                 default:
227                         usage();
228                 }
229         }
230         ctx = fo;
231         if (dflag) {
232                 ctx = evtr_open_write(fo);
233                 if (!ctx) {
234                         err(1, "Can't create event stream");
235                 }
236         }
237         if (cflag + iflag + tflag + xflag + fflag + pflag == 0) {
238                 cflag = 1;
239                 iflag = 1;
240                 tflag = 1;
241                 pflag = 1;
242         }
243         if (correction_factor != 0.0 && (rflag == 0 || nflag)) {
244                 fprintf(stderr, "Correction factor can only be applied with -r and without -n\n");
245                 exit(1);
246         }
247         ac -= optind;
248         av += optind;
249         if (ac != 0)
250                 usage();
251
252         /*
253          * Open our execfile and corefile, resolve needed symbols and read in
254          * the trace buffer.
255          */
256         if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
257             Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
258                 errx(1, "%s", errbuf);
259         if (kvm_nlist(kd, nl1) != 0)
260                 errx(1, "%s", kvm_geterr(kd));
261         if (kvm_read(kd, nl1[0].n_value, &ktr_version, sizeof(ktr_version)) == -1)
262                 errx(1, "%s", kvm_geterr(kd));
263         if (kvm_read(kd, nl1[2].n_value, &ncpus, sizeof(ncpus)) == -1)
264                 errx(1, "%s", kvm_geterr(kd));
265         ktr_start_index = malloc(sizeof(*ktr_start_index) * ncpus);
266         if (ktr_version >= KTR_VERSION_WITH_FREQ && kvm_nlist(kd, nl2) == 0) {
267                 if (kvm_read(kd, nl2[0].n_value, &tts, sizeof(tts)) == -1)
268                         errx(1, "%s", kvm_geterr(kd));
269                 tsc_frequency = (double)tts;
270         }
271         if (ktr_version > KTR_VERSION)
272                 errx(1, "ktr version too high for us to handle");
273         if (kvm_read(kd, nl1[1].n_value, &entries_per_buf,
274                                 sizeof(entries_per_buf)) == -1)
275                 errx(1, "%s", kvm_geterr(kd));
276         fifo_mask = entries_per_buf - 1;
277
278         printf("TSC frequency is %6.3f MHz\n", tsc_frequency / 1000000.0);
279
280         if (dflag) {
281                 dump_machine_info((evtr_t)ctx);
282         }
283         ktr_kbuf = calloc(ncpus, sizeof(*ktr_kbuf));
284         ktr_idx = calloc(ncpus, sizeof(*ktr_idx));
285
286         if (nflag == 0)
287                 read_symbols(Nflag ? execfile : NULL);
288
289         if (ktr_version < KTR_VERSION_KTR_CPU) {
290                 if (kvm_nlist(kd, nl_version_ktr_idx))
291                         errx(1, "%s", kvm_geterr(kd));
292         } else {
293                 if (kvm_nlist(kd, nl_version_ktr_cpu))
294                         errx(1, "%s", kvm_geterr(kd));
295         }
296
297         get_indices(ktr_kbuf, ktr_idx);
298
299         ktr_bufs = ktr_bufs_init();
300
301         if (sflag) {
302                 u_int64_t last_timestamp = 0;
303                 do {
304                         load_bufs(ktr_bufs, ktr_kbuf, ktr_idx);
305                         iterate_bufs_timesorted(ctx, ktr_bufs, &last_timestamp,
306                                                 callback);
307                         if (lflag)
308                                 usleep(1000000 / 10);
309                 } while (lflag);
310         } else {
311                 u_int64_t *last_timestamp = calloc(sizeof(u_int64_t), ncpus);
312                 do {
313                         load_bufs(ktr_bufs, ktr_kbuf, ktr_idx);
314                         for (n = 0; n < ncpus; ++n)
315                                 iterate_buf(ctx, ktr_bufs, n, &last_timestamp[n],
316                                         callback);
317                         if (lflag)
318                                 usleep(1000000 / 10);
319                 } while (lflag);
320         }
321         if (dflag)
322                 evtr_close(ctx);
323         return (0);
324 }
325
326 static
327 void
328 dump_machine_info(evtr_t evtr)
329 {
330         struct evtr_event ev;
331
332         ev.type = EVTR_TYPE_CPUINFO;
333         ev.ncpus = ncpus;
334
335         evtr_dump_event(evtr, &ev);
336 }
337
338 static void
339 print_header(FILE *fo, int row)
340 {
341         if (qflag == 0 && (u_int32_t)row % 20 == 0) {
342                 fprintf(fo, "%-6s ", "index");
343                 if (cflag)
344                         fprintf(fo, "%-3s ", "cpu");
345                 if (tflag || rflag)
346                         fprintf(fo, "%-16s ", "timestamp");
347                 if (xflag) {
348                         if (nflag)
349                             fprintf(fo, "%-10s %-10s", "caller2", "caller1");
350                         else
351                             fprintf(fo, "%-20s %-20s", "caller2", "caller1");
352                 }
353                 if (iflag)
354                         fprintf(fo, "%-20s ", "ID");
355                 if (fflag)
356                         fprintf(fo, "%10s%-30s ", "", "file and line");
357                 if (pflag)
358                         fprintf(fo, "%s", "trace");
359                 fprintf(fo, "\n");
360         }
361 }
362
363 static void
364 print_entry(FILE *fo, int n, int row, struct ktr_entry *entry,
365             u_int64_t *last_timestamp)
366 {
367         struct ktr_info *info = NULL;
368         static struct save_ctx nctx, pctx, fmtctx, symctx, infoctx;
369
370         fprintf(fo, " %06x ", row & 0x00FFFFFF);
371         if (cflag)
372                 fprintf(fo, "%-3d ", n);
373         if (tflag || rflag) {
374                 if (rflag && !nflag && tsc_frequency != 0.0) {
375                         fprintf(fo, "%13.3f uS ",
376                                 (double)(entry->ktr_timestamp - *last_timestamp) * 1000000.0 / tsc_frequency - correction_factor);
377                 } else if (rflag) {
378                         fprintf(fo, "%-16ju ",
379                             (uintmax_t)(entry->ktr_timestamp - *last_timestamp));
380                 } else {
381                         fprintf(fo, "%-16ju ",
382                             (uintmax_t)entry->ktr_timestamp);
383                 }
384         }
385         if (xflag) {
386                 if (nflag) {
387                     fprintf(fo, "%p %p ", 
388                             entry->ktr_caller2, entry->ktr_caller1);
389                 } else {
390                     fprintf(fo, "%-25s ", 
391                             address_to_symbol(entry->ktr_caller2, &symctx));
392                     fprintf(fo, "%-25s ", 
393                             address_to_symbol(entry->ktr_caller1, &symctx));
394                 }
395         }
396         if (iflag) {
397                 info = kvm_ktrinfo(entry->ktr_info, &infoctx);
398                 if (info)
399                         fprintf(fo, "%-20s ", kvm_string(info->kf_name, &nctx));
400                 else
401                         fprintf(fo, "%-20s ", "<empty>");
402         }
403         if (fflag)
404                 fprintf(fo, "%34s:%-4d ",
405                         trunc_path(kvm_string(entry->ktr_file, &pctx), 34),
406                         entry->ktr_line);
407         if (pflag) {
408                 if (info == NULL)
409                         info = kvm_ktrinfo(entry->ktr_info, &infoctx);
410                 if (info)
411                         kvmfprintf(fo, kvm_string(info->kf_format, &fmtctx),
412                                  (void *)&entry->ktr_data);
413         }
414         fprintf(fo, "\n");
415         *last_timestamp = entry->ktr_timestamp;
416 }
417
418 static
419 void
420 print_callback(void *ctx, int n, int row, struct ktr_entry *entry, uint64_t *last_ts)
421 {
422         FILE *fo = (FILE *)ctx;
423         print_header(fo, row);
424         print_entry(fo, n, row, entry, last_ts);
425 }
426
427 /*
428  * If free == 0, replace all (kvm) string pointers in fmtdata with pointers
429  * to user-allocated copies of the strings.
430  * If free != 0, free those pointers.
431  */
432 static
433 int
434 mangle_string_ptrs(const char *fmt, uint8_t *fmtdata, int dofree)
435 {
436         const char *f, *p;
437         size_t skipsize, intsz;
438         static struct save_ctx strctx;
439         int ret = 0;
440
441         for (f = fmt; f[0] != '\0'; ++f) {
442                 if (f[0] != '%')
443                         continue;
444                 ++f;
445                 skipsize = 0;
446                 for (p = f; p[0]; ++p) {
447                         int again = 0;
448                         /*
449                          * Eat flags. Notice this will accept duplicate
450                          * flags.
451                          */
452                         switch (p[0]) {
453                         case '#':
454                         case '0':
455                         case '-':
456                         case ' ':
457                         case '+':
458                         case '\'':
459                                 again = !0;
460                                 break;
461                         }
462                         if (!again)
463                                 break;
464                 }
465                 /* Eat minimum field width, if any */
466                 for (; isdigit(p[0]); ++p)
467                         ;
468                 if (p[0] == '.')
469                         ++p;
470                 /* Eat precision, if any */
471                 for (; isdigit(p[0]); ++p)
472                         ;
473                 intsz = 0;
474                 switch (p[0]) {
475                 case 'l':
476                         if (p[1] == 'l') {
477                                 ++p;
478                                 intsz = sizeof(long long);
479                         } else {
480                                 intsz = sizeof(long);
481                         }
482                         break;
483                 case 'j':
484                         intsz = sizeof(intmax_t);
485                         break;
486                 case 't':
487                         intsz = sizeof(ptrdiff_t);
488                         break;
489                 case 'z':
490                         intsz = sizeof(size_t);
491                         break;
492                 default:
493                         break;
494                 }
495                 if (intsz != 0)
496                         ++p;
497                 else
498                         intsz = sizeof(int);
499
500                 switch (p[0]) {
501                 case 'd':
502                 case 'i':
503                 case 'o':
504                 case 'u':
505                 case 'x':
506                 case 'X':
507                 case 'c':
508                         skipsize = intsz;
509                         break;
510                 case 'p':
511                         skipsize = sizeof(void *);
512                         break;
513                 case 'f':
514                         if (p[-1] == 'l')
515                                 skipsize = sizeof(double);
516                         else
517                                 skipsize = sizeof(float);
518                         break;
519                 case 's':
520                         if (dofree) {
521                           char *t = ((char **)fmtdata)[0];
522                           free(t);
523                           skipsize = sizeof(char *);
524                         } else {
525                           char *t = strdup(kvm_string(((char **)fmtdata)[0],
526                                                           &strctx));
527                           ((const char **)fmtdata)[0] = t;
528                                         
529                                 skipsize = sizeof(char *);
530                         }
531                         ++ret;
532                         break;
533                 default:
534                         fprintf(stderr, "Unknown conversion specifier %c "
535                                 "in fmt starting with %s", p[0], f - 1);
536                         return -1;
537                 }
538                 fmtdata += skipsize;
539         }
540         return ret;
541 }
542
543 static
544 void
545 dump_callback(void *ctx, int n, int row __unused, struct ktr_entry *entry,
546               uint64_t *last_ts __unused)
547 {
548         evtr_t evtr = (evtr_t)ctx;
549         struct evtr_event ev;
550         static struct save_ctx pctx, fmtctx, infoctx;
551         struct ktr_info *ki;
552         int conv = 0;   /* pointless */
553
554         ev.ts = entry->ktr_timestamp;
555         ev.type = EVTR_TYPE_PROBE;
556         ev.line = entry->ktr_line;
557         ev.file = kvm_string(entry->ktr_file, &pctx);
558         ev.func = NULL;
559         ev.cpu = n;
560         if ((ki = kvm_ktrinfo(entry->ktr_info, &infoctx))) {
561                 ev.fmt = kvm_string(ki->kf_format, &fmtctx);
562                 ev.fmtdata = entry->ktr_data;
563                 if ((conv = mangle_string_ptrs(ev.fmt,
564                                                __DECONST(uint8_t *, ev.fmtdata),
565                                                0)) < 0)
566                         errx(1, "Can't parse format string\n");
567                 ev.fmtdatalen = ki->kf_data_size;
568         } else {
569                 ev.fmt = ev.fmtdata = NULL;
570                 ev.fmtdatalen = 0;
571         }
572         if (evtr_dump_event(evtr, &ev)) {
573                 err(1, evtr_errmsg(evtr));
574         }
575         if (ev.fmtdata && conv) {
576                 mangle_string_ptrs(ev.fmt, __DECONST(uint8_t *, ev.fmtdata),
577                                    !0);
578         }
579 }
580
581 static
582 struct ktr_info *
583 kvm_ktrinfo(void *kptr, struct save_ctx *ctx)
584 {
585         struct ktr_info *ki = (void *)ctx->save_buf;
586
587         if (kptr == NULL)
588                 return(NULL);
589         if (ctx->save_kptr != kptr) {
590                 if (kvm_read(kd, (uintptr_t)kptr, ki, sizeof(*ki)) == -1) {
591                         bzero(&ki, sizeof(*ki));
592                 } else {
593                         ctx->save_kptr = kptr;
594                 }
595         }
596         return(ki);
597 }
598
599 static
600 const char *
601 kvm_string(const char *kptr, struct save_ctx *ctx)
602 {
603         u_int l;
604         u_int n;
605
606         if (kptr == NULL)
607                 return("?");
608         if (ctx->save_kptr != (const void *)kptr) {
609                 ctx->save_kptr = (const void *)kptr;
610                 l = 0;
611                 while (l < sizeof(ctx->save_buf) - 1) {
612                         n = 256 - ((intptr_t)(kptr + l) & 255);
613                         if (n > sizeof(ctx->save_buf) - l - 1)
614                                 n = sizeof(ctx->save_buf) - l - 1;
615                         if (kvm_read(kd, (uintptr_t)(kptr + l), ctx->save_buf + l, n) < 0)
616                                 break;
617                         while (l < sizeof(ctx->save_buf) && n) {
618                             if (ctx->save_buf[l] == 0)
619                                     break;
620                             --n;
621                             ++l;
622                         }
623                         if (n)
624                             break;
625                 }
626                 ctx->save_buf[l] = 0;
627         }
628         return(ctx->save_buf);
629 }
630
631 static
632 const char *
633 trunc_path(const char *str, int maxlen)
634 {
635         int len = strlen(str);
636
637         if (len > maxlen)
638                 return(str + len - maxlen);
639         else
640                 return(str);
641 }
642
643 struct symdata {
644         TAILQ_ENTRY(symdata) link;
645         const char *symname;
646         char *symaddr;
647         char symtype;
648 };
649
650 static TAILQ_HEAD(symlist, symdata) symlist;
651 static struct symdata *symcache;
652 static char *symbegin;
653 static char *symend;
654
655 static
656 void
657 read_symbols(const char *file)
658 {
659         char buf[256];
660         char cmd[256];
661         size_t buflen = sizeof(buf);
662         FILE *fp;
663         struct symdata *sym;
664         char *s1;
665         char *s2;
666         char *s3;
667
668         TAILQ_INIT(&symlist);
669
670         if (file == NULL) {
671                 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
672                         file = "/boot/kernel";
673                 else
674                         file = buf;
675         }
676         snprintf(cmd, sizeof(cmd), "nm -n %s", file);
677         if ((fp = popen(cmd, "r")) != NULL) {
678                 while (fgets(buf, sizeof(buf), fp) != NULL) {
679                     s1 = strtok(buf, " \t\n");
680                     s2 = strtok(NULL, " \t\n");
681                     s3 = strtok(NULL, " \t\n");
682                     if (s1 && s2 && s3) {
683                         sym = malloc(sizeof(struct symdata));
684                         sym->symaddr = (char *)strtoul(s1, NULL, 16);
685                         sym->symtype = s2[0];
686                         sym->symname = strdup(s3);
687                         if (strcmp(s3, "kernbase") == 0)
688                                 symbegin = sym->symaddr;
689                         if (strcmp(s3, "end") == 0)
690                                 symend = sym->symaddr;
691                         TAILQ_INSERT_TAIL(&symlist, sym, link);
692                     }
693                 }
694                 pclose(fp);
695         }
696         symcache = TAILQ_FIRST(&symlist);
697 }
698
699 static
700 const char *
701 address_to_symbol(void *kptr, struct save_ctx *ctx)
702 {
703         char *buf = ctx->save_buf;
704         int size = sizeof(ctx->save_buf);
705
706         if (symcache == NULL ||
707            (char *)kptr < symbegin || (char *)kptr >= symend
708         ) {
709                 snprintf(buf, size, "%p", kptr);
710                 return(buf);
711         }
712         while ((char *)symcache->symaddr < (char *)kptr) {
713                 if (TAILQ_NEXT(symcache, link) == NULL)
714                         break;
715                 symcache = TAILQ_NEXT(symcache, link);
716         }
717         while ((char *)symcache->symaddr > (char *)kptr) {
718                 if (symcache != TAILQ_FIRST(&symlist))
719                         symcache = TAILQ_PREV(symcache, symlist, link);
720         }
721         snprintf(buf, size, "%s+%d", symcache->symname,
722                 (int)((char *)kptr - symcache->symaddr));
723         return(buf);
724 }
725
726 static
727 struct ktr_buffer *
728 ktr_bufs_init(void)
729 {
730         struct ktr_buffer *ktr_bufs, *it;
731         int i;
732
733         ktr_bufs = malloc(sizeof(*ktr_bufs) * ncpus);
734         if (!ktr_bufs)
735                 err(1, "can't allocate data structures\n");
736         for (i = 0; i < ncpus; ++i) {
737                 it = ktr_bufs + i;
738                 it->ents = malloc(sizeof(struct ktr_entry) * entries_per_buf);
739                 if (it->ents == NULL)
740                         err(1, "can't allocate data structures\n");
741                 it->reset = 1;
742                 it->beg_idx = -1;
743                 it->end_idx = -1;
744         }
745         return ktr_bufs;
746 }
747
748 static
749 void
750 get_indices(struct ktr_entry **ktr_kbuf, int *ktr_idx)
751 {
752         static struct ktr_cpu *ktr_cpus;
753         int i;
754
755         if (ktr_cpus == NULL)
756                 ktr_cpus = malloc(sizeof(*ktr_cpus) * ncpus);
757
758         if (ktr_version < KTR_VERSION_KTR_CPU) {
759                 if (kvm_read(kd, nl_version_ktr_idx[0].n_value, ktr_idx,
760                     sizeof(*ktr_idx) * ncpus) == -1) {
761                         errx(1, "%s", kvm_geterr(kd));
762                 }
763                 if (ktr_kbuf[0] == NULL) {
764                         if (kvm_read(kd, nl_version_ktr_idx[1].n_value,
765                             ktr_kbuf, sizeof(*ktr_kbuf) * ncpus) == -1) {
766                                 errx(1, "%s", kvm_geterr(kd));
767                         }
768                 }
769         } else {
770                 if (kvm_read(kd, nl_version_ktr_cpu[0].n_value,
771                              ktr_cpus, sizeof(*ktr_cpus) * ncpus) == -1) {
772                                 errx(1, "%s", kvm_geterr(kd));
773                 }
774                 for (i = 0; i < ncpus; ++i) {
775                         ktr_idx[i] = ktr_cpus[i].core.ktr_idx;
776                         ktr_kbuf[i] = ktr_cpus[i].core.ktr_buf;
777                 }
778         }
779 }
780
781 /*
782  * Get the trace buffer data from the kernel
783  */
784 static
785 void
786 load_bufs(struct ktr_buffer *ktr_bufs, struct ktr_entry **kbufs, int *ktr_idx)
787 {
788         struct ktr_buffer *kbuf;
789         int i;
790
791         get_indices(kbufs, ktr_idx);
792         for (i = 0; i < ncpus; ++i) {
793                 kbuf = &ktr_bufs[i];
794                 if (ktr_idx[i] == kbuf->end_idx)
795                         continue;
796                 kbuf->end_idx = ktr_idx[i];
797
798                 /*
799                  * If we do not have a notion of the beginning index, assume
800                  * it is entries_per_buf before the ending index.  Don't
801                  * worry about underflows/negative numbers, the indices will
802                  * be masked.
803                  */
804                 if (kbuf->reset) {
805                         kbuf->beg_idx = kbuf->end_idx - entries_per_buf + 1;
806                         kbuf->reset = 0;
807                 }
808                 if (kvm_read(kd, (uintptr_t)kbufs[i], ktr_bufs[i].ents,
809                                 sizeof(struct ktr_entry) * entries_per_buf)
810                                                                         == -1)
811                         errx(1, "%s", kvm_geterr(kd));
812                 kbuf->modified = 1;
813                 kbuf->beg_idx = earliest_ts(kbuf);
814         }
815
816 }
817
818 /*
819  * Locate the earliest timestamp iterating backwards from end_idx, but
820  * not going further back then beg_idx.  We have to do this because
821  * the kernel uses a circulating buffer.
822  */
823 static
824 int
825 earliest_ts(struct ktr_buffer *buf)
826 {
827         struct ktr_entry *save;
828         int count, scan, i, earliest;
829
830         count = 0;
831         earliest = buf->end_idx - 1;
832         save = &buf->ents[earliest & fifo_mask];
833         for (scan = buf->end_idx - 1; scan != buf->beg_idx -1; --scan) {
834                 i = scan & fifo_mask;
835                 if (buf->ents[i].ktr_timestamp <= save->ktr_timestamp &&
836                     buf->ents[i].ktr_timestamp > 0)
837                         earliest = scan;
838                 /*
839                  * We may have gotten so far behind that beg_idx wrapped
840                  * more then once around the buffer.  Just stop
841                  */
842                 if (++count == entries_per_buf)
843                         break;
844         }
845         return earliest;
846 }
847
848 static
849 void
850 iterate_buf(FILE *fo, struct ktr_buffer *ktr_bufs, int cpu,
851             u_int64_t *last_timestamp, ktr_iter_cb_t cb)
852 {
853         struct ktr_buffer *buf = ktr_bufs + cpu;
854
855         if (buf->modified == 0)
856                 return;
857         if (*last_timestamp == 0) {
858                 *last_timestamp =
859                         buf->ents[buf->beg_idx & fifo_mask].ktr_timestamp;
860         }
861         while (buf->beg_idx != buf->end_idx) {
862                 cb(fo, cpu, buf->beg_idx,
863                    &buf->ents[buf->beg_idx & fifo_mask],
864                    last_timestamp);
865                 ++buf->beg_idx;
866         }
867         buf->modified = 0;
868 }
869
870 static
871 void
872 iterate_bufs_timesorted(FILE *fo, struct ktr_buffer *ktr_bufs,
873                         u_int64_t *last_timestamp, ktr_iter_cb_t cb)
874 {
875         struct ktr_entry *ent;
876         struct ktr_buffer *buf;
877         int n, bestn;
878         u_int64_t ts;
879         static int row = 0;
880
881         for (;;) {
882                 ts = 0;
883                 bestn = -1;
884                 for (n = 0; n < ncpus; ++n) {
885                         buf = ktr_bufs + n;
886                         if (buf->beg_idx == buf->end_idx)
887                                 continue;
888                         ent = &buf->ents[buf->beg_idx & fifo_mask];
889                         if (ts == 0 || (ts >= ent->ktr_timestamp)) {
890                                 ts = ent->ktr_timestamp;
891                                 bestn = n;
892                         }
893                 }
894                 if ((bestn < 0) || (ts < *last_timestamp))
895                         break;
896                 buf = ktr_bufs + bestn;
897                 cb(fo, bestn, row,
898                    &buf->ents[buf->beg_idx & fifo_mask],
899                    last_timestamp);
900                 ++buf->beg_idx;
901                 *last_timestamp = ts;
902                 ++row;
903         }
904 }
905
906 static
907 void
908 kvmfprintf(FILE *fp, const char *ctl, va_list va)
909 {
910         int n;
911         int is_long;
912         int is_done;
913         char fmt[256];
914         static struct save_ctx strctx;
915         const char *s;
916
917         while (*ctl) {
918                 for (n = 0; ctl[n]; ++n) {
919                         fmt[n] = ctl[n];
920                         if (ctl[n] == '%')
921                                 break;
922                 }
923                 if (n == 0) {
924                         is_long = 0;
925                         is_done = 0;
926                         n = 1;
927                         while (n < (int)sizeof(fmt)) {
928                                 fmt[n] = ctl[n];
929                                 fmt[n+1] = 0;
930
931                                 switch(ctl[n]) {
932                                 case 'p':
933                                         is_long = 1;
934                                         /* fall through */
935                                 case 'd':
936                                 case 'u':
937                                 case 'x':
938                                 case 'o':
939                                 case 'X':
940                                         /*
941                                          * Integral
942                                          */
943                                         switch(is_long) {
944                                         case 0:
945                                                 fprintf(fp, fmt,
946                                                         va_arg(va, int));
947                                                 break;
948                                         case 1:
949                                                 fprintf(fp, fmt,
950                                                         va_arg(va, long));
951                                                 break;
952                                         case 2:
953                                                 fprintf(fp, fmt,
954                                                     va_arg(va, long long));
955                                                 break;
956                                         case 3:
957                                                 fprintf(fp, fmt,
958                                                     va_arg(va, size_t));
959                                                 break;
960                                         }
961                                         ++n;
962                                         is_done = 1;
963                                         break;
964                                 case 's':
965                                         /*
966                                          * String
967                                          */
968                                         s = kvm_string(va_arg(va, char *), &strctx);
969                                         fwrite(s, 1, strlen(s), fp);
970                                         ++n;
971                                         is_done = 1;
972                                         break;
973                                 case 'f':
974                                         /*
975                                          * Floating
976                                          */
977                                         fprintf(fp, fmt,
978                                                 va_arg(va, double));
979                                         ++n;
980                                         break;
981                                 case 'j':
982                                         is_long = 3;
983                                         break;
984                                 case 'l':
985                                         if (is_long)
986                                                 is_long = 2;
987                                         else
988                                                 is_long = 1;
989                                         break;
990                                 case '.':
991                                 case '-':
992                                 case '+':
993                                 case '0':
994                                 case '1':
995                                 case '2':
996                                 case '3':
997                                 case '4':
998                                 case '5':
999                                 case '6':
1000                                 case '7':
1001                                 case '8':
1002                                 case '9':
1003                                         break;
1004                                 default:
1005                                         is_done = 1;
1006                                         break;
1007                                 }
1008                                 if (is_done)
1009                                         break;
1010                                 ++n;
1011                         }
1012                 } else {
1013                         fmt[n] = 0;
1014                         fprintf(fp, fmt, NULL);
1015                 }
1016                 ctl += n;
1017         }
1018 }
1019
1020 static void
1021 usage(void)
1022 {
1023         fprintf(stderr, "usage: ktrdump [-acfilnpqrstx] [-A factor] "
1024                         "[-N execfile] [-M corefile] [-o outfile]\n");
1025         exit(1);
1026 }