xref: /netbsd-src/usr.sbin/crash/crash.c (revision 1a32d96e8566669af03015336cbab3a6765f1d46)
1*1a32d96eSskrll /*	$NetBSD: crash.c,v 1.17 2024/11/21 07:20:10 skrll Exp $	*/
2101a9782Sad 
3101a9782Sad /*-
4101a9782Sad  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5101a9782Sad  * All rights reserved.
6101a9782Sad  *
7101a9782Sad  * This code is derived from software contributed to The NetBSD Foundation
8101a9782Sad  * by Andrew Doran.
9101a9782Sad  *
10101a9782Sad  * Redistribution and use in source and binary forms, with or without
11101a9782Sad  * modification, are permitted provided that the following conditions
12101a9782Sad  * are met:
13101a9782Sad  * 1. Redistributions of source code must retain the above copyright
14101a9782Sad  *    notice, this list of conditions and the following disclaimer.
15101a9782Sad  * 2. Redistributions in binary form must reproduce the above copyright
16101a9782Sad  *    notice, this list of conditions and the following disclaimer in the
17101a9782Sad  *    documentation and/or other materials provided with the distribution.
18101a9782Sad  *
19101a9782Sad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20101a9782Sad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21101a9782Sad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22101a9782Sad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23101a9782Sad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24101a9782Sad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25101a9782Sad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26101a9782Sad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27101a9782Sad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28101a9782Sad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29101a9782Sad  * POSSIBILITY OF SUCH DAMAGE.
30101a9782Sad  */
31101a9782Sad 
32101a9782Sad #include <sys/cdefs.h>
33101a9782Sad #ifndef lint
34*1a32d96eSskrll __RCSID("$NetBSD: crash.c,v 1.17 2024/11/21 07:20:10 skrll Exp $");
35101a9782Sad #endif /* not lint */
36101a9782Sad 
37f55073dfSriastradh #include <sys/types.h>
38101a9782Sad 
39101a9782Sad #include <sys/fcntl.h>
40101a9782Sad #include <sys/mman.h>
4122e2f968Schristos #include <sys/stat.h>
42101a9782Sad #include <sys/ioctl.h>
43101a9782Sad 
444d62e245Smrg #ifndef __mips__
45101a9782Sad #include <machine/frame.h>
464d62e245Smrg #endif
47101a9782Sad 
48f55073dfSriastradh #include <ddb/ddb.h>
49f55073dfSriastradh 
50101a9782Sad #include <stdarg.h>
51101a9782Sad #include <stdlib.h>
52101a9782Sad #include <unistd.h>
53101a9782Sad #include <getopt.h>
54101a9782Sad #include <errno.h>
55101a9782Sad #include <histedit.h>
56101a9782Sad #include <paths.h>
57101a9782Sad #include <kvm.h>
58101a9782Sad #include <err.h>
59101a9782Sad #include <ctype.h>
60bee149d1Schristos #include <util.h>
61101a9782Sad 
62101a9782Sad #include "extern.h"
63101a9782Sad 
64101a9782Sad #define	MAXSTAB	(16 * 1024 * 1024)
65101a9782Sad 
66101a9782Sad db_regs_t	ddb_regs;
67bee149d1Schristos 
68bee149d1Schristos static kvm_t		*kd;
69bee149d1Schristos static History		*hist;
70bee149d1Schristos static HistEvent	he;
71bee149d1Schristos static EditLine		*elptr;
72bee149d1Schristos static char		imgrelease[16];
73bee149d1Schristos static FILE		*ofp;
74101a9782Sad 
75101a9782Sad static struct nlist nl[] = {
76101a9782Sad #define	X_OSRELEASE	0
77101a9782Sad 	{ .n_name = "_osrelease" },
78101a9782Sad #define	X_PANICSTR	1
79101a9782Sad 	{ .n_name = "_panicstr" },
80fbd525a3Schristos #ifdef LOCKDEBUG
81fbd525a3Schristos #define	X_LOCKDEBUG	2
82fbd525a3Schristos 	{ .n_name = "ld_all" },
83fbd525a3Schristos #endif
84101a9782Sad 	{ .n_name = NULL },
85101a9782Sad };
86101a9782Sad 
87fbd525a3Schristos #ifdef LOCKDEBUG
88fbd525a3Schristos struct lockdebug;
89fbd525a3Schristos TAILQ_HEAD(, lockdebug) ld_all;
90fbd525a3Schristos #else
91fbd525a3Schristos void lockdebug_lock_print(void);
92fbd525a3Schristos void lockdebug_lock_print(void) {
93fbd525a3Schristos 	warnx("No lockdebug support compiled in");
94fbd525a3Schristos }
95fbd525a3Schristos #endif
96fbd525a3Schristos 
97bee149d1Schristos static void
98bee149d1Schristos cleanup(void)
99bee149d1Schristos {
100bee149d1Schristos 	if (ofp != stdout) {
101bee149d1Schristos 		(void)fflush(ofp);
102bee149d1Schristos 		(void)pclose(ofp);
103bee149d1Schristos 		ofp = stdout;
104bee149d1Schristos 	}
105bee149d1Schristos 	el_end(elptr);
106bee149d1Schristos 	history_end(hist);
107bee149d1Schristos }
108bee149d1Schristos 
109101a9782Sad void
110101a9782Sad db_vprintf(const char *fmt, va_list ap)
111101a9782Sad {
112101a9782Sad 	char buf[1024];
113101a9782Sad 	int b, c;
114101a9782Sad 
115101a9782Sad 	c = vsnprintf(buf, sizeof(buf), fmt, ap);
116101a9782Sad 	for (b = 0; b < c; b++) {
117101a9782Sad 		db_putchar(buf[b]);
118101a9782Sad 	}
119101a9782Sad }
120101a9782Sad 
121101a9782Sad void
122101a9782Sad db_printf(const char *fmt, ...)
123101a9782Sad {
124101a9782Sad 	va_list ap;
125101a9782Sad 
126101a9782Sad 	va_start(ap, fmt);
127101a9782Sad 	db_vprintf(fmt, ap);
128101a9782Sad 	va_end(ap);
129101a9782Sad }
130101a9782Sad 
131101a9782Sad void
132101a9782Sad db_write_bytes(db_addr_t addr, size_t size, const char *str)
133101a9782Sad {
134101a9782Sad 
1352269fc7fSlukem 	if ((size_t)kvm_write(kd, addr, str, size) != size) {
136101a9782Sad 		warnx("kvm_write(%p, %zd): %s", (void *)addr, size,
137101a9782Sad 		    kvm_geterr(kd));
138101a9782Sad 		longjmp(db_recover);
139101a9782Sad 	}
140101a9782Sad }
141101a9782Sad 
142101a9782Sad void
143101a9782Sad db_read_bytes(db_addr_t addr, size_t size, char *str)
144101a9782Sad {
145101a9782Sad 
1462269fc7fSlukem 	if ((size_t)kvm_read(kd, addr, str, size) != size) {
147101a9782Sad 		warnx("kvm_read(%p, %zd): %s", (void *)addr, size,
148101a9782Sad 		    kvm_geterr(kd));
149101a9782Sad 		longjmp(db_recover);
150101a9782Sad 	}
151101a9782Sad }
152101a9782Sad 
153101a9782Sad void *
154101a9782Sad db_alloc(size_t sz)
155101a9782Sad {
156101a9782Sad 
157bee149d1Schristos 	return emalloc(sz);
158101a9782Sad }
159101a9782Sad 
160101a9782Sad void *
161101a9782Sad db_zalloc(size_t sz)
162101a9782Sad {
163101a9782Sad 
164bee149d1Schristos 	return ecalloc(1, sz);
165101a9782Sad }
166101a9782Sad 
167101a9782Sad void
168101a9782Sad db_free(void *p, size_t sz)
169101a9782Sad {
170101a9782Sad 
171101a9782Sad 	free(p);
172101a9782Sad }
173101a9782Sad 
174101a9782Sad static void
175101a9782Sad punt(void)
176101a9782Sad {
177101a9782Sad 
178101a9782Sad 	db_printf("This command can only be used in-kernel.\n");
179101a9782Sad }
180101a9782Sad 
181101a9782Sad void
182101a9782Sad db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
183101a9782Sad     const char *modif)
184101a9782Sad {
185101a9782Sad 
186101a9782Sad 	punt();
187101a9782Sad }
188101a9782Sad 
189101a9782Sad void
190101a9782Sad db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
191101a9782Sad     const char *modif)
192101a9782Sad {
193101a9782Sad 
194101a9782Sad 	db_cmd_loop_done = true;
195101a9782Sad }
196101a9782Sad 
197101a9782Sad void
198101a9782Sad db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
199101a9782Sad     const char *modif)
200101a9782Sad {
201101a9782Sad 
202101a9782Sad 	punt();
203101a9782Sad }
204101a9782Sad 
205101a9782Sad void
206101a9782Sad db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
207101a9782Sad     const char *modif)
208101a9782Sad {
209101a9782Sad 
210101a9782Sad 	punt();
211101a9782Sad }
212101a9782Sad 
213101a9782Sad 
214101a9782Sad void
215101a9782Sad db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
216101a9782Sad     const char *modif)
217101a9782Sad {
218101a9782Sad 
219101a9782Sad 	punt();
220101a9782Sad }
221101a9782Sad 
222101a9782Sad 
223101a9782Sad void
224101a9782Sad db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
225101a9782Sad     const char *modif)
226101a9782Sad {
227101a9782Sad 
228101a9782Sad 	punt();
229101a9782Sad }
230101a9782Sad 
231101a9782Sad 
232101a9782Sad void
233101a9782Sad db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
234101a9782Sad     const char *modif)
235101a9782Sad {
236101a9782Sad 
237101a9782Sad 	punt();
238101a9782Sad }
239101a9782Sad 
240101a9782Sad 
241101a9782Sad void
242101a9782Sad db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
243101a9782Sad     const char *modif)
244101a9782Sad {
245101a9782Sad 
246101a9782Sad 	punt();
247101a9782Sad }
248101a9782Sad 
249101a9782Sad int
250101a9782Sad db_readline(char *lstart, int lsize)
251101a9782Sad {
252101a9782Sad 	const char *el;
253101a9782Sad 	char *pcmd;
254101a9782Sad 	int cnt;
255101a9782Sad 
256101a9782Sad 	db_force_whitespace();
257101a9782Sad 
258101a9782Sad 	/* Close any open pipe. */
259101a9782Sad 	if (ofp != stdout) {
260101a9782Sad 		(void)fflush(ofp);
261101a9782Sad 		(void)pclose(ofp);
262101a9782Sad 		ofp = stdout;
263101a9782Sad 	}
264101a9782Sad 
265101a9782Sad 	/* Read next command. */
266101a9782Sad 	el = el_gets(elptr, &cnt);
267bee149d1Schristos 	if (el == NULL) {	/* EOF */
268bee149d1Schristos 		exit(EXIT_SUCCESS);
269101a9782Sad 	}
270101a9782Sad 
271101a9782Sad 	/* Save to history, and copy to caller's buffer. */
272101a9782Sad 	history(hist, &he, H_ENTER, el);
273101a9782Sad 	strlcpy(lstart, el, lsize);
274101a9782Sad 	if (cnt >= lsize) {
275101a9782Sad 		cnt = lsize - 1;
276101a9782Sad 	}
277101a9782Sad 	lstart[cnt] = '\0';
278101a9782Sad 	if (cnt > 0 && lstart[cnt - 1] == '\n') {
279101a9782Sad 		lstart[cnt - 1] = '\0';
280101a9782Sad 	}
281101a9782Sad 
282101a9782Sad 	/* Need to open a pipe? If not, return now. */
283101a9782Sad 	pcmd = strchr(lstart, '|');
284101a9782Sad 	if (pcmd == NULL) {
285101a9782Sad 		return strlen(lstart);
286101a9782Sad 	}
287101a9782Sad 
288101a9782Sad 	/* Open a pipe to specified command, redirect output. */
289101a9782Sad 	assert(ofp == stdout);
290bee149d1Schristos 	for (*pcmd++ = '\0'; isspace((unsigned char)*pcmd); pcmd++) {
291101a9782Sad 		/* nothing */
292101a9782Sad 	}
293101a9782Sad 	errno = 0;
294101a9782Sad 	ofp = popen(pcmd, "w");
295101a9782Sad 	if (ofp == NULL) {
296101a9782Sad 		warn("opening pipe for command `%s'", pcmd);
297101a9782Sad 		*lstart = '\0';
298101a9782Sad 	}
299101a9782Sad 	return strlen(lstart);
300101a9782Sad }
301101a9782Sad 
302101a9782Sad void
303101a9782Sad db_check_interrupt(void)
304101a9782Sad {
305101a9782Sad 
306101a9782Sad }
307101a9782Sad 
308101a9782Sad int
309101a9782Sad cngetc(void)
310101a9782Sad {
311832b6478Smlelstv 	char ch;
312101a9782Sad 
313832b6478Smlelstv 	if (el_getc(elptr, &ch) <= 0)
314832b6478Smlelstv 		return 0;
315832b6478Smlelstv 	return (unsigned char)ch;
316101a9782Sad }
317101a9782Sad 
318101a9782Sad void
319101a9782Sad cnputc(int c)
320101a9782Sad {
321101a9782Sad 
322101a9782Sad 	putc(c, ofp);
323101a9782Sad }
324101a9782Sad 
325bec77c5fSjoerg __dead static void
326101a9782Sad usage(void)
327101a9782Sad {
328101a9782Sad 
329101a9782Sad 	fprintf(stderr,
33004f07660Swiz 	    "usage: %s [-w] [-M core] [-N kernel]\n\n"
33134564de1Schristos 	    "-M core\tspecify memory file (default /dev/mem)\n"
33204f07660Swiz 	    "-N kernel\tspecify name list file (default /dev/ksyms)\n",
333101a9782Sad 	    getprogname());
334101a9782Sad 	exit(EXIT_FAILURE);
335101a9782Sad }
336101a9782Sad 
337101a9782Sad static const char *
338101a9782Sad prompt(void)
339101a9782Sad {
340101a9782Sad 
341101a9782Sad 	return "crash> ";
342101a9782Sad }
343101a9782Sad 
344101a9782Sad int
345101a9782Sad main(int argc, char **argv)
346101a9782Sad {
347101a9782Sad 	const char *nlistf, *memf;
348101a9782Sad 	uintptr_t panicstr;
349101a9782Sad 	struct winsize ws;
350101a9782Sad 	struct stat sb;
351101a9782Sad 	size_t sz;
352101a9782Sad 	void *elf;
353f7bf36a3Schristos 	int fd, ch, flags;
354101a9782Sad 	char c;
355101a9782Sad 
356101a9782Sad 	nlistf = _PATH_KSYMS;
357101a9782Sad 	memf = _PATH_MEM;
358101a9782Sad 	ofp = stdout;
359f7bf36a3Schristos 	flags = O_RDONLY;
360101a9782Sad 
361bee149d1Schristos 	setprogname(argv[0]);
362bee149d1Schristos 
363101a9782Sad 	/*
364101a9782Sad 	 * Parse options.
365101a9782Sad 	 */
366f7bf36a3Schristos 	while ((ch = getopt(argc, argv, "M:N:w")) != -1) {
367101a9782Sad 		switch (ch) {
368101a9782Sad 		case 'M':
369101a9782Sad 			memf = optarg;
370101a9782Sad 			break;
371101a9782Sad 		case 'N':
372101a9782Sad 			nlistf = optarg;
373101a9782Sad 			break;
374f7bf36a3Schristos 		case 'w':
375f7bf36a3Schristos 			flags = O_RDWR;
376f7bf36a3Schristos 			break;
377101a9782Sad 		default:
378101a9782Sad 			usage();
379101a9782Sad 		}
380101a9782Sad 	}
381101a9782Sad 	argc -= optind;
382101a9782Sad 	argv += optind;
383101a9782Sad 
384101a9782Sad 	/*
385101a9782Sad 	 * Print a list of images, and allow user to select.
386101a9782Sad 	 */
387101a9782Sad 	/* XXX */
388101a9782Sad 
389101a9782Sad 	/*
390101a9782Sad 	 * Open the images (crash dump and symbol table).
391101a9782Sad 	 */
392f7bf36a3Schristos 	kd = kvm_open(nlistf, memf, NULL, flags, getprogname());
393101a9782Sad 	if (kd == NULL) {
394101a9782Sad 		return EXIT_FAILURE;
395101a9782Sad 	}
396101a9782Sad 	fd = open(nlistf, O_RDONLY);
397bee149d1Schristos 	if (fd == -1)  {
398bee149d1Schristos 		err(EXIT_FAILURE, "open `%s'", nlistf);
399101a9782Sad 	}
400bee149d1Schristos 	if (fstat(fd, &sb) == -1) {
401bee149d1Schristos 		err(EXIT_FAILURE, "stat `%s'", nlistf);
402101a9782Sad 	}
403101a9782Sad 	if ((sb.st_mode & S_IFMT) != S_IFREG) {	/* XXX ksyms */
404101a9782Sad 		sz = MAXSTAB;
405101a9782Sad 		elf = malloc(sz);
406101a9782Sad 		if (elf == NULL) {
407bee149d1Schristos 			err(EXIT_FAILURE, "malloc(%zu)", sz);
408101a9782Sad 		}
409101a9782Sad 		sz = read(fd, elf, sz);
410bee149d1Schristos 		if ((ssize_t)sz == -1) {
411bee149d1Schristos 			err(EXIT_FAILURE, "read `%s'", nlistf);
412101a9782Sad 		}
413101a9782Sad 		if (sz == MAXSTAB) {
414bee149d1Schristos 			errx(EXIT_FAILURE, "symbol table > %d bytes", MAXSTAB);
415101a9782Sad 		}
416101a9782Sad 	} else {
417101a9782Sad 		sz = sb.st_size;
41867f79fd4Schristos 		elf = mmap(NULL, sz, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
419101a9782Sad 		if (elf == MAP_FAILED) {
420bee149d1Schristos 			err(EXIT_FAILURE, "mmap `%s'", nlistf);
421101a9782Sad 		}
422101a9782Sad 	}
423101a9782Sad 
424101a9782Sad 	/*
425101a9782Sad 	 * Print kernel & crash versions.
426101a9782Sad 	 */
427101a9782Sad 	if (kvm_nlist(kd, nl) == -1) {
428101a9782Sad 		errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd));
429101a9782Sad 	}
430101a9782Sad 	if ((size_t)kvm_read(kd, nl[X_OSRELEASE].n_value, imgrelease,
431101a9782Sad 	    sizeof(imgrelease)) != sizeof(imgrelease)) {
432101a9782Sad 		errx(EXIT_FAILURE, "cannot read osrelease: %s",
433101a9782Sad 		    kvm_geterr(kd));
434101a9782Sad 	}
435101a9782Sad 	printf("Crash version %s, image version %s.\n", osrelease, imgrelease);
436101a9782Sad 	if (strcmp(osrelease, imgrelease) != 0) {
437101a9782Sad 		printf("WARNING: versions differ, you may not be able to "
438101a9782Sad 		    "examine this image.\n");
439101a9782Sad 	}
44071e41cecSad #ifdef LOCKDEBUG
44171e41cecSad 	if ((size_t)kvm_read(kd, nl[X_LOCKDEBUG].n_value, &ld_all,
44271e41cecSad 	    sizeof(ld_all)) != sizeof(ld_all))
44371e41cecSad 		printf("Kernel compiled without options LOCKDEBUG.\n");
44471e41cecSad #endif
445101a9782Sad 
446101a9782Sad 	/*
447101a9782Sad 	 * Print the panic string, if any.
448101a9782Sad 	 */
449101a9782Sad 	if ((size_t)kvm_read(kd, nl[X_PANICSTR].n_value, &panicstr,
450101a9782Sad 	    sizeof(panicstr)) != sizeof(panicstr)) {
451101a9782Sad 		errx(EXIT_FAILURE, "cannot read panicstr: %s",
452101a9782Sad 		    kvm_geterr(kd));
453101a9782Sad 	}
454101a9782Sad 	if (strcmp(memf, _PATH_MEM) == 0) {
455101a9782Sad 		printf("Output from a running system is unreliable.\n");
456101a9782Sad 	} else if (panicstr == 0) {
457101a9782Sad 		printf("System does not appear to have panicked.\n");
458101a9782Sad 	} else {
459101a9782Sad 		printf("System panicked: ");
460101a9782Sad 		for (;;) {
461101a9782Sad 			if ((size_t)kvm_read(kd, panicstr, &c, sizeof(c)) !=
462101a9782Sad 			    sizeof(c)) {
463101a9782Sad 				errx(EXIT_FAILURE, "cannot read *panicstr: %s",
464101a9782Sad 				    kvm_geterr(kd));
465101a9782Sad 			}
466101a9782Sad 			if (c == '\0') {
467101a9782Sad 				break;
468101a9782Sad 			}
469101a9782Sad 			putchar(c);
470101a9782Sad 			panicstr++;
471101a9782Sad 		}
472101a9782Sad 		putchar('\n');
473101a9782Sad 	}
474101a9782Sad 
475101a9782Sad 	/*
476101a9782Sad 	 * Initialize line editing.
477101a9782Sad 	 */
478101a9782Sad 	hist = history_init();
479101a9782Sad 	history(hist, &he, H_SETSIZE, 100);
480101a9782Sad 	elptr = el_init(getprogname(), stdin, stdout, stderr);
481101a9782Sad 	el_set(elptr, EL_EDITOR, "emacs");
482101a9782Sad 	el_set(elptr, EL_SIGNAL, 1);
483101a9782Sad 	el_set(elptr, EL_HIST, history, hist);
484101a9782Sad 	el_set(elptr, EL_PROMPT, prompt);
485101a9782Sad 	el_source(elptr, NULL);
486101a9782Sad 
487bee149d1Schristos 	atexit(cleanup);
488bee149d1Schristos 
489101a9782Sad 	/*
490101a9782Sad 	 * Initialize ddb.
491101a9782Sad 	 */
492101a9782Sad 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
493101a9782Sad 		db_max_width = ws.ws_col;
494101a9782Sad 	}
495101a9782Sad 	db_mach_init(kd);
496101a9782Sad 	ddb_init(sz, elf, (char *)elf + sz);
497101a9782Sad 
498101a9782Sad 	/*
499101a9782Sad 	 * Debug it!
500101a9782Sad 	 */
501101a9782Sad 	db_command_loop();
502101a9782Sad 
503101a9782Sad 	/*
504bee149d1Schristos 	 * Finish.
505101a9782Sad 	 */
506bee149d1Schristos 	return EXIT_SUCCESS;
507101a9782Sad }
508