Synchronize the TSC between all cpus on startup and provide a sysctl,
[dragonfly.git] / usr.bin / ktrdump / ktrdump.c
CommitLineData
9f815599
HP
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.
6e8dad34
MD
26 *
27 * $FreeBSD: src/usr.bin/ktrdump/ktrdump.c,v 1.10 2005/05/21 09:55:06 ru Exp $
fa9963c9 28 * $DragonFly: src/usr.bin/ktrdump/ktrdump.c,v 1.3 2005/06/21 06:50:28 dillon Exp $
9f815599
HP
29 */
30
6e8dad34
MD
31#include <sys/cdefs.h>
32
33#include <sys/types.h>
9f815599
HP
34#include <sys/ktr.h>
35#include <sys/mman.h>
36#include <sys/stat.h>
fa9963c9 37#include <sys/queue.h>
9f815599
HP
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
9f815599
HP
51
52extern char *optarg;
53extern int optind;
54
55static void usage(void);
6e8dad34
MD
56static void print_header(FILE *fo, int row);
57static void print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry);
58static struct ktr_info *kvm_ktrinfo(kvm_t *kd, void *kptr);
59static const char *kvm_string(kvm_t *kd, const char *kptr);
60static const char *trunc_path(const char *str, int maxlen);
fa9963c9
MD
61static void read_symbols(const char *execfile);
62static const char *address_to_symbol(void *kptr);
9f815599
HP
63
64static struct nlist nl[] = {
65 { "_ktr_version" },
66 { "_ktr_entries" },
67 { "_ktr_idx" },
68 { "_ktr_buf" },
6e8dad34 69 { "_ncpus" },
9f815599
HP
70 { NULL }
71};
72
73static int cflag;
9f815599 74static int fflag;
6e8dad34 75static int iflag;
6e8dad34 76static int nflag;
9f815599
HP
77static int qflag;
78static int rflag;
79static int tflag;
6e8dad34
MD
80static int xflag;
81static int pflag;
fa9963c9
MD
82static int Mflag;
83static int Nflag;
6e8dad34 84static int64_t last_timestamp;
9f815599
HP
85
86static char corefile[PATH_MAX];
87static char execfile[PATH_MAX];
88
89static char desc[SBUFLEN];
90static char errbuf[_POSIX2_LINE_MAX];
91static char fbuf[PATH_MAX];
92static char obuf[PATH_MAX];
9f815599
HP
93
94/*
95 * Reads the ktr trace buffer from kernel memory and prints the trace entries.
96 */
97int
98main(int ac, char **av)
99{
6e8dad34 100 struct ktr_entry **ktr_buf;
9f815599
HP
101 uintmax_t tlast, tnow;
102 struct stat sb;
103 kvm_t *kd;
6e8dad34 104 FILE *fo;
9f815599
HP
105 char *p;
106 int version;
107 int entries;
6e8dad34
MD
108 int *ktr_idx;
109 int ncpus;
fa9963c9 110 int did_display_flag = 0;
9f815599
HP
111 int in;
112 int c;
6e8dad34
MD
113 int i;
114 int n;
9f815599
HP
115
116 /*
117 * Parse commandline arguments.
118 */
6e8dad34 119 fo = stdout;
fa9963c9 120 while ((c = getopt(ac, av, "acfiqrtxpN:M:o:")) != -1) {
9f815599 121 switch (c) {
6e8dad34
MD
122 case 'a':
123 cflag = 1;
124 iflag = 1;
125 tflag = 1;
126 xflag = 1;
127 fflag = 1;
128 pflag = 1;
129 break;
9f815599
HP
130 case 'c':
131 cflag = 1;
132 break;
6e8dad34 133 case 'N':
9f815599
HP
134 if (strlcpy(execfile, optarg, sizeof(execfile))
135 >= sizeof(execfile))
136 errx(1, "%s: File name too long", optarg);
fa9963c9 137 Nflag = 1;
9f815599
HP
138 break;
139 case 'f':
140 fflag = 1;
141 break;
142 case 'i':
143 iflag = 1;
9f815599 144 break;
6e8dad34 145 case 'M':
9f815599
HP
146 if (strlcpy(corefile, optarg, sizeof(corefile))
147 >= sizeof(corefile))
148 errx(1, "%s: File name too long", optarg);
fa9963c9
MD
149 Mflag = 1;
150 break;
151 case 'n':
152 nflag = 1;
9f815599
HP
153 break;
154 case 'o':
6e8dad34 155 if ((fo = fopen(optarg, "w")) == NULL)
9f815599
HP
156 err(1, "%s", optarg);
157 break;
6e8dad34
MD
158 case 'p':
159 pflag++;
160 break;
9f815599
HP
161 case 'q':
162 qflag++;
163 break;
164 case 'r':
165 rflag = 1;
166 break;
167 case 't':
168 tflag = 1;
169 break;
6e8dad34
MD
170 case 'x':
171 xflag = 1;
172 break;
9f815599
HP
173 case '?':
174 default:
175 usage();
176 }
fa9963c9
MD
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
9f815599
HP
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 */
fa9963c9
MD
195 if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
196 Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
9f815599 197 errx(1, "%s", errbuf);
6e8dad34
MD
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)
9f815599 201 errx(1, "%s", kvm_geterr(kd));
6e8dad34
MD
202 if (kvm_read(kd, nl[4].n_value, &ncpus, sizeof(ncpus)) == -1)
203 errx(1, "%s", kvm_geterr(kd));
204
9f815599
HP
205 if (version != KTR_VERSION)
206 errx(1, "ktr version mismatch");
6e8dad34
MD
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
fa9963c9
MD
212 if (nflag == 0)
213 read_symbols(Nflag ? execfile : NULL);
214
6e8dad34
MD
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));
9f815599
HP
224 }
225
226 /*
6e8dad34 227 * Now tear through the trace buffer.
9f815599 228 */
6e8dad34
MD
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}
9f815599 238
6e8dad34
MD
239static void
240print_header(FILE *fo, int row)
241{
242 if (qflag == 0 && row % 20 == 0) {
243 fprintf(fo, "%-6s ", "index");
9f815599 244 if (cflag)
6e8dad34
MD
245 fprintf(fo, "%-3s ", "cpu");
246 if (tflag || rflag)
247 fprintf(fo, "%-16s ", "timestamp");
fa9963c9
MD
248 if (xflag) {
249 if (nflag)
250 fprintf(fo, "%-10s %-10s", "caller2", "caller1");
251 else
252 fprintf(fo, "%-20s %-20s", "caller2", "caller1");
253 }
6e8dad34
MD
254 if (iflag)
255 fprintf(fo, "%-20s ", "ID");
9f815599 256 if (fflag)
6e8dad34
MD
257 fprintf(fo, "%10s%-30s ", "", "file and line");
258 if (pflag)
259 fprintf(fo, "%s", "trace");
260 fprintf(fo, "\n");
9f815599 261 }
6e8dad34 262}
9f815599 263
6e8dad34
MD
264static void
265print_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 }
fa9963c9
MD
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 }
6e8dad34
MD
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, "");
9f815599 311 }
6e8dad34
MD
312 }
313 fprintf(fo, "\n");
314 last_timestamp = entry->ktr_timestamp;
315}
316
317static
318struct ktr_info *
319kvm_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));
9f815599 329 } else {
6e8dad34
MD
330 save_kptr = kptr;
331 }
332 }
333 return(&save_info);
334}
335
336static
337const char *
338kvm_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)
9f815599 355 break;
6e8dad34
MD
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;
9f815599 364 }
6e8dad34 365 save_str[l] = 0;
9f815599 366 }
6e8dad34
MD
367 return(save_str);
368}
9f815599 369
6e8dad34
MD
370static
371const char *
372trunc_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);
9f815599
HP
380}
381
fa9963c9
MD
382struct symdata {
383 TAILQ_ENTRY(symdata) link;
384 const char *symname;
385 char *symaddr;
386 char symtype;
387};
388
389static TAILQ_HEAD(symlist, symdata) symlist;
390static struct symdata *symcache;
391static char *symbegin;
392static char *symend;
393
394static
395void
396read_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
438static
439const char *
440address_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
9f815599
HP
464static void
465usage(void)
466{
fa9963c9 467 fprintf(stderr, "usage: ktrdump [-acfinpqrtx] [-N execfile] "
6e8dad34
MD
468 "[-M corefile] [-o outfile]\n");
469 exit(1);
9f815599 470}