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