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