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