kdump.c revision 152331
1/*-
2 * Copyright (c) 1988, 1993
3 *	The Regents of the University of California.  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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1988, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)kdump.c	8.1 (Berkeley) 6/6/93";
43#endif
44#endif /* not lint */
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: head/usr.bin/kdump/kdump.c 152331 2005-11-12 14:21:48Z rwatson $");
47
48#define _KERNEL
49extern int errno;
50#include <sys/errno.h>
51#undef _KERNEL
52#include <sys/param.h>
53#include <sys/errno.h>
54#define _KERNEL
55#include <sys/time.h>
56#undef _KERNEL
57#include <sys/uio.h>
58#include <sys/ktrace.h>
59#include <sys/ioctl.h>
60#include <sys/ptrace.h>
61#include <err.h>
62#include <locale.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <unistd.h>
67#include <vis.h>
68#include "ktrace.h"
69
70int fread_tail(void *, int, int);
71void dumpheader(struct ktr_header *);
72void ktrsyscall(struct ktr_syscall *);
73void ktrsysret(struct ktr_sysret *);
74void ktrnamei(char *, int);
75void hexdump(char *, int, int);
76void visdump(char *, int, int);
77void ktrgenio(struct ktr_genio *, int);
78void ktrpsig(struct ktr_psig *);
79void ktrcsw(struct ktr_csw *);
80void ktruser(int, unsigned char *);
81void usage(void);
82const char *ioctlname(u_long);
83
84int timestamp, decimal, fancy = 1, suppressdata, tail, threads, maxdata;
85const char *tracefile = DEF_TRACEFILE;
86struct ktr_header ktr_header;
87
88#define eqs(s1, s2)	(strcmp((s1), (s2)) == 0)
89
90int
91main(int argc, char *argv[])
92{
93	int ch, ktrlen, size;
94	void *m;
95	int trpoints = ALL_POINTS;
96	int drop_logged;
97	pid_t pid = 0;
98
99	(void) setlocale(LC_CTYPE, "");
100
101	while ((ch = getopt(argc,argv,"f:dElm:np:HRsTt:")) != -1)
102		switch((char)ch) {
103		case 'f':
104			tracefile = optarg;
105			break;
106		case 'd':
107			decimal = 1;
108			break;
109		case 'l':
110			tail = 1;
111			break;
112		case 'm':
113			maxdata = atoi(optarg);
114			break;
115		case 'n':
116			fancy = 0;
117			break;
118		case 'p':
119			pid = atoi(optarg);
120			break;
121		case 's':
122			suppressdata = 1;
123			break;
124		case 'E':
125			timestamp = 3;	/* elapsed timestamp */
126			break;
127		case 'H':
128			threads = 1;
129			break;
130		case 'R':
131			timestamp = 2;	/* relative timestamp */
132			break;
133		case 'T':
134			timestamp = 1;
135			break;
136		case 't':
137			trpoints = getpoints(optarg);
138			if (trpoints < 0)
139				errx(1, "unknown trace point in %s", optarg);
140			break;
141		default:
142			usage();
143		}
144
145	if (argc > optind)
146		usage();
147
148	m = (void *)malloc(size = 1025);
149	if (m == NULL)
150		errx(1, "%s", strerror(ENOMEM));
151	if (!freopen(tracefile, "r", stdin))
152		err(1, "%s", tracefile);
153	drop_logged = 0;
154	while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
155		if (ktr_header.ktr_type & KTR_DROP) {
156			ktr_header.ktr_type &= ~KTR_DROP;
157			if (!drop_logged && threads) {
158				(void)printf("%6d %6d %-8.*s Events dropped.\n",
159				    ktr_header.ktr_pid, ktr_header.ktr_tid >
160				    0 ? ktr_header.ktr_tid : 0, MAXCOMLEN,
161				    ktr_header.ktr_comm);
162				drop_logged = 1;
163			} else if (!drop_logged) {
164				(void)printf("%6d %-8.*s Events dropped.\n",
165				    ktr_header.ktr_pid, MAXCOMLEN,
166				    ktr_header.ktr_comm);
167				drop_logged = 1;
168			}
169		}
170		if (trpoints & (1<<ktr_header.ktr_type))
171			if (pid == 0 || ktr_header.ktr_pid == pid)
172				dumpheader(&ktr_header);
173		if ((ktrlen = ktr_header.ktr_len) < 0)
174			errx(1, "bogus length 0x%x", ktrlen);
175		if (ktrlen > size) {
176			m = (void *)realloc(m, ktrlen+1);
177			if (m == NULL)
178				errx(1, "%s", strerror(ENOMEM));
179			size = ktrlen;
180		}
181		if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
182			errx(1, "data too short");
183		if (pid && ktr_header.ktr_pid != pid)
184			continue;
185		if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
186			continue;
187		drop_logged = 0;
188		switch (ktr_header.ktr_type) {
189		case KTR_SYSCALL:
190			ktrsyscall((struct ktr_syscall *)m);
191			break;
192		case KTR_SYSRET:
193			ktrsysret((struct ktr_sysret *)m);
194			break;
195		case KTR_NAMEI:
196			ktrnamei(m, ktrlen);
197			break;
198		case KTR_GENIO:
199			ktrgenio((struct ktr_genio *)m, ktrlen);
200			break;
201		case KTR_PSIG:
202			ktrpsig((struct ktr_psig *)m);
203			break;
204		case KTR_CSW:
205			ktrcsw((struct ktr_csw *)m);
206			break;
207		case KTR_USER:
208			ktruser(ktrlen, m);
209			break;
210		default:
211			printf("\n");
212			break;
213		}
214		if (tail)
215			(void)fflush(stdout);
216	}
217	return 0;
218}
219
220int
221fread_tail(void *buf, int size, int num)
222{
223	int i;
224
225	while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
226		(void)sleep(1);
227		clearerr(stdin);
228	}
229	return (i);
230}
231
232void
233dumpheader(struct ktr_header *kth)
234{
235	static char unknown[64];
236	static struct timeval prevtime, temp;
237	const char *type;
238
239	switch (kth->ktr_type) {
240	case KTR_SYSCALL:
241		type = "CALL";
242		break;
243	case KTR_SYSRET:
244		type = "RET ";
245		break;
246	case KTR_NAMEI:
247		type = "NAMI";
248		break;
249	case KTR_GENIO:
250		type = "GIO ";
251		break;
252	case KTR_PSIG:
253		type = "PSIG";
254		break;
255	case KTR_CSW:
256		type = "CSW";
257		break;
258	case KTR_USER:
259		type = "USER";
260		break;
261	default:
262		(void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type);
263		type = unknown;
264	}
265
266	/*
267	 * The ktr_tid field was previously the ktr_buffer field, which held
268	 * the kernel pointer value for the buffer associated with data
269	 * following the record header.  It now holds a threadid, but only
270	 * for trace files after the change.  Older trace files still contain
271	 * kernel pointers.  Detect this and suppress the results by printing
272	 * negative tid's as 0.
273	 */
274	if (threads)
275		(void)printf("%6d %6d %-8.*s ", kth->ktr_pid, kth->ktr_tid >
276		    0 ? kth->ktr_tid : 0, MAXCOMLEN, kth->ktr_comm);
277	else
278		(void)printf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN,
279		    kth->ktr_comm);
280	if (timestamp) {
281		if (timestamp == 3) {
282			if (prevtime.tv_sec == 0)
283				prevtime = kth->ktr_time;
284			timevalsub(&kth->ktr_time, &prevtime);
285		}
286		if (timestamp == 2) {
287			temp = kth->ktr_time;
288			timevalsub(&kth->ktr_time, &prevtime);
289			prevtime = temp;
290		}
291		(void)printf("%ld.%06ld ",
292		    kth->ktr_time.tv_sec, kth->ktr_time.tv_usec);
293	}
294	(void)printf("%s  ", type);
295}
296
297#include <sys/syscall.h>
298#define KTRACE
299#include <sys/kern/syscalls.c>
300#undef KTRACE
301int nsyscalls = sizeof (syscallnames) / sizeof (syscallnames[0]);
302
303static const char *ptrace_ops[] = {
304	"PT_TRACE_ME",	"PT_READ_I",	"PT_READ_D",	"PT_READ_U",
305	"PT_WRITE_I",	"PT_WRITE_D",	"PT_WRITE_U",	"PT_CONTINUE",
306	"PT_KILL",	"PT_STEP",	"PT_ATTACH",	"PT_DETACH",
307};
308
309void
310ktrsyscall(struct ktr_syscall *ktr)
311{
312	int narg = ktr->ktr_narg;
313	register_t *ip;
314
315	if (ktr->ktr_code >= nsyscalls || ktr->ktr_code < 0)
316		(void)printf("[%d]", ktr->ktr_code);
317	else
318		(void)printf("%s", syscallnames[ktr->ktr_code]);
319	ip = &ktr->ktr_args[0];
320	if (narg) {
321		char c = '(';
322		if (fancy) {
323			if (ktr->ktr_code == SYS_ioctl) {
324				const char *cp;
325				if (decimal)
326					(void)printf("(%ld", (long)*ip);
327				else
328					(void)printf("(%#lx", (long)*ip);
329				ip++;
330				narg--;
331				if ((cp = ioctlname(*ip)) != NULL)
332					(void)printf(",%s", cp);
333				else {
334					if (decimal)
335						(void)printf(",%ld", (long)*ip);
336					else
337						(void)printf(",%#lx ", (long)*ip);
338				}
339				c = ',';
340				ip++;
341				narg--;
342			} else if (ktr->ktr_code == SYS_ptrace) {
343				if ((size_t)*ip < sizeof(ptrace_ops) /
344				    sizeof(ptrace_ops[0]) && *ip >= 0)
345					(void)printf("(%s", ptrace_ops[*ip]);
346#ifdef PT_GETREGS
347				else if (*ip == PT_GETREGS)
348					(void)printf("(%s", "PT_GETREGS");
349#endif
350#ifdef PT_SETREGS
351				else if (*ip == PT_SETREGS)
352					(void)printf("(%s", "PT_SETREGS");
353#endif
354#ifdef PT_GETFPREGS
355				else if (*ip == PT_GETFPREGS)
356					(void)printf("(%s", "PT_GETFPREGS");
357#endif
358#ifdef PT_SETFPREGS
359				else if (*ip == PT_SETFPREGS)
360					(void)printf("(%s", "PT_SETFPREGS");
361#endif
362#ifdef PT_GETDBREGS
363				else if (*ip == PT_GETDBREGS)
364					(void)printf("(%s", "PT_GETDBREGS");
365#endif
366#ifdef PT_SETDBREGS
367				else if (*ip == PT_SETDBREGS)
368					(void)printf("(%s", "PT_SETDBREGS");
369#endif
370				else
371					(void)printf("(%ld", (long)*ip);
372				c = ',';
373				ip++;
374				narg--;
375			}
376		}
377		while (narg) {
378			if (decimal)
379				(void)printf("%c%ld", c, (long)*ip);
380			else
381				(void)printf("%c%#lx", c, (long)*ip);
382			c = ',';
383			ip++;
384			narg--;
385		}
386		(void)putchar(')');
387	}
388	(void)putchar('\n');
389}
390
391void
392ktrsysret(struct ktr_sysret *ktr)
393{
394	register_t ret = ktr->ktr_retval;
395	int error = ktr->ktr_error;
396	int code = ktr->ktr_code;
397
398	if (code >= nsyscalls || code < 0)
399		(void)printf("[%d] ", code);
400	else
401		(void)printf("%s ", syscallnames[code]);
402
403	if (error == 0) {
404		if (fancy) {
405			(void)printf("%d", ret);
406			if (ret < 0 || ret > 9)
407				(void)printf("/%#lx", (long)ret);
408		} else {
409			if (decimal)
410				(void)printf("%ld", (long)ret);
411			else
412				(void)printf("%#lx", (long)ret);
413		}
414	} else if (error == ERESTART)
415		(void)printf("RESTART");
416	else if (error == EJUSTRETURN)
417		(void)printf("JUSTRETURN");
418	else {
419		(void)printf("-1 errno %d", ktr->ktr_error);
420		if (fancy)
421			(void)printf(" %s", strerror(ktr->ktr_error));
422	}
423	(void)putchar('\n');
424}
425
426void
427ktrnamei(char *cp, int len)
428{
429	(void)printf("\"%.*s\"\n", len, cp);
430}
431
432void
433hexdump(char *p, int len, int screenwidth)
434{
435	int n, i;
436	int width;
437
438	width = 0;
439	do {
440		width += 2;
441		i = 13;			/* base offset */
442		i += (width / 2) + 1;	/* spaces every second byte */
443		i += (width * 2);	/* width of bytes */
444		i += 3;			/* "  |" */
445		i += width;		/* each byte */
446		i += 1;			/* "|" */
447	} while (i < screenwidth);
448	width -= 2;
449
450	for (n = 0; n < len; n += width) {
451		for (i = n; i < n + width; i++) {
452			if ((i % width) == 0) {	/* beginning of line */
453				printf("       0x%04x", i);
454			}
455			if ((i % 2) == 0) {
456				printf(" ");
457			}
458			if (i < len)
459				printf("%02x", p[i] & 0xff);
460			else
461				printf("  ");
462		}
463		printf("  |");
464		for (i = n; i < n + width; i++) {
465			if (i >= len)
466				break;
467			if (p[i] >= ' ' && p[i] <= '~')
468				printf("%c", p[i]);
469			else
470				printf(".");
471		}
472		printf("|\n");
473	}
474	if ((i % width) != 0)
475		printf("\n");
476}
477
478void
479visdump(char *dp, int datalen, int screenwidth)
480{
481	int col = 0;
482	char *cp;
483	int width;
484	char visbuf[5];
485
486	(void)printf("       \"");
487	col = 8;
488	for (;datalen > 0; datalen--, dp++) {
489		(void) vis(visbuf, *dp, VIS_CSTYLE, *(dp+1));
490		cp = visbuf;
491		/*
492		 * Keep track of printables and
493		 * space chars (like fold(1)).
494		 */
495		if (col == 0) {
496			(void)putchar('\t');
497			col = 8;
498		}
499		switch(*cp) {
500		case '\n':
501			col = 0;
502			(void)putchar('\n');
503			continue;
504		case '\t':
505			width = 8 - (col&07);
506			break;
507		default:
508			width = strlen(cp);
509		}
510		if (col + width > (screenwidth-2)) {
511			(void)printf("\\\n\t");
512			col = 8;
513		}
514		col += width;
515		do {
516			(void)putchar(*cp++);
517		} while (*cp);
518	}
519	if (col == 0)
520		(void)printf("       ");
521	(void)printf("\"\n");
522}
523
524void
525ktrgenio(struct ktr_genio *ktr, int len)
526{
527	int datalen = len - sizeof (struct ktr_genio);
528	char *dp = (char *)ktr + sizeof (struct ktr_genio);
529	static int screenwidth = 0;
530	int i, binary;
531
532	if (screenwidth == 0) {
533		struct winsize ws;
534
535		if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
536		    ws.ws_col > 8)
537			screenwidth = ws.ws_col;
538		else
539			screenwidth = 80;
540	}
541	printf("fd %d %s %d byte%s\n", ktr->ktr_fd,
542		ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen,
543		datalen == 1 ? "" : "s");
544	if (suppressdata)
545		return;
546	if (maxdata && datalen > maxdata)
547		datalen = maxdata;
548
549	for (i = 0, binary = 0; i < datalen && binary == 0; i++)  {
550		if (dp[i] >= 32 && dp[i] < 127)
551			continue;
552		if (dp[i] == 10 || dp[i] == 13 || dp[i] == 0 || dp[i] == 9)
553			continue;
554		binary = 1;
555	}
556	if (binary)
557		hexdump(dp, datalen, screenwidth);
558	else
559		visdump(dp, datalen, screenwidth);
560}
561
562const char *signames[] = {
563	"NULL", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT",	/*  1 - 6  */
564	"EMT", "FPE", "KILL", "BUS", "SEGV", "SYS",		/*  7 - 12 */
565	"PIPE", "ALRM",  "TERM", "URG", "STOP", "TSTP",		/* 13 - 18 */
566	"CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU",		/* 19 - 24 */
567	"XFSZ", "VTALRM", "PROF", "WINCH", "29", "USR1",	/* 25 - 30 */
568	"USR2", NULL,						/* 31 - 32 */
569};
570
571void
572ktrpsig(struct ktr_psig *psig)
573{
574	(void)printf("SIG%s ", signames[psig->signo]);
575	if (psig->action == SIG_DFL)
576		(void)printf("SIG_DFL\n");
577	else {
578		(void)printf("caught handler=0x%lx mask=0x%x code=0x%x\n",
579		    (u_long)psig->action, psig->mask.__bits[0], psig->code);
580	}
581}
582
583void
584ktrcsw(struct ktr_csw *cs)
585{
586	(void)printf("%s %s\n", cs->out ? "stop" : "resume",
587		cs->user ? "user" : "kernel");
588}
589
590void
591ktruser(int len, unsigned char *p)
592{
593	(void)printf("%d ", len);
594	while (len--)
595		if (decimal)
596			(void)printf(" %d", *p++);
597		else
598			(void)printf(" %02x", *p++);
599	(void)printf("\n");
600
601}
602
603void
604usage(void)
605{
606	(void)fprintf(stderr,
607  "usage: kdump [-dEnlHRsT] [-f trfile] [-m maxdata] [-p pid] [-t [cnisuw]]\n");
608	exit(1);
609}
610