xref: /netbsd-src/usr.bin/kdump/kdump.c (revision 7cc2f76925f078d01ddc9e640a98f4ccfc9f8c3b)
1 /*	$NetBSD: kdump.c,v 1.32 2000/12/17 16:09:40 jdolecek Exp $	*/
2 
3 /*-
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1988, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)kdump.c	8.4 (Berkeley) 4/28/95";
45 #else
46 __RCSID("$NetBSD: kdump.c,v 1.32 2000/12/17 16:09:40 jdolecek Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #define _KERNEL
52 #include <sys/errno.h>
53 #undef _KERNEL
54 #include <sys/time.h>
55 #include <sys/uio.h>
56 #include <sys/ktrace.h>
57 #include <sys/ioctl.h>
58 #include <sys/ptrace.h>
59 
60 #include <err.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <vis.h>
67 
68 #include "ktrace.h"
69 #include "setemul.h"
70 
71 #include <sys/syscall.h>
72 
73 int timestamp, decimal, fancy = 1, tail, maxdata;
74 const char *tracefile = DEF_TRACEFILE;
75 struct ktr_header ktr_header;
76 
77 #define eqs(s1, s2)	(strcmp((s1), (s2)) == 0)
78 
79 static const char *ptrace_ops[] = {
80 	"PT_TRACE_ME",	"PT_READ_I",	"PT_READ_D",	"PT_READ_U",
81 	"PT_WRITE_I",	"PT_WRITE_D",	"PT_WRITE_U",	"PT_CONTINUE",
82 	"PT_KILL",	"PT_ATTACH",	"PT_DETACH",
83 };
84 
85 static const char *linux_ptrace_ops[] = {
86 	"PTRACE_TRACEME",
87 	"PTRACE_PEEKTEXT", "PTRACE_PEEKDATA", "PTRACE_PEEKUSER",
88 	"PTRACE_POKETEXT", "PTRACE_POKEDATA", "PTRACE_POKEUSER",
89 	"PTRACE_CONT", "PTRACE_KILL", "PTRACE_SINGLESTEP",
90 	NULL, NULL,
91 	"PTRACE_GETREGS", "PTRACE_SETREGS", "PTRACE_GETFPREGS",
92 	"PTRACE_SETFPREGS", "PTRACE_ATTACH", "PTRACE_DETACH",
93 	"PTRACE_SYSCALL",
94 };
95 
96 int	main __P((int, char **));
97 int	fread_tail __P((char *, int, int));
98 void	dumpheader __P((struct ktr_header *));
99 void	ioctldecode __P((u_long));
100 void	ktrsyscall __P((struct ktr_syscall *));
101 void	ktrsysret __P((struct ktr_sysret *));
102 void	ktrnamei __P((char *, int));
103 void	ktremul __P((char *, int, int));
104 void	ktrgenio __P((struct ktr_genio *, int));
105 void	ktrpsig __P((struct ktr_psig *));
106 void	ktrcsw __P((struct ktr_csw *));
107 void	ktruser __P((char *, int));
108 void	usage __P((void));
109 void	eprint __P((int));
110 char	*ioctlname __P((long));
111 
112 int
113 main(argc, argv)
114 	int argc;
115 	char *argv[];
116 {
117 	int ch, ktrlen, size;
118 	void *m;
119 	int trpoints = ALL_POINTS;
120 	const char *emul_name = "netbsd";
121 
122 	while ((ch = getopt(argc, argv, "e:f:dlm:nRTt:")) != -1)
123 		switch (ch) {
124 		case 'e':
125 			emul_name = strdup(optarg); /* it's safer to copy it */
126 			break;
127 		case 'f':
128 			tracefile = optarg;
129 			break;
130 		case 'd':
131 			decimal = 1;
132 			break;
133 		case 'l':
134 			tail = 1;
135 			break;
136 		case 'm':
137 			maxdata = atoi(optarg);
138 			break;
139 		case 'n':
140 			fancy = 0;
141 			break;
142 		case 'R':
143 			timestamp = 2;	/* relative timestamp */
144 			break;
145 		case 'T':
146 			timestamp = 1;
147 			break;
148 		case 't':
149 			trpoints = getpoints(optarg);
150 			if (trpoints < 0)
151 				errx(1, "unknown trace point in %s", optarg);
152 			break;
153 		default:
154 			usage();
155 		}
156 	argv += optind;
157 	argc -= optind;
158 
159 	if (argc > 1)
160 		usage();
161 
162 	setemul(emul_name, 0, 0);
163 
164 	m = malloc(size = 1024);
165 	if (m == NULL)
166 		errx(1, "malloc: %s", strerror(ENOMEM));
167 	if (!freopen(tracefile, "r", stdin))
168 		err(1, "%s", tracefile);
169 	while (fread_tail((char *)&ktr_header, sizeof(struct ktr_header), 1)) {
170 		if (trpoints & (1<<ktr_header.ktr_type))
171 			dumpheader(&ktr_header);
172 		if ((ktrlen = ktr_header.ktr_len) < 0)
173 			errx(1, "bogus length 0x%x", ktrlen);
174 		if (ktrlen > size) {
175 			while(ktrlen > size) size *= 2;
176 			m = (void *)realloc(m, size);
177 			if (m == NULL)
178 				errx(1, "realloc: %s", strerror(ENOMEM));
179 		}
180 		if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
181 			errx(1, "data too short");
182 		if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
183 			continue;
184 
185 		/* update context to match currently processed record */
186 		ectx_sanify(ktr_header.ktr_pid);
187 
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_EMUL:
208 			ktremul(m, ktrlen, size);
209 			break;
210 		case KTR_USER:
211 			ktruser(m, ktrlen);
212 			break;
213 		}
214 		if (tail)
215 			(void)fflush(stdout);
216 	}
217 	return (0);
218 }
219 
220 int
221 fread_tail(buf, size, num)
222 	char *buf;
223 	int num, size;
224 {
225 	int i;
226 
227 	while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
228 		(void)sleep(1);
229 		clearerr(stdin);
230 	}
231 	return (i);
232 }
233 
234 void
235 dumpheader(kth)
236 	struct ktr_header *kth;
237 {
238 	char unknown[64], *type;
239 	static struct timeval prevtime;
240 	struct timeval temp;
241 
242 	switch (kth->ktr_type) {
243 	case KTR_SYSCALL:
244 		type = "CALL";
245 		break;
246 	case KTR_SYSRET:
247 		type = "RET ";
248 		break;
249 	case KTR_NAMEI:
250 		type = "NAMI";
251 		break;
252 	case KTR_GENIO:
253 		type = "GIO ";
254 		break;
255 	case KTR_PSIG:
256 		type = "PSIG";
257 		break;
258 	case KTR_CSW:
259 		type = "CSW";
260 		break;
261 	case KTR_EMUL:
262 		type = "EMUL";
263 		break;
264 	case KTR_USER:
265 		type = "USER";
266 		break;
267 	default:
268 		(void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type);
269 		type = unknown;
270 	}
271 
272 	(void)printf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN, kth->ktr_comm);
273 	if (timestamp) {
274 		if (timestamp == 2) {
275 			timersub(&kth->ktr_time, &prevtime, &temp);
276 			prevtime = kth->ktr_time;
277 		} else
278 			temp = kth->ktr_time;
279 		(void)printf("%ld.%06ld ",
280 		    (long int)temp.tv_sec, (long int)temp.tv_usec);
281 	}
282 	(void)printf("%s  ", type);
283 }
284 
285 void
286 ioctldecode(cmd)
287 	u_long cmd;
288 {
289 	char dirbuf[4], *dir = dirbuf;
290 
291 	if (cmd & IOC_IN)
292 		*dir++ = 'W';
293 	if (cmd & IOC_OUT)
294 		*dir++ = 'R';
295 	*dir = '\0';
296 
297 	printf(decimal ? ",_IO%s('%c',%ld" : ",_IO%s('%c',%#lx",
298 	    dirbuf, (int) ((cmd >> 8) & 0xff), cmd & 0xff);
299 	if ((cmd & IOC_VOID) == 0)
300 		printf(decimal ? ",%ld)" : ",%#lx)", (cmd >> 16) & 0xff);
301 	else
302 		printf(")");
303 }
304 
305 void
306 ktrsyscall(ktr)
307 	struct ktr_syscall *ktr;
308 {
309 	int argsize = ktr->ktr_argsize;
310 	register_t *ap;
311 
312 	if (ktr->ktr_code >= current->nsysnames || ktr->ktr_code < 0)
313 		(void)printf("[%d]", ktr->ktr_code);
314 	else
315 		(void)printf("%s", current->sysnames[ktr->ktr_code]);
316 	ap = (register_t *)((char *)ktr + sizeof(struct ktr_syscall));
317 	if (argsize) {
318 		char c = '(';
319 		if (fancy) {
320 			if (ktr->ktr_code == SYS_ioctl) {
321 				char *cp;
322 				if (decimal)
323 					(void)printf("(%ld", (long)*ap);
324 				else
325 					(void)printf("(%#lx", (long)*ap);
326 				ap++;
327 				argsize -= sizeof(register_t);
328 				if ((cp = ioctlname(*ap)) != NULL)
329 					(void)printf(",%s", cp);
330 				else
331 					ioctldecode(*ap);
332 				c = ',';
333 				ap++;
334 				argsize -= sizeof(register_t);
335 			} else if (ktr->ktr_code == SYS_ptrace) {
336 				if (strcmp(current->name, "linux") == 0) {
337 				  if (*ap >= 0 && *ap <=
338 				    sizeof(linux_ptrace_ops) / sizeof(linux_ptrace_ops[0]))
339 					(void)printf("(%s", linux_ptrace_ops[*ap]);
340 				  else
341 					(void)printf("(%ld", (long)*ap);
342 				} else {
343 				  if (*ap >= 0 && *ap <=
344 				    sizeof(ptrace_ops) / sizeof(ptrace_ops[0]))
345 					(void)printf("(%s", ptrace_ops[*ap]);
346 				  else
347 					(void)printf("(%ld", (long)*ap);
348 				}
349 				c = ',';
350 				ap++;
351 				argsize -= sizeof(register_t);
352 			}
353 		}
354 		while (argsize) {
355 			if (decimal)
356 				(void)printf("%c%ld", c, (long)*ap);
357 			else
358 				(void)printf("%c%#lx", c, (long)*ap);
359 			c = ',';
360 			ap++;
361 			argsize -= sizeof(register_t);
362 		}
363 		(void)putchar(')');
364 	}
365 	(void)putchar('\n');
366 }
367 
368 void
369 ktrsysret(ktr)
370 	struct ktr_sysret *ktr;
371 {
372 	register_t ret = ktr->ktr_retval;
373 	int error = ktr->ktr_error;
374 	int code = ktr->ktr_code;
375 
376 	if (code >= current->nsysnames || code < 0)
377 		(void)printf("[%d] ", code);
378 	else
379 		(void)printf("%s ", current->sysnames[code]);
380 
381 	switch (error) {
382 	case 0:
383 		if (fancy) {
384 			(void)printf("%ld", (long)ret);
385 			if (ret < 0 || ret > 9)
386 				(void)printf("/%#lx", (long)ret);
387 		} else {
388 			if (decimal)
389 				(void)printf("%ld", (long)ret);
390 			else
391 				(void)printf("%#lx", (long)ret);
392 		}
393 		break;
394 
395 	default:
396 		eprint(error);
397 		break;
398 	}
399 	(void)putchar('\n');
400 
401 }
402 
403 /*
404  * We print the original emulation's error numerically, but we
405  * translate it to netbsd to print it symbolically.
406  */
407 void
408 eprint(e)
409 	int e;
410 {
411 	int i = e;
412 
413 	if (current->errno) {
414 
415 		/* No remapping for ERESTART and EJUSTRETURN */
416 		/* Kludge for linux that has negative error numbers */
417 		if (current->errno[2] > 0 && e < 0)
418 			goto normal;
419 
420 		for (i = 0; i < current->nerrno; i++)
421 			if (e == current->errno[i])
422 				break;
423 
424 		if (i == current->nerrno) {
425 			printf("-1 unknown errno %d", e);
426 			return;
427 		}
428 	}
429 
430 normal:
431 	switch (i) {
432 	case ERESTART:
433 		(void)printf("RESTART");
434 		break;
435 
436 	case EJUSTRETURN:
437 		(void)printf("JUSTRETURN");
438 		break;
439 
440 	default:
441 		(void)printf("-1 errno %d", e);
442 		if (fancy)
443 			(void)printf(" %s", strerror(i));
444 	}
445 }
446 
447 void
448 ktrnamei(cp, len)
449 	char *cp;
450 	int len;
451 {
452 
453 	(void)printf("\"%.*s\"\n", len, cp);
454 }
455 
456 void
457 ktremul(name, len, bufsize)
458 	char *name;
459 	int len, bufsize;
460 {
461 	if (len >= bufsize)
462 		len = bufsize - 1;
463 
464 	name[len] = '\0';
465 	setemul(name, ktr_header.ktr_pid, 1);
466 
467 	(void)printf("\"%s\"\n", name);
468 }
469 
470 void
471 ktrgenio(ktr, len)
472 	struct ktr_genio *ktr;
473 	int len;
474 {
475 	int datalen = len - sizeof (struct ktr_genio);
476 	char *dp = (char *)ktr + sizeof (struct ktr_genio);
477 	char *cp;
478 	int col = 0;
479 	int width;
480 	char visbuf[5];
481 	static int screenwidth = 0;
482 
483 	if (screenwidth == 0) {
484 		struct winsize ws;
485 
486 		if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
487 		    ws.ws_col > 8)
488 			screenwidth = ws.ws_col;
489 		else
490 			screenwidth = 80;
491 	}
492 	printf("fd %d %s %d bytes\n", ktr->ktr_fd,
493 		ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen);
494 	if (maxdata && datalen > maxdata)
495 		datalen = maxdata;
496 	(void)printf("       \"");
497 	col = 8;
498 	for (; datalen > 0; datalen--, dp++) {
499 		(void) vis(visbuf, *dp, VIS_CSTYLE, datalen>1?*(dp+1):0);
500 		cp = visbuf;
501 		/*
502 		 * Keep track of printables and
503 		 * space chars (like fold(1)).
504 		 */
505 		if (col == 0) {
506 			(void)putchar('\t');
507 			col = 8;
508 		}
509 		switch(*cp) {
510 		case '\n':
511 			col = 0;
512 			(void)putchar('\n');
513 			continue;
514 		case '\t':
515 			width = 8 - (col&07);
516 			break;
517 		default:
518 			width = strlen(cp);
519 		}
520 		if (col + width > (screenwidth-2)) {
521 			(void)printf("\\\n\t");
522 			col = 8;
523 		}
524 		col += width;
525 		do {
526 			(void)putchar(*cp++);
527 		} while (*cp);
528 	}
529 	if (col == 0)
530 		(void)printf("       ");
531 	(void)printf("\"\n");
532 }
533 
534 void
535 ktrpsig(psig)
536 	struct ktr_psig *psig;
537 {
538 	int signo, first;
539 
540 	(void)printf("SIG%s ", sys_signame[psig->signo]);
541 	if (psig->action == SIG_DFL)
542 		(void)printf("SIG_DFL\n");
543 	else {
544 		(void)printf("caught handler=0x%lx mask=(",
545 		    (u_long)psig->action);
546 		first = 1;
547 		for (signo = 1; signo < NSIG; signo++) {
548 			if (sigismember(&psig->mask, signo)) {
549 				if (first)
550 					first = 0;
551 				else
552 					(void)printf(",");
553 				(void)printf("%d", signo);
554 			}
555 		}
556 		(void)printf(") code=0x%x\n", psig->code);
557 	}
558 }
559 
560 void
561 ktrcsw(cs)
562 	struct ktr_csw *cs;
563 {
564 
565 	(void)printf("%s %s\n", cs->out ? "stop" : "resume",
566 	    cs->user ? "user" : "kernel");
567 }
568 
569 void
570 ktruser(name, len)
571 	char *name;
572 	int len;
573 {
574 	int i;
575 	printf("\"%d, ", len);
576 	for(i=0; i < len; i++)
577 		printf("%x", name[i]);
578 	printf("\"\n");
579 }
580 
581 void
582 usage()
583 {
584 
585 	(void)fprintf(stderr,
586 "usage: kdump [-dnlRT] [-e emulation] [-f trfile] [-m maxdata] [-t [cnis]]\n");
587 	exit(1);
588 }
589