Home | History | Annotate | Line # | Download | only in crash
crash.c revision 1.11.14.1
      1 /*	$NetBSD: crash.c,v 1.11.14.1 2020/04/08 14:09:19 martin Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Andrew Doran.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: crash.c,v 1.11.14.1 2020/04/08 14:09:19 martin Exp $");
     35 #endif /* not lint */
     36 
     37 #include <ddb/ddb.h>
     38 
     39 #include <sys/fcntl.h>
     40 #include <sys/mman.h>
     41 #include <sys/stat.h>
     42 #include <sys/ioctl.h>
     43 
     44 #include <machine/frame.h>
     45 
     46 #include <stdarg.h>
     47 #include <stdlib.h>
     48 #include <unistd.h>
     49 #include <getopt.h>
     50 #include <errno.h>
     51 #include <histedit.h>
     52 #include <paths.h>
     53 #include <kvm.h>
     54 #include <err.h>
     55 #include <ctype.h>
     56 #include <util.h>
     57 
     58 #include "extern.h"
     59 
     60 #define	MAXSTAB	(16 * 1024 * 1024)
     61 
     62 db_regs_t	ddb_regs;
     63 
     64 static kvm_t		*kd;
     65 static History		*hist;
     66 static HistEvent	he;
     67 static EditLine		*elptr;
     68 static char		imgrelease[16];
     69 static FILE		*ofp;
     70 
     71 static struct nlist nl[] = {
     72 #define	X_OSRELEASE	0
     73 	{ .n_name = "_osrelease" },
     74 #define	X_PANICSTR	1
     75 	{ .n_name = "_panicstr" },
     76 #ifdef LOCKDEBUG
     77 #define	X_LOCKDEBUG	2
     78 	{ .n_name = "ld_all" },
     79 #endif
     80 	{ .n_name = NULL },
     81 };
     82 
     83 #ifdef LOCKDEBUG
     84 struct lockdebug;
     85 TAILQ_HEAD(, lockdebug) ld_all;
     86 #else
     87 void lockdebug_lock_print(void);
     88 void lockdebug_lock_print(void) {
     89 	warnx("No lockdebug support compiled in");
     90 }
     91 #endif
     92 
     93 
     94 static void
     95 cleanup(void)
     96 {
     97 	if (ofp != stdout) {
     98 		(void)fflush(ofp);
     99 		(void)pclose(ofp);
    100 		ofp = stdout;
    101 	}
    102 	el_end(elptr);
    103 	history_end(hist);
    104 }
    105 
    106 void
    107 db_vprintf(const char *fmt, va_list ap)
    108 {
    109 	char buf[1024];
    110 	int b, c;
    111 
    112 	c = vsnprintf(buf, sizeof(buf), fmt, ap);
    113 	for (b = 0; b < c; b++) {
    114 		db_putchar(buf[b]);
    115 	}
    116 }
    117 
    118 void
    119 db_printf(const char *fmt, ...)
    120 {
    121 	va_list ap;
    122 
    123 	va_start(ap, fmt);
    124 	db_vprintf(fmt, ap);
    125 	va_end(ap);
    126 }
    127 
    128 void
    129 db_write_bytes(db_addr_t addr, size_t size, const char *str)
    130 {
    131 
    132 	if ((size_t)kvm_write(kd, addr, str, size) != size) {
    133 		warnx("kvm_write(%p, %zd): %s", (void *)addr, size,
    134 		    kvm_geterr(kd));
    135 		longjmp(db_recover);
    136 	}
    137 }
    138 
    139 void
    140 db_read_bytes(db_addr_t addr, size_t size, char *str)
    141 {
    142 
    143 	if ((size_t)kvm_read(kd, addr, str, size) != size) {
    144 		warnx("kvm_read(%p, %zd): %s", (void *)addr, size,
    145 		    kvm_geterr(kd));
    146 		longjmp(db_recover);
    147 	}
    148 }
    149 
    150 void *
    151 db_alloc(size_t sz)
    152 {
    153 
    154 	return emalloc(sz);
    155 }
    156 
    157 void *
    158 db_zalloc(size_t sz)
    159 {
    160 
    161 	return ecalloc(1, sz);
    162 }
    163 
    164 void
    165 db_free(void *p, size_t sz)
    166 {
    167 
    168 	free(p);
    169 }
    170 
    171 static void
    172 punt(void)
    173 {
    174 
    175 	db_printf("This command can only be used in-kernel.\n");
    176 }
    177 
    178 void
    179 db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    180     const char *modif)
    181 {
    182 
    183 	punt();
    184 }
    185 
    186 void
    187 db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    188     const char *modif)
    189 {
    190 
    191 	db_cmd_loop_done = true;
    192 }
    193 
    194 void
    195 db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    196     const char *modif)
    197 {
    198 
    199 	punt();
    200 }
    201 
    202 void
    203 db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    204     const char *modif)
    205 {
    206 
    207 	punt();
    208 }
    209 
    210 
    211 void
    212 db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    213     const char *modif)
    214 {
    215 
    216 	punt();
    217 }
    218 
    219 
    220 void
    221 db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    222     const char *modif)
    223 {
    224 
    225 	punt();
    226 }
    227 
    228 
    229 void
    230 db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    231     const char *modif)
    232 {
    233 
    234 	punt();
    235 }
    236 
    237 
    238 void
    239 db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    240     const char *modif)
    241 {
    242 
    243 	punt();
    244 }
    245 
    246 int
    247 db_readline(char *lstart, int lsize)
    248 {
    249 	const char *el;
    250 	char *pcmd;
    251 	int cnt;
    252 
    253 	db_force_whitespace();
    254 
    255 	/* Close any open pipe. */
    256 	if (ofp != stdout) {
    257 		(void)fflush(ofp);
    258 		(void)pclose(ofp);
    259 		ofp = stdout;
    260 	}
    261 
    262 	/* Read next command. */
    263 	el = el_gets(elptr, &cnt);
    264 	if (el == NULL) {	/* EOF */
    265 		exit(EXIT_SUCCESS);
    266 	}
    267 
    268 	/* Save to history, and copy to caller's buffer. */
    269 	history(hist, &he, H_ENTER, el);
    270 	strlcpy(lstart, el, lsize);
    271 	if (cnt >= lsize) {
    272 		cnt = lsize - 1;
    273 	}
    274 	lstart[cnt] = '\0';
    275 	if (cnt > 0 && lstart[cnt - 1] == '\n') {
    276 		lstart[cnt - 1] = '\0';
    277 	}
    278 
    279 	/* Need to open a pipe? If not, return now. */
    280 	pcmd = strchr(lstart, '|');
    281 	if (pcmd == NULL) {
    282 		return strlen(lstart);
    283 	}
    284 
    285 	/* Open a pipe to specified command, redirect output. */
    286 	assert(ofp == stdout);
    287 	for (*pcmd++ = '\0'; isspace((unsigned char)*pcmd); pcmd++) {
    288 		/* nothing */
    289 	}
    290 	errno = 0;
    291 	ofp = popen(pcmd, "w");
    292 	if (ofp == NULL) {
    293 		warn("opening pipe for command `%s'", pcmd);
    294 		*lstart = '\0';
    295 	}
    296 	return strlen(lstart);
    297 }
    298 
    299 void
    300 db_check_interrupt(void)
    301 {
    302 
    303 }
    304 
    305 int
    306 cngetc(void)
    307 {
    308 
    309 	fprintf(stderr, "cngetc\n");
    310 	abort();
    311 }
    312 
    313 void
    314 cnputc(int c)
    315 {
    316 
    317 	putc(c, ofp);
    318 }
    319 
    320 __dead static void
    321 usage(void)
    322 {
    323 
    324 	fprintf(stderr,
    325 	    "usage: %s [-w] [-M core] [-N kernel]\n\n"
    326 	    "-M core\tspecify memory file (default /dev/mem)\n"
    327 	    "-N kernel\tspecify name list file (default /dev/ksyms)\n",
    328 	    getprogname());
    329 	exit(EXIT_FAILURE);
    330 }
    331 
    332 static const char *
    333 prompt(void)
    334 {
    335 
    336 	return "crash> ";
    337 }
    338 
    339 int
    340 main(int argc, char **argv)
    341 {
    342 	const char *nlistf, *memf;
    343 	uintptr_t panicstr;
    344 	struct winsize ws;
    345 	struct stat sb;
    346 	size_t sz;
    347 	void *elf;
    348 	int fd, ch, flags;
    349 	char c;
    350 
    351 	nlistf = _PATH_KSYMS;
    352 	memf = _PATH_MEM;
    353 	ofp = stdout;
    354 	flags = O_RDONLY;
    355 
    356 	setprogname(argv[0]);
    357 
    358 	/*
    359 	 * Parse options.
    360 	 */
    361 	while ((ch = getopt(argc, argv, "M:N:w")) != -1) {
    362 		switch (ch) {
    363 		case 'M':
    364 			memf = optarg;
    365 			break;
    366 		case 'N':
    367 			nlistf = optarg;
    368 			break;
    369 		case 'w':
    370 			flags = O_RDWR;
    371 			break;
    372 		default:
    373 			usage();
    374 		}
    375 	}
    376 	argc -= optind;
    377 	argv += optind;
    378 
    379 	/*
    380 	 * Print a list of images, and allow user to select.
    381 	 */
    382 	/* XXX */
    383 
    384 	/*
    385 	 * Open the images (crash dump and symbol table).
    386 	 */
    387 	kd = kvm_open(nlistf, memf, NULL, flags, getprogname());
    388 	if (kd == NULL) {
    389 		return EXIT_FAILURE;
    390 	}
    391 	fd = open(nlistf, O_RDONLY);
    392 	if (fd == -1)  {
    393 		err(EXIT_FAILURE, "open `%s'", nlistf);
    394 	}
    395 	if (fstat(fd, &sb) == -1) {
    396 		err(EXIT_FAILURE, "stat `%s'", nlistf);
    397 	}
    398 	if ((sb.st_mode & S_IFMT) != S_IFREG) {	/* XXX ksyms */
    399 		sz = MAXSTAB;
    400 		elf = malloc(sz);
    401 		if (elf == NULL) {
    402 			err(EXIT_FAILURE, "malloc(%zu)", sz);
    403 		}
    404 		sz = read(fd, elf, sz);
    405 		if ((ssize_t)sz == -1) {
    406 			err(EXIT_FAILURE, "read `%s'", nlistf);
    407 		}
    408 		if (sz == MAXSTAB) {
    409 			errx(EXIT_FAILURE, "symbol table > %d bytes", MAXSTAB);
    410 		}
    411 	} else {
    412 		sz = sb.st_size;
    413 		elf = mmap(NULL, sz, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
    414 		if (elf == MAP_FAILED) {
    415 			err(EXIT_FAILURE, "mmap `%s'", nlistf);
    416 		}
    417 	}
    418 
    419 	/*
    420 	 * Print kernel & crash versions.
    421 	 */
    422 	if (kvm_nlist(kd, nl) == -1) {
    423 		errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd));
    424 	}
    425 	if ((size_t)kvm_read(kd, nl[X_OSRELEASE].n_value, imgrelease,
    426 	    sizeof(imgrelease)) != sizeof(imgrelease)) {
    427 		errx(EXIT_FAILURE, "cannot read osrelease: %s",
    428 		    kvm_geterr(kd));
    429 	}
    430 	printf("Crash version %s, image version %s.\n", osrelease, imgrelease);
    431 	if (strcmp(osrelease, imgrelease) != 0) {
    432 		printf("WARNING: versions differ, you may not be able to "
    433 		    "examine this image.\n");
    434 	}
    435 #ifdef LOCKDEBUG
    436 	if ((size_t)kvm_read(kd, nl[X_LOCKDEBUG].n_value, &ld_all,
    437 	    sizeof(ld_all)) != sizeof(ld_all))
    438 		printf("Kernel compiled without options LOCKDEBUG.\n");
    439 #endif
    440 
    441 	/*
    442 	 * Print the panic string, if any.
    443 	 */
    444 	if ((size_t)kvm_read(kd, nl[X_PANICSTR].n_value, &panicstr,
    445 	    sizeof(panicstr)) != sizeof(panicstr)) {
    446 		errx(EXIT_FAILURE, "cannot read panicstr: %s",
    447 		    kvm_geterr(kd));
    448 	}
    449 	if (strcmp(memf, _PATH_MEM) == 0) {
    450 		printf("Output from a running system is unreliable.\n");
    451 	} else if (panicstr == 0) {
    452 		printf("System does not appear to have panicked.\n");
    453 	} else {
    454 		printf("System panicked: ");
    455 		for (;;) {
    456 			if ((size_t)kvm_read(kd, panicstr, &c, sizeof(c)) !=
    457 			    sizeof(c)) {
    458 				errx(EXIT_FAILURE, "cannot read *panicstr: %s",
    459 				    kvm_geterr(kd));
    460 			}
    461 			if (c == '\0') {
    462 				break;
    463 			}
    464 			putchar(c);
    465 			panicstr++;
    466 		}
    467 		putchar('\n');
    468 	}
    469 
    470 	/*
    471 	 * Initialize line editing.
    472 	 */
    473 	hist = history_init();
    474 	history(hist, &he, H_SETSIZE, 100);
    475 	elptr = el_init(getprogname(), stdin, stdout, stderr);
    476 	el_set(elptr, EL_EDITOR, "emacs");
    477 	el_set(elptr, EL_SIGNAL, 1);
    478 	el_set(elptr, EL_HIST, history, hist);
    479 	el_set(elptr, EL_PROMPT, prompt);
    480 	el_source(elptr, NULL);
    481 
    482 	atexit(cleanup);
    483 
    484 	/*
    485 	 * Initialize ddb.
    486 	 */
    487 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
    488 		db_max_width = ws.ws_col;
    489 	}
    490 	db_mach_init(kd);
    491 	ddb_init(sz, elf, (char *)elf + sz);
    492 
    493 	/*
    494 	 * Debug it!
    495 	 */
    496 	db_command_loop();
    497 
    498 	/*
    499 	 * Finish.
    500 	 */
    501 	return EXIT_SUCCESS;
    502 }
    503