Synchronize the TSC between all cpus on startup and provide a sysctl,
[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.3 2005/06/21 06:50:28 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         { NULL }
71 };
72
73 static int cflag;
74 static int fflag;
75 static int iflag;
76 static int nflag;
77 static int qflag;
78 static int rflag;
79 static int tflag;
80 static int xflag;
81 static int pflag;
82 static int Mflag;
83 static int Nflag;
84 static int64_t last_timestamp;
85
86 static char corefile[PATH_MAX];
87 static char execfile[PATH_MAX];
88
89 static char desc[SBUFLEN];
90 static char errbuf[_POSIX2_LINE_MAX];
91 static char fbuf[PATH_MAX];
92 static char obuf[PATH_MAX];
93
94 /*
95  * Reads the ktr trace buffer from kernel memory and prints the trace entries.
96  */
97 int
98 main(int ac, char **av)
99 {
100         struct ktr_entry **ktr_buf;
101         uintmax_t tlast, tnow;
102         struct stat sb;
103         kvm_t *kd;
104         FILE *fo;
105         char *p;
106         int version;
107         int entries;
108         int *ktr_idx;
109         int ncpus;
110         int did_display_flag = 0;
111         int in;
112         int c;
113         int i;
114         int n;
115
116         /*
117          * Parse commandline arguments.
118          */
119         fo = stdout;
120         while ((c = getopt(ac, av, "acfiqrtxpN:M:o:")) != -1) {
121                 switch (c) {
122                 case 'a':
123                         cflag = 1;
124                         iflag = 1;
125                         tflag = 1;
126                         xflag = 1;
127                         fflag = 1;
128                         pflag = 1;
129                         break;
130                 case 'c':
131                         cflag = 1;
132                         break;
133                 case 'N':
134                         if (strlcpy(execfile, optarg, sizeof(execfile))
135                             >= sizeof(execfile))
136                                 errx(1, "%s: File name too long", optarg);
137                         Nflag = 1;
138                         break;
139                 case 'f':
140                         fflag = 1;
141                         break;
142                 case 'i':
143                         iflag = 1;
144                         break;
145                 case 'M':
146                         if (strlcpy(corefile, optarg, sizeof(corefile))
147                             >= sizeof(corefile))
148                                 errx(1, "%s: File name too long", optarg);
149                         Mflag = 1;
150                         break;
151                 case 'n':
152                         nflag = 1;
153                         break;
154                 case 'o':
155                         if ((fo = fopen(optarg, "w")) == NULL)
156                                 err(1, "%s", optarg);
157                         break;
158                 case 'p':
159                         pflag++;
160                         break;
161                 case 'q':
162                         qflag++;
163                         break;
164                 case 'r':
165                         rflag = 1;
166                         break;
167                 case 't':
168                         tflag = 1;
169                         break;
170                 case 'x':
171                         xflag = 1;
172                         break;
173                 case '?':
174                 default:
175                         usage();
176                 }
177         }
178         if (cflag + iflag + tflag + xflag + fflag + pflag == 0) {
179                 cflag = 1;
180                 iflag = 1;
181                 tflag = 1;
182                 fflag = 1;
183                 pflag = 1;
184         }
185
186         ac -= optind;
187         av += optind;
188         if (ac != 0)
189                 usage();
190
191         /*
192          * Open our execfile and corefile, resolve needed symbols and read in
193          * the trace buffer.
194          */
195         if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
196             Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
197                 errx(1, "%s", errbuf);
198         if (kvm_nlist(kd, nl) != 0)
199                 errx(1, "%s", kvm_geterr(kd));
200         if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1)
201                 errx(1, "%s", kvm_geterr(kd));
202         if (kvm_read(kd, nl[4].n_value, &ncpus, sizeof(ncpus)) == -1)
203                 errx(1, "%s", kvm_geterr(kd));
204
205         if (version != KTR_VERSION)
206                 errx(1, "ktr version mismatch");
207         if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) == -1)
208                 errx(1, "%s", kvm_geterr(kd));
209         ktr_buf = malloc(sizeof(*ktr_buf) * ncpus);
210         ktr_idx = malloc(sizeof(*ktr_idx) * ncpus);
211
212         if (nflag == 0)
213                 read_symbols(Nflag ? execfile : NULL);
214
215         if (kvm_read(kd, nl[2].n_value, ktr_idx, sizeof(*ktr_idx) * ncpus) == -1)
216                 errx(1, "%s", kvm_geterr(kd));
217         if (kvm_read(kd, nl[3].n_value, ktr_buf, sizeof(*ktr_buf) * ncpus) == -1)
218                 errx(1, "%s", kvm_geterr(kd));
219         for (n = 0; n < ncpus; ++n) {
220                 void *kptr = ktr_buf[n];
221                 ktr_buf[n] = malloc(sizeof(**ktr_buf) * entries);
222                 if (kvm_read(kd, (uintptr_t)kptr, ktr_buf[n], sizeof(**ktr_buf) * entries) == -1)
223                 errx(1, "%s", kvm_geterr(kd));
224         }
225
226         /*
227          * Now tear through the trace buffer.
228          */
229         for (n = 0; n < ncpus; ++n) {
230                 last_timestamp = 0;
231                 for (i = 0; i < entries; ++i) {
232                         print_header(fo, i);
233                         print_entry(fo, kd, n, i, &ktr_buf[n][i]);
234                 }
235         }
236         return (0);
237 }
238
239 static void
240 print_header(FILE *fo, int row)
241 {
242         if (qflag == 0 && row % 20 == 0) {
243                 fprintf(fo, "%-6s ", "index");
244                 if (cflag)
245                         fprintf(fo, "%-3s ", "cpu");
246                 if (tflag || rflag)
247                         fprintf(fo, "%-16s ", "timestamp");
248                 if (xflag) {
249                         if (nflag)
250                             fprintf(fo, "%-10s %-10s", "caller2", "caller1");
251                         else
252                             fprintf(fo, "%-20s %-20s", "caller2", "caller1");
253                 }
254                 if (iflag)
255                         fprintf(fo, "%-20s ", "ID");
256                 if (fflag)
257                         fprintf(fo, "%10s%-30s ", "", "file and line");
258                 if (pflag)
259                         fprintf(fo, "%s", "trace");
260                 fprintf(fo, "\n");
261         }
262 }
263
264 static void
265 print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry)
266 {
267         struct ktr_info *info = NULL;
268
269         fprintf(fo, " %5d ", i);
270         if (cflag)
271                 fprintf(fo, "%-3d ", n);
272         if (tflag || rflag) {
273                 if (rflag)
274                         fprintf(fo, "%-16lld ", entry->ktr_timestamp -
275                                                 last_timestamp);
276                 else
277                         fprintf(fo, "%-16lld ", entry->ktr_timestamp);
278         }
279         if (xflag) {
280                 if (nflag) {
281                     fprintf(fo, "%p %p ", 
282                             entry->ktr_caller2, entry->ktr_caller1);
283                 } else {
284                     fprintf(fo, "%-20s ", 
285                             address_to_symbol(entry->ktr_caller2));
286                     fprintf(fo, "%-20s ", 
287                             address_to_symbol(entry->ktr_caller1));
288                 }
289         }
290         if (iflag) {
291                 info = kvm_ktrinfo(kd, entry->ktr_info);
292                 if (info)
293                         fprintf(fo, "%-20s ", kvm_string(kd, info->kf_name));
294                 else
295                         fprintf(fo, "%-20s ", "<empty>");
296         }
297         if (fflag)
298                 fprintf(fo, "%34s:%-4d ", trunc_path(kvm_string(kd, entry->ktr_file), 34), entry->ktr_line);
299         if (pflag) {
300                 if (info == NULL)
301                         info = kvm_ktrinfo(kd, entry->ktr_info);
302                 if (info) {
303                         fprintf(fo, kvm_string(kd, info->kf_format),
304                                 entry->ktr_data[0], entry->ktr_data[1],
305                                 entry->ktr_data[2], entry->ktr_data[3],
306                                 entry->ktr_data[4], entry->ktr_data[5],
307                                 entry->ktr_data[6], entry->ktr_data[7],
308                                 entry->ktr_data[8], entry->ktr_data[9]);
309                 } else {
310                         fprintf(fo, "");
311                 }
312         }
313         fprintf(fo, "\n");
314         last_timestamp = entry->ktr_timestamp;
315 }
316
317 static
318 struct ktr_info *
319 kvm_ktrinfo(kvm_t *kd, void *kptr)
320 {
321         static struct ktr_info save_info;
322         static void *save_kptr;
323
324         if (kptr == NULL)
325                 return(NULL);
326         if (save_kptr != kptr) {
327                 if (kvm_read(kd, (uintptr_t)kptr, &save_info, sizeof(save_info)) == -1) {
328                         bzero(&save_info, sizeof(save_info));
329                 } else {
330                         save_kptr = kptr;
331                 }
332         }
333         return(&save_info);
334 }
335
336 static
337 const char *
338 kvm_string(kvm_t *kd, const char *kptr)
339 {
340         static char save_str[128];
341         static const char *save_kptr;
342         int l;
343         int n;
344
345         if (kptr == NULL)
346                 return("?");
347         if (save_kptr != kptr) {
348                 save_kptr = kptr;
349                 l = 0;
350                 while (l < sizeof(save_str) - 1) {
351                         n = 256 - ((intptr_t)(kptr + l) & 255);
352                         if (n > sizeof(save_str) - l - 1)
353                                 n = sizeof(save_str) - l - 1;
354                         if (kvm_read(kd, (uintptr_t)(kptr + l), save_str + l, n) < 0)
355                                 break;
356                         while (l < sizeof(save_str) && n) {
357                             if (save_str[l] == 0)
358                                     break;
359                             --n;
360                             ++l;
361                         }
362                         if (n)
363                             break;
364                 }
365                 save_str[l] = 0;
366         }
367         return(save_str);
368 }
369
370 static
371 const char *
372 trunc_path(const char *str, int maxlen)
373 {
374         int len = strlen(str);
375
376         if (len > maxlen)
377                 return(str + len - maxlen);
378         else
379                 return(str);
380 }
381
382 struct symdata {
383         TAILQ_ENTRY(symdata) link;
384         const char *symname;
385         char *symaddr;
386         char symtype;
387 };
388
389 static TAILQ_HEAD(symlist, symdata) symlist;
390 static struct symdata *symcache;
391 static char *symbegin;
392 static char *symend;
393
394 static
395 void
396 read_symbols(const char *execfile)
397 {
398         char buf[256];
399         char cmd[256];
400         int buflen = sizeof(buf);
401         FILE *fp;
402         struct symdata *sym;
403         char *s1;
404         char *s2;
405         char *s3;
406
407         TAILQ_INIT(&symlist);
408
409         if (execfile == NULL) {
410                 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
411                         execfile = "/kernel";
412                 else
413                         execfile = buf;
414         }
415         snprintf(cmd, sizeof(cmd), "nm -n %s", execfile);
416         if ((fp = popen(cmd, "r")) != NULL) {
417                 while (fgets(buf, sizeof(buf), fp) != NULL) {
418                     s1 = strtok(buf, " \t\n");
419                     s2 = strtok(NULL, " \t\n");
420                     s3 = strtok(NULL, " \t\n");
421                     if (s1 && s2 && s3) {
422                         sym = malloc(sizeof(struct symdata));
423                         sym->symaddr = (char *)strtoul(s1, NULL, 16);
424                         sym->symtype = s2[0];
425                         sym->symname = strdup(s3);
426                         if (strcmp(s3, "kernbase") == 0)
427                                 symbegin = sym->symaddr;
428                         if (strcmp(s3, "end") == 0)
429                                 symend = sym->symaddr;
430                         TAILQ_INSERT_TAIL(&symlist, sym, link);
431                     }
432                 }
433                 pclose(fp);
434         }
435         symcache = TAILQ_FIRST(&symlist);
436 }
437
438 static
439 const char *
440 address_to_symbol(void *kptr)
441 {
442         static char buf[64];
443
444         if (symcache == NULL ||
445            (char *)kptr < symbegin || (char *)kptr >= symend
446         ) {
447                 snprintf(buf, sizeof(buf), "%p", kptr);
448                 return(buf);
449         }
450         while ((char *)symcache->symaddr < (char *)kptr) {
451                 if (TAILQ_NEXT(symcache, link) == NULL)
452                         break;
453                 symcache = TAILQ_NEXT(symcache, link);
454         }
455         while ((char *)symcache->symaddr > (char *)kptr) {
456                 if (symcache != TAILQ_FIRST(&symlist))
457                         symcache = TAILQ_PREV(symcache, symlist, link);
458         }
459         snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
460                 (int)((char *)kptr - symcache->symaddr));
461         return(buf);
462 }
463
464 static void
465 usage(void)
466 {
467         fprintf(stderr, "usage: ktrdump [-acfinpqrtx] [-N execfile] "
468                         "[-M corefile] [-o outfile]\n");
469         exit(1);
470 }