Don't use readline anymore. kadmin/ktutil should be updated to use
[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.6 2005/12/19 17:09:58 dillon 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
50 #define SBUFLEN 128
51
52 extern char *optarg;
53 extern int optind;
54
55 static void usage(void);
56 static void print_header(FILE *fo, int row);
57 static void print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry);
58 static struct ktr_info *kvm_ktrinfo(kvm_t *kd, void *kptr);
59 static const char *kvm_string(kvm_t *kd, const char *kptr);
60 static const char *trunc_path(const char *str, int maxlen);
61 static void read_symbols(const char *execfile);
62 static const char *address_to_symbol(void *kptr);
63
64 static struct nlist nl[] = {
65         { "_ktr_version" },
66         { "_ktr_entries" },
67         { "_ktr_idx" },
68         { "_ktr_buf" },
69         { "_ncpus" },
70         { "_tsc_frequency" },
71         { NULL }
72 };
73
74 static int cflag;
75 static int fflag;
76 static int iflag;
77 static int nflag;
78 static int qflag;
79 static int rflag;
80 static int sflag;
81 static int tflag;
82 static int xflag;
83 static int pflag;
84 static int Mflag;
85 static int Nflag;
86 static int64_t last_timestamp;
87 static double tsc_frequency;
88 static double correction_factor = 0.0;
89
90 static char corefile[PATH_MAX];
91 static char execfile[PATH_MAX];
92
93 static char desc[SBUFLEN];
94 static char errbuf[_POSIX2_LINE_MAX];
95 static char fbuf[PATH_MAX];
96 static char obuf[PATH_MAX];
97
98 /*
99  * Reads the ktr trace buffer from kernel memory and prints the trace entries.
100  */
101 int
102 main(int ac, char **av)
103 {
104         struct ktr_entry **ktr_buf;
105         struct ktr_entry *entry;
106         struct ktr_entry *entryx;
107         uintmax_t tlast, tnow;
108         struct stat sb;
109         kvm_t *kd;
110         FILE *fo;
111         char *p;
112         int64_t first_timestamp;
113         int64_t tts;
114         int *ktr_start_index;
115         int version;
116         int entries;
117         int *ktr_idx;
118         int ncpus;
119         int did_display_flag = 0;
120         int in;
121         int c;
122         int i;
123         int n;
124         int bestn;
125         int row;
126
127         /*
128          * Parse commandline arguments.
129          */
130         fo = stdout;
131         while ((c = getopt(ac, av, "acfinqrtxpsA:N:M:o:")) != -1) {
132                 switch (c) {
133                 case 'a':
134                         cflag = 1;
135                         iflag = 1;
136                         tflag = 1;
137                         xflag = 1;
138                         fflag = 1;
139                         pflag = 1;
140                         break;
141                 case 'c':
142                         cflag = 1;
143                         break;
144                 case 'N':
145                         if (strlcpy(execfile, optarg, sizeof(execfile))
146                             >= sizeof(execfile))
147                                 errx(1, "%s: File name too long", optarg);
148                         Nflag = 1;
149                         break;
150                 case 'f':
151                         fflag = 1;
152                         break;
153                 case 'i':
154                         iflag = 1;
155                         break;
156                 case 'A':
157                         correction_factor = strtod(optarg, NULL);
158                         break;
159                 case 'M':
160                         if (strlcpy(corefile, optarg, sizeof(corefile))
161                             >= sizeof(corefile))
162                                 errx(1, "%s: File name too long", optarg);
163                         Mflag = 1;
164                         break;
165                 case 'n':
166                         nflag = 1;
167                         break;
168                 case 'o':
169                         if ((fo = fopen(optarg, "w")) == NULL)
170                                 err(1, "%s", optarg);
171                         break;
172                 case 'p':
173                         pflag++;
174                         break;
175                 case 'q':
176                         qflag++;
177                         break;
178                 case 'r':
179                         rflag = 1;
180                         break;
181                 case 's':
182                         sflag = 1;      /* sort across the cpus */
183                         break;
184                 case 't':
185                         tflag = 1;
186                         break;
187                 case 'x':
188                         xflag = 1;
189                         break;
190                 case '?':
191                 default:
192                         usage();
193                 }
194         }
195         if (cflag + iflag + tflag + xflag + fflag + pflag == 0) {
196                 cflag = 1;
197                 iflag = 1;
198                 tflag = 1;
199                 pflag = 1;
200         }
201         if (correction_factor != 0.0 && (rflag == 0 || nflag)) {
202                 fprintf(stderr, "Correction factor can only be applied with -r and without -n\n");
203                 exit(1);
204         }
205         ac -= optind;
206         av += optind;
207         if (ac != 0)
208                 usage();
209
210         /*
211          * Open our execfile and corefile, resolve needed symbols and read in
212          * the trace buffer.
213          */
214         if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
215             Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
216                 errx(1, "%s", errbuf);
217         if (kvm_nlist(kd, nl) != 0)
218                 errx(1, "%s", kvm_geterr(kd));
219         if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1)
220                 errx(1, "%s", kvm_geterr(kd));
221         if (kvm_read(kd, nl[4].n_value, &ncpus, sizeof(ncpus)) == -1)
222                 errx(1, "%s", kvm_geterr(kd));
223         ktr_start_index = malloc(sizeof(*ktr_start_index) * ncpus);
224         if (version >= 3) {
225                 if (kvm_read(kd, nl[5].n_value, &tts, sizeof(tts)) == -1)
226                         errx(1, "%s", kvm_geterr(kd));
227                 tsc_frequency = (double)tts;
228         }
229         if (version > KTR_VERSION)
230                 errx(1, "ktr version too high for us to handle");
231         if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) == -1)
232                 errx(1, "%s", kvm_geterr(kd));
233         ktr_buf = malloc(sizeof(*ktr_buf) * ncpus);
234         ktr_idx = malloc(sizeof(*ktr_idx) * ncpus);
235
236         if (nflag == 0)
237                 read_symbols(Nflag ? execfile : NULL);
238
239         if (kvm_read(kd, nl[2].n_value, ktr_idx, sizeof(*ktr_idx) * ncpus) == -1)
240                 errx(1, "%s", kvm_geterr(kd));
241         if (kvm_read(kd, nl[3].n_value, ktr_buf, sizeof(*ktr_buf) * ncpus) == -1)
242                 errx(1, "%s", kvm_geterr(kd));
243         for (n = 0; n < ncpus; ++n) {
244                 void *kptr = ktr_buf[n];
245                 ktr_buf[n] = malloc(sizeof(**ktr_buf) * entries);
246                 if (kvm_read(kd, (uintptr_t)kptr, ktr_buf[n], sizeof(**ktr_buf) * entries) == -1)
247                         errx(1, "%s", kvm_geterr(kd));
248         }
249
250         /*
251          * Figure out the lowest numbered timestamp.
252          */
253         first_timestamp = 0;
254         printf("TSC frequency is %6.3f MHz\n", tsc_frequency / 1000000.0);
255         for (n = 0; n < ncpus; ++n) {
256                 for (i = 0; i < entries; ++i) {
257                         entry = &ktr_buf[n][i];
258                         if (entry->ktr_timestamp && (first_timestamp == 0 ||
259                             first_timestamp < entry->ktr_timestamp)) {
260                                 first_timestamp = entry->ktr_timestamp;
261                         }
262                 }
263         }
264
265         /*
266          * Figure out the starting entry for each cpu
267          */
268         for (n = 0; n < ncpus; ++n) {
269                 ktr_start_index[n] = 0;
270                 tts = 0;
271                 for (i = 0; i < entries; ++i) {
272                         entry = &ktr_buf[n][i];
273                         if (entry->ktr_timestamp == 0)
274                                 continue;
275                         if (tts == 0 || tts > entry->ktr_timestamp) {
276                                 tts = entry->ktr_timestamp;
277                                 ktr_start_index[n] = i;
278                         }
279                 }
280         }
281
282         /*
283          * Now tear through the trace buffer.
284          */
285         if (sflag) {
286                 row = 0;
287                 for (;;) {
288                         bestn = -1;
289                         tts = 0;
290                         for (n = 0; n < ncpus; ++n) {
291                                 i = ktr_start_index[n];
292                                 entry = &ktr_buf[n][i];
293                                 if (entry->ktr_timestamp == 0)
294                                         continue;
295                                 if (tts == 0 || tts >= entry->ktr_timestamp) {
296                                         tts = entry->ktr_timestamp;
297                                         bestn = n;
298                                 }
299                         }
300                         if (bestn < 0 || tts < last_timestamp)
301                                 break;
302                         print_header(fo, row);
303                         print_entry(fo, kd, bestn, ktr_start_index[bestn],
304                                     &ktr_buf[bestn][ktr_start_index[bestn]]);
305                         if (++ktr_start_index[bestn] == entries)
306                                 ktr_start_index[bestn] = 0;
307                         last_timestamp = tts;
308                         ++row;
309                 }
310         } else {
311                 for (n = 0; n < ncpus; ++n) {
312                         last_timestamp = first_timestamp;
313                         i = ktr_start_index[n];
314                         do {
315                                 entry = &ktr_buf[n][i];
316                                 print_header(fo, i);
317                                 print_entry(fo, kd, n, i, &ktr_buf[n][i]);
318                                 if (++i == entries)
319                                         i = 0;
320                         } while (i != ktr_start_index[n]);
321                 }
322         }
323         return (0);
324 }
325
326 static void
327 print_header(FILE *fo, int row)
328 {
329         if (qflag == 0 && row % 20 == 0) {
330                 fprintf(fo, "%-6s ", "index");
331                 if (cflag)
332                         fprintf(fo, "%-3s ", "cpu");
333                 if (tflag || rflag)
334                         fprintf(fo, "%-16s ", "timestamp");
335                 if (xflag) {
336                         if (nflag)
337                             fprintf(fo, "%-10s %-10s", "caller2", "caller1");
338                         else
339                             fprintf(fo, "%-20s %-20s", "caller2", "caller1");
340                 }
341                 if (iflag)
342                         fprintf(fo, "%-20s ", "ID");
343                 if (fflag)
344                         fprintf(fo, "%10s%-30s ", "", "file and line");
345                 if (pflag)
346                         fprintf(fo, "%s", "trace");
347                 fprintf(fo, "\n");
348         }
349 }
350
351 static void
352 print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry)
353 {
354         struct ktr_info *info = NULL;
355
356         fprintf(fo, " %5d ", i);
357         if (cflag)
358                 fprintf(fo, "%-3d ", n);
359         if (tflag || rflag) {
360                 if (rflag && !nflag && tsc_frequency != 0.0) {
361                         fprintf(fo, "%13.3f uS ",
362                                 (double)(entry->ktr_timestamp - last_timestamp) * 1000000.0 / tsc_frequency - correction_factor);
363                 } else if (rflag) {
364                         fprintf(fo, "%-16lld ", entry->ktr_timestamp -
365                                                 last_timestamp);
366                 } else {
367                         fprintf(fo, "%-16lld ", entry->ktr_timestamp);
368                 }
369         }
370         if (xflag) {
371                 if (nflag) {
372                     fprintf(fo, "%p %p ", 
373                             entry->ktr_caller2, entry->ktr_caller1);
374                 } else {
375                     fprintf(fo, "%-20s ", 
376                             address_to_symbol(entry->ktr_caller2));
377                     fprintf(fo, "%-20s ", 
378                             address_to_symbol(entry->ktr_caller1));
379                 }
380         }
381         if (iflag) {
382                 info = kvm_ktrinfo(kd, entry->ktr_info);
383                 if (info)
384                         fprintf(fo, "%-20s ", kvm_string(kd, info->kf_name));
385                 else
386                         fprintf(fo, "%-20s ", "<empty>");
387         }
388         if (fflag)
389                 fprintf(fo, "%34s:%-4d ", trunc_path(kvm_string(kd, entry->ktr_file), 34), entry->ktr_line);
390         if (pflag) {
391                 if (info == NULL)
392                         info = kvm_ktrinfo(kd, entry->ktr_info);
393                 if (info) {
394                         fprintf(fo, kvm_string(kd, info->kf_format),
395                                 entry->ktr_data[0], entry->ktr_data[1],
396                                 entry->ktr_data[2], entry->ktr_data[3],
397                                 entry->ktr_data[4], entry->ktr_data[5],
398                                 entry->ktr_data[6], entry->ktr_data[7],
399                                 entry->ktr_data[8], entry->ktr_data[9]);
400                 } else {
401                         fprintf(fo, "");
402                 }
403         }
404         fprintf(fo, "\n");
405         last_timestamp = entry->ktr_timestamp;
406 }
407
408 static
409 struct ktr_info *
410 kvm_ktrinfo(kvm_t *kd, void *kptr)
411 {
412         static struct ktr_info save_info;
413         static void *save_kptr;
414
415         if (kptr == NULL)
416                 return(NULL);
417         if (save_kptr != kptr) {
418                 if (kvm_read(kd, (uintptr_t)kptr, &save_info, sizeof(save_info)) == -1) {
419                         bzero(&save_info, sizeof(save_info));
420                 } else {
421                         save_kptr = kptr;
422                 }
423         }
424         return(&save_info);
425 }
426
427 static
428 const char *
429 kvm_string(kvm_t *kd, const char *kptr)
430 {
431         static char save_str[128];
432         static const char *save_kptr;
433         int l;
434         int n;
435
436         if (kptr == NULL)
437                 return("?");
438         if (save_kptr != kptr) {
439                 save_kptr = kptr;
440                 l = 0;
441                 while (l < sizeof(save_str) - 1) {
442                         n = 256 - ((intptr_t)(kptr + l) & 255);
443                         if (n > sizeof(save_str) - l - 1)
444                                 n = sizeof(save_str) - l - 1;
445                         if (kvm_read(kd, (uintptr_t)(kptr + l), save_str + l, n) < 0)
446                                 break;
447                         while (l < sizeof(save_str) && n) {
448                             if (save_str[l] == 0)
449                                     break;
450                             --n;
451                             ++l;
452                         }
453                         if (n)
454                             break;
455                 }
456                 save_str[l] = 0;
457         }
458         return(save_str);
459 }
460
461 static
462 const char *
463 trunc_path(const char *str, int maxlen)
464 {
465         int len = strlen(str);
466
467         if (len > maxlen)
468                 return(str + len - maxlen);
469         else
470                 return(str);
471 }
472
473 struct symdata {
474         TAILQ_ENTRY(symdata) link;
475         const char *symname;
476         char *symaddr;
477         char symtype;
478 };
479
480 static TAILQ_HEAD(symlist, symdata) symlist;
481 static struct symdata *symcache;
482 static char *symbegin;
483 static char *symend;
484
485 static
486 void
487 read_symbols(const char *execfile)
488 {
489         char buf[256];
490         char cmd[256];
491         int buflen = sizeof(buf);
492         FILE *fp;
493         struct symdata *sym;
494         char *s1;
495         char *s2;
496         char *s3;
497
498         TAILQ_INIT(&symlist);
499
500         if (execfile == NULL) {
501                 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
502                         execfile = "/kernel";
503                 else
504                         execfile = buf;
505         }
506         snprintf(cmd, sizeof(cmd), "nm -n %s", execfile);
507         if ((fp = popen(cmd, "r")) != NULL) {
508                 while (fgets(buf, sizeof(buf), fp) != NULL) {
509                     s1 = strtok(buf, " \t\n");
510                     s2 = strtok(NULL, " \t\n");
511                     s3 = strtok(NULL, " \t\n");
512                     if (s1 && s2 && s3) {
513                         sym = malloc(sizeof(struct symdata));
514                         sym->symaddr = (char *)strtoul(s1, NULL, 16);
515                         sym->symtype = s2[0];
516                         sym->symname = strdup(s3);
517                         if (strcmp(s3, "kernbase") == 0)
518                                 symbegin = sym->symaddr;
519                         if (strcmp(s3, "end") == 0)
520                                 symend = sym->symaddr;
521                         TAILQ_INSERT_TAIL(&symlist, sym, link);
522                     }
523                 }
524                 pclose(fp);
525         }
526         symcache = TAILQ_FIRST(&symlist);
527 }
528
529 static
530 const char *
531 address_to_symbol(void *kptr)
532 {
533         static char buf[64];
534
535         if (symcache == NULL ||
536            (char *)kptr < symbegin || (char *)kptr >= symend
537         ) {
538                 snprintf(buf, sizeof(buf), "%p", kptr);
539                 return(buf);
540         }
541         while ((char *)symcache->symaddr < (char *)kptr) {
542                 if (TAILQ_NEXT(symcache, link) == NULL)
543                         break;
544                 symcache = TAILQ_NEXT(symcache, link);
545         }
546         while ((char *)symcache->symaddr > (char *)kptr) {
547                 if (symcache != TAILQ_FIRST(&symlist))
548                         symcache = TAILQ_PREV(symcache, symlist, link);
549         }
550         snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
551                 (int)((char *)kptr - symcache->symaddr));
552         return(buf);
553 }
554
555 static void
556 usage(void)
557 {
558         fprintf(stderr, "usage: ktrdump [-acfinpqrtx] [-N execfile] "
559                         "[-M corefile] [-o outfile]\n");
560         exit(1);
561 }