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