xref: /dragonfly/usr.bin/ktrdump/ktrdump.c (revision 1bf4b486)
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 }
471