1169689Skan/*-
2169689Skan * Copyright (c) 2002 Jake Burkholder
3169689Skan * Copyright (c) 2004 Robert Watson
4169689Skan * All rights reserved.
5169689Skan *
6169689Skan * Redistribution and use in source and binary forms, with or without
7169689Skan * modification, are permitted provided that the following conditions
8169689Skan * are met:
9169689Skan * 1. Redistributions of source code must retain the above copyright
10169689Skan *    notice, this list of conditions and the following disclaimer.
11169689Skan * 2. Redistributions in binary form must reproduce the above copyright
12169689Skan *    notice, this list of conditions and the following disclaimer in the
13169689Skan *    documentation and/or other materials provided with the distribution.
14169689Skan *
15169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18169689Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25169689Skan * SUCH DAMAGE.
26169689Skan */
27169689Skan
28169689Skan#include <sys/cdefs.h>
29169689Skan__FBSDID("$FreeBSD$");
30169689Skan
31169689Skan#include <sys/types.h>
32169689Skan#include <sys/ktr.h>
33169689Skan#include <sys/mman.h>
34169689Skan#include <sys/stat.h>
35169689Skan
36169689Skan#include <err.h>
37169689Skan#include <fcntl.h>
38169689Skan#include <kvm.h>
39169689Skan#include <limits.h>
40169689Skan#include <nlist.h>
41169689Skan#include <stdint.h>
42169689Skan#include <stdio.h>
43169689Skan#include <stdlib.h>
44169689Skan#include <string.h>
45169689Skan#include <unistd.h>
46169689Skan
47169689Skan#define	SBUFLEN	128
48169689Skan#define	USAGE \
49169689Skan	"usage: ktrdump [-cfqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n"
50169689Skan
51169689Skanstatic void usage(void);
52169689Skan
53169689Skanstatic struct nlist nl[] = {
54169689Skan	{ "_ktr_version" },
55169689Skan	{ "_ktr_entries" },
56169689Skan	{ "_ktr_idx" },
57169689Skan	{ "_ktr_buf" },
58169689Skan	{ NULL }
59169689Skan};
60169689Skan
61169689Skanstatic int cflag;
62169689Skanstatic int fflag;
63169689Skanstatic int Mflag;
64169689Skanstatic int Nflag;
65169689Skanstatic int qflag;
66169689Skanstatic int rflag;
67169689Skanstatic int tflag;
68169689Skanstatic int iflag;
69169689Skanstatic int hflag;
70169689Skan
71169689Skanstatic char corefile[PATH_MAX];
72169689Skanstatic char execfile[PATH_MAX];
73169689Skan
74169689Skanstatic char desc[SBUFLEN];
75169689Skanstatic char errbuf[_POSIX2_LINE_MAX];
76169689Skanstatic char fbuf[PATH_MAX];
77169689Skanstatic char obuf[PATH_MAX];
78169689Skanstatic char sbuf[KTR_PARMS][SBUFLEN];
79169689Skan
80169689Skan/*
81169689Skan * Reads the ktr trace buffer from kernel memory and prints the trace entries.
82169689Skan */
83169689Skanint
84169689Skanmain(int ac, char **av)
85169689Skan{
86169689Skan	u_long parms[KTR_PARMS];
87169689Skan	struct ktr_entry *buf;
88169689Skan	uintmax_t tlast, tnow;
89169689Skan	unsigned long bufptr;
90169689Skan	struct stat sb;
91169689Skan	kvm_t *kd;
92169689Skan	FILE *out;
93169689Skan	char *p;
94169689Skan	int version;
95169689Skan	int entries;
96169689Skan	int index;
97169689Skan	int parm;
98169689Skan	int in;
99169689Skan	int c;
100169689Skan	int i = 0;
101169689Skan
102169689Skan	/*
103169689Skan	 * Parse commandline arguments.
104169689Skan	 */
105169689Skan	out = stdout;
106169689Skan	while ((c = getopt(ac, av, "cfqrtHe:i:m:M:N:o:")) != -1)
107169689Skan		switch (c) {
108169689Skan		case 'c':
109169689Skan			cflag = 1;
110169689Skan			break;
111169689Skan		case 'N':
112169689Skan		case 'e':
113169689Skan			if (strlcpy(execfile, optarg, sizeof(execfile))
114169689Skan			    >= sizeof(execfile))
115169689Skan				errx(1, "%s: File name too long", optarg);
116169689Skan			Nflag = 1;
117169689Skan			break;
118169689Skan		case 'f':
119169689Skan			fflag = 1;
120169689Skan			break;
121169689Skan		case 'i':
122169689Skan			iflag = 1;
123169689Skan			if ((in = open(optarg, O_RDONLY)) == -1)
124169689Skan				err(1, "%s", optarg);
125169689Skan			break;
126169689Skan		case 'M':
127169689Skan		case 'm':
128169689Skan			if (strlcpy(corefile, optarg, sizeof(corefile))
129169689Skan			    >= sizeof(corefile))
130169689Skan				errx(1, "%s: File name too long", optarg);
131169689Skan			Mflag = 1;
132169689Skan			break;
133169689Skan		case 'o':
134169689Skan			if ((out = fopen(optarg, "w")) == NULL)
135169689Skan				err(1, "%s", optarg);
136169689Skan			break;
137169689Skan		case 'q':
138169689Skan			qflag++;
139169689Skan			break;
140169689Skan		case 'r':
141169689Skan			rflag = 1;
142169689Skan			break;
143169689Skan		case 't':
144169689Skan			tflag = 1;
145169689Skan			break;
146169689Skan		case 'H':
147169689Skan			hflag = 1;
148169689Skan			break;
149169689Skan		case '?':
150169689Skan		default:
151169689Skan			usage();
152169689Skan		}
153169689Skan	ac -= optind;
154169689Skan	av += optind;
155169689Skan	if (ac != 0)
156169689Skan		usage();
157169689Skan
158169689Skan	/*
159169689Skan	 * Open our execfile and corefile, resolve needed symbols and read in
160169689Skan	 * the trace buffer.
161169689Skan	 */
162169689Skan	if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
163169689Skan	    Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
164169689Skan		errx(1, "%s", errbuf);
165169689Skan	if (kvm_nlist(kd, nl) != 0 ||
166169689Skan	    kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1)
167169689Skan		errx(1, "%s", kvm_geterr(kd));
168169689Skan	if (version != KTR_VERSION)
169169689Skan		errx(1, "ktr version mismatch");
170169689Skan	if (iflag) {
171169689Skan		if (fstat(in, &sb) == -1)
172169689Skan			errx(1, "stat");
173169689Skan		entries = sb.st_size / sizeof(*buf);
174169689Skan		index = 0;
175169689Skan		buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
176169689Skan		if (buf == MAP_FAILED)
177169689Skan			errx(1, "mmap");
178169689Skan	} else {
179169689Skan		if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries))
180169689Skan		    == -1)
181169689Skan			errx(1, "%s", kvm_geterr(kd));
182169689Skan		if ((buf = malloc(sizeof(*buf) * entries)) == NULL)
183169689Skan			err(1, NULL);
184169689Skan		if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 ||
185169689Skan		    kvm_read(kd, nl[3].n_value, &bufptr,
186169689Skan		    sizeof(bufptr)) == -1 ||
187169689Skan		    kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1)
188169689Skan			errx(1, "%s", kvm_geterr(kd));
189169689Skan	}
190169689Skan
191169689Skan	/*
192169689Skan	 * Print a nice header.
193169689Skan	 */
194169689Skan	if (!qflag) {
195169689Skan		fprintf(out, "%-6s ", "index");
196169689Skan		if (cflag)
197169689Skan			fprintf(out, "%-3s ", "cpu");
198169689Skan		if (tflag)
199169689Skan			fprintf(out, "%-16s ", "timestamp");
200169689Skan		if (fflag)
201169689Skan			fprintf(out, "%-40s ", "file and line");
202169689Skan		if (hflag)
203169689Skan			fprintf(out, "%-18s ", "tid");
204169689Skan		fprintf(out, "%s", "trace");
205169689Skan		fprintf(out, "\n");
206169689Skan
207169689Skan		fprintf(out, "------ ");
208169689Skan		if (cflag)
209169689Skan			fprintf(out, "--- ");
210169689Skan		if (tflag)
211169689Skan			fprintf(out, "---------------- ");
212169689Skan		if (fflag)
213169689Skan			fprintf(out,
214169689Skan			    "---------------------------------------- ");
215169689Skan		if (hflag)
216169689Skan			fprintf(out, "------------------ ");
217169689Skan		fprintf(out, "----- ");
218169689Skan		fprintf(out, "\n");
219169689Skan	}
220169689Skan
221169689Skan	/*
222169689Skan	 * Now tear through the trace buffer.
223169689Skan	 */
224169689Skan	if (!iflag)
225169689Skan		i = (index - 1) % entries;
226169689Skan	tlast = -1;
227169689Skan	for (;;) {
228169689Skan		if (buf[i].ktr_desc == NULL)
229169689Skan			break;
230169689Skan		if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc,
231169689Skan		    sizeof(desc)) == -1)
232169689Skan			errx(1, "%s", kvm_geterr(kd));
233169689Skan		desc[sizeof(desc) - 1] = '\0';
234169689Skan		parm = 0;
235169689Skan		for (p = desc; (c = *p++) != '\0';) {
236169689Skan			if (c != '%')
237169689Skan				continue;
238169689Skannext:			if ((c = *p++) == '\0')
239169689Skan				break;
240169689Skan			if (parm == KTR_PARMS)
241169689Skan				errx(1, "too many parameters");
242169689Skan			switch (c) {
243169689Skan			case '0': case '1': case '2': case '3': case '4':
244169689Skan			case '5': case '6': case '7': case '8': case '9':
245169689Skan			case '#': case '-': case ' ': case '+': case '\'':
246169689Skan			case 'h': case 'l': case 'j': case 't': case 'z':
247169689Skan			case 'q': case 'L': case '.':
248169689Skan				goto next;
249169689Skan			case 's':
250169689Skan				if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm],
251169689Skan				    sbuf[parm], sizeof(sbuf[parm])) == -1)
252169689Skan					strcpy(sbuf[parm], "(null)");
253169689Skan				sbuf[parm][sizeof(sbuf[0]) - 1] = '\0';
254169689Skan				parms[parm] = (u_long)sbuf[parm];
255169689Skan				parm++;
256169689Skan				break;
257169689Skan			default:
258169689Skan				parms[parm] = buf[i].ktr_parms[parm];
259169689Skan				parm++;
260169689Skan				break;
261169689Skan			}
262169689Skan		}
263169689Skan		fprintf(out, "%6d ", i);
264169689Skan		if (cflag)
265169689Skan			fprintf(out, "%3d ", buf[i].ktr_cpu);
266169689Skan		if (tflag) {
267169689Skan			tnow = (uintmax_t)buf[i].ktr_timestamp;
268169689Skan			if (rflag) {
269169689Skan				if (tlast == -1)
270169689Skan					tlast = tnow;
271169689Skan				fprintf(out, "%16ju ", !iflag ? tlast - tnow :
272169689Skan				    tnow - tlast);
273				tlast = tnow;
274			} else
275				fprintf(out, "%16ju ", tnow);
276		}
277		if (fflag) {
278			if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf,
279			    sizeof(fbuf)) == -1)
280				strcpy(fbuf, "(null)");
281			snprintf(obuf, sizeof(obuf), "%s:%d", fbuf,
282			    buf[i].ktr_line);
283			fprintf(out, "%-40s ", obuf);
284		}
285		if (hflag)
286			fprintf(out, "%p ", buf[i].ktr_thread);
287		fprintf(out, desc, parms[0], parms[1], parms[2], parms[3],
288		    parms[4], parms[5]);
289		fprintf(out, "\n");
290		if (!iflag) {
291			if (i == index)
292				break;
293			i = (i - 1) % entries;
294		} else {
295			if (++i == entries)
296				break;
297		}
298	}
299
300	return (0);
301}
302
303static void
304usage(void)
305{
306
307	fprintf(stderr, USAGE);
308	exit(1);
309}
310