ktrdump.c revision 125858
1/*-
2 * Copyright (c) 2002 Jake Burkholder
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/usr.bin/ktrdump/ktrdump.c 125858 2004-02-15 22:12:32Z dwmalone $");
29
30#include <sys/types.h>
31#include <sys/ktr.h>
32#include <sys/mman.h>
33#include <sys/stat.h>
34
35#include <err.h>
36#include <fcntl.h>
37#include <kvm.h>
38#include <limits.h>
39#include <nlist.h>
40#include <stdint.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#define	SBUFLEN	128
47#define	USAGE \
48	"usage: ktrdump [-c] [-f] [-t] [-e execfile] [-i ktrfile ] [-m corefile] [-o outfile]"
49
50extern char *optarg;
51extern int optind;
52
53static void usage(void);
54
55static struct nlist nl[] = {
56	{ "_ktr_version" },
57	{ "_ktr_entries" },
58	{ "_ktr_idx" },
59	{ "_ktr_buf" },
60	{ NULL }
61};
62
63static int cflag;
64static int eflag;
65static int fflag;
66static int mflag;
67static int tflag;
68static int iflag;
69
70static char corefile[PATH_MAX];
71static char execfile[PATH_MAX];
72
73static char desc[SBUFLEN];
74static char errbuf[_POSIX2_LINE_MAX];
75static char fbuf[PATH_MAX];
76static char obuf[PATH_MAX];
77static char sbuf[KTR_PARMS][SBUFLEN];
78
79/*
80 * Reads the ktr trace buffer from kernel memory and prints the trace entries.
81 */
82int
83main(int ac, char **av)
84{
85	u_long parms[KTR_PARMS];
86	struct ktr_entry *buf;
87	struct stat sb;
88	kvm_t *kd;
89	FILE *out;
90	char *p;
91	int version;
92	int entries;
93	int index;
94	int parm;
95	int in;
96	int c;
97	int i;
98
99	/*
100	 * Parse commandline arguments.
101	 */
102	out = stdout;
103	while ((c = getopt(ac, av, "cfte:i:m:o:")) != -1)
104		switch (c) {
105		case 'c':
106			cflag = 1;
107			break;
108		case 'e':
109			if (strlcpy(execfile, optarg, sizeof(execfile))
110			    >= sizeof(execfile))
111				errx(1, "%s: File name too long", optarg);
112			eflag = 1;
113			break;
114		case 'f':
115			fflag = 1;
116			break;
117		case 'i':
118			iflag = 1;
119			if ((in = open(optarg, O_RDONLY)) == -1)
120				err(1, "%s", optarg);
121			break;
122		case 'm':
123			if (strlcpy(corefile, optarg, sizeof(corefile))
124			    >= sizeof(corefile))
125				errx(1, "%s: File name too long", optarg);
126			mflag = 1;
127			break;
128		case 'o':
129			if ((out = fopen(optarg, "w")) == NULL)
130				err(1, "%s", optarg);
131			break;
132		case 't':
133			tflag = 1;
134			break;
135		case '?':
136		default:
137			usage();
138		}
139	ac -= optind;
140	av += optind;
141	if (ac != 0)
142		usage();
143
144	/*
145	 * Open our execfile and corefile, resolve needed symbols and read in
146	 * the trace buffer.
147	 */
148	if ((kd = kvm_openfiles(eflag ? execfile : NULL,
149	    mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
150		errx(1, "%s", errbuf);
151	if (kvm_nlist(kd, nl) != 0 ||
152	    kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1)
153		errx(1, "%s", kvm_geterr(kd));
154	if (version != KTR_VERSION)
155		errx(1, "ktr version mismatch");
156	if (iflag) {
157		if (fstat(in, &sb) == -1)
158			errx(1, "stat");
159		entries = sb.st_size / sizeof(*buf);
160		index = 0;
161		buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
162		if (buf == MAP_FAILED)
163			errx(1, "mmap");
164	} else {
165		if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries))
166		    == -1)
167			errx(1, "%s", kvm_geterr(kd));
168		if ((buf = malloc(sizeof(*buf) * entries)) == NULL)
169			err(1, NULL);
170		if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 ||
171		    kvm_read(kd, nl[3].n_value, buf, sizeof(*buf) * entries)
172		    == -1)
173			errx(1, "%s", kvm_geterr(kd));
174	}
175
176	/*
177	 * Print a nice header.
178	 */
179	fprintf(out, "%-6s ", "index");
180	if (cflag)
181		fprintf(out, "%-3s ", "cpu");
182	if (tflag)
183		fprintf(out, "%-16s ", "timestamp");
184	if (fflag)
185		fprintf(out, "%-32s ", "file and line");
186	fprintf(out, "%s", "trace");
187	fprintf(out, "\n");
188
189	fprintf(out, "------ ");
190	if (cflag)
191		fprintf(out, "--- ");
192	if (tflag)
193		fprintf(out, "---------------- ");
194	if (fflag)
195		fprintf(out, "---------------------------------------- ");
196	fprintf(out, "----- ");
197	fprintf(out, "\n");
198
199	/*
200	 * Now tear through the trace buffer.
201	 */
202	if (!iflag)
203		i = (index - 1) & (entries - 1);
204	for (;;) {
205		if (buf[i].ktr_desc == NULL)
206			break;
207		if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc,
208		    sizeof(desc)) == -1)
209			errx(1, "%s", kvm_geterr(kd));
210		desc[sizeof(desc) - 1] = '\0';
211		parm = 0;
212		for (p = desc; (c = *p++) != '\0';) {
213			if (c != '%')
214				continue;
215			if ((c = *p++) == '\0')
216				break;
217			if (parm == KTR_PARMS)
218				errx(1, "too many parameters");
219			switch (c) {
220			case 's':
221				if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm],
222				    sbuf[parm], sizeof(sbuf[parm])) == -1)
223					strcpy(sbuf[parm], "(null)");
224				sbuf[parm][sizeof(sbuf[0]) - 1] = '\0';
225				parms[parm] = (u_long)sbuf[parm];
226				parm++;
227				break;
228			default:
229				parms[parm] = buf[i].ktr_parms[parm];
230				parm++;
231				break;
232			}
233		}
234		fprintf(out, "%6d ", i);
235		if (cflag)
236			fprintf(out, "%3d ", buf[i].ktr_cpu);
237		if (tflag)
238			fprintf(out, "%16ju ",
239			    (uintmax_t)buf[i].ktr_timestamp);
240		if (fflag) {
241			if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf,
242			    sizeof(fbuf)) == -1)
243				strcpy(fbuf, "(null)");
244			snprintf(obuf, sizeof(obuf), "%s:%d", fbuf,
245			    buf[i].ktr_line);
246			fprintf(out, "%-40s ", obuf);
247		}
248		fprintf(out, desc, parms[0], parms[1], parms[2], parms[3],
249		    parms[4], parms[5]);
250		fprintf(out, "\n");
251		if (!iflag) {
252			if (i == index)
253				break;
254			i = (i - 1) & (entries - 1);
255		} else {
256			if (++i == entries)
257				break;
258		}
259	}
260
261	return (0);
262}
263
264static void
265usage(void)
266{
267	errx(1, USAGE);
268}
269