crash.c revision 1.17 1 /* $NetBSD: crash.c,v 1.17 2024/11/21 07:20:10 skrll 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.17 2024/11/21 07:20:10 skrll 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
66 db_regs_t ddb_regs;
67
68 static kvm_t *kd;
69 static History *hist;
70 static HistEvent he;
71 static EditLine *elptr;
72 static char imgrelease[16];
73 static FILE *ofp;
74
75 static 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
88 struct lockdebug;
89 TAILQ_HEAD(, lockdebug) ld_all;
90 #else
91 void lockdebug_lock_print(void);
92 void lockdebug_lock_print(void) {
93 warnx("No lockdebug support compiled in");
94 }
95 #endif
96
97 static void
98 cleanup(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
109 void
110 db_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
121 void
122 db_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
131 void
132 db_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
142 void
143 db_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
153 void *
154 db_alloc(size_t sz)
155 {
156
157 return emalloc(sz);
158 }
159
160 void *
161 db_zalloc(size_t sz)
162 {
163
164 return ecalloc(1, sz);
165 }
166
167 void
168 db_free(void *p, size_t sz)
169 {
170
171 free(p);
172 }
173
174 static void
175 punt(void)
176 {
177
178 db_printf("This command can only be used in-kernel.\n");
179 }
180
181 void
182 db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
183 const char *modif)
184 {
185
186 punt();
187 }
188
189 void
190 db_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
197 void
198 db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
199 const char *modif)
200 {
201
202 punt();
203 }
204
205 void
206 db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
207 const char *modif)
208 {
209
210 punt();
211 }
212
213
214 void
215 db_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
223 void
224 db_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
232 void
233 db_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
241 void
242 db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
243 const char *modif)
244 {
245
246 punt();
247 }
248
249 int
250 db_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
302 void
303 db_check_interrupt(void)
304 {
305
306 }
307
308 int
309 cngetc(void)
310 {
311 char ch;
312
313 if (el_getc(elptr, &ch) <= 0)
314 return 0;
315 return (unsigned char)ch;
316 }
317
318 void
319 cnputc(int c)
320 {
321
322 putc(c, ofp);
323 }
324
325 __dead static void
326 usage(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
337 static const char *
338 prompt(void)
339 {
340
341 return "crash> ";
342 }
343
344 int
345 main(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