1 /* $NetBSD: crash.c,v 1.15 2023/04/12 17:53:32 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.15 2023/04/12 17:53:32 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 #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 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 313 fprintf(stderr, "cngetc\n"); 314 abort(); 315 } 316 317 void 318 cnputc(int c) 319 { 320 321 putc(c, ofp); 322 } 323 324 __dead static void 325 usage(void) 326 { 327 328 fprintf(stderr, 329 "usage: %s [-w] [-M core] [-N kernel]\n\n" 330 "-M core\tspecify memory file (default /dev/mem)\n" 331 "-N kernel\tspecify name list file (default /dev/ksyms)\n", 332 getprogname()); 333 exit(EXIT_FAILURE); 334 } 335 336 static const char * 337 prompt(void) 338 { 339 340 return "crash> "; 341 } 342 343 int 344 main(int argc, char **argv) 345 { 346 const char *nlistf, *memf; 347 uintptr_t panicstr; 348 struct winsize ws; 349 struct stat sb; 350 size_t sz; 351 void *elf; 352 int fd, ch, flags; 353 char c; 354 355 nlistf = _PATH_KSYMS; 356 memf = _PATH_MEM; 357 ofp = stdout; 358 flags = O_RDONLY; 359 360 setprogname(argv[0]); 361 362 /* 363 * Parse options. 364 */ 365 while ((ch = getopt(argc, argv, "M:N:w")) != -1) { 366 switch (ch) { 367 case 'M': 368 memf = optarg; 369 break; 370 case 'N': 371 nlistf = optarg; 372 break; 373 case 'w': 374 flags = O_RDWR; 375 break; 376 default: 377 usage(); 378 } 379 } 380 argc -= optind; 381 argv += optind; 382 383 /* 384 * Print a list of images, and allow user to select. 385 */ 386 /* XXX */ 387 388 /* 389 * Open the images (crash dump and symbol table). 390 */ 391 kd = kvm_open(nlistf, memf, NULL, flags, getprogname()); 392 if (kd == NULL) { 393 return EXIT_FAILURE; 394 } 395 fd = open(nlistf, O_RDONLY); 396 if (fd == -1) { 397 err(EXIT_FAILURE, "open `%s'", nlistf); 398 } 399 if (fstat(fd, &sb) == -1) { 400 err(EXIT_FAILURE, "stat `%s'", nlistf); 401 } 402 if ((sb.st_mode & S_IFMT) != S_IFREG) { /* XXX ksyms */ 403 sz = MAXSTAB; 404 elf = malloc(sz); 405 if (elf == NULL) { 406 err(EXIT_FAILURE, "malloc(%zu)", sz); 407 } 408 sz = read(fd, elf, sz); 409 if ((ssize_t)sz == -1) { 410 err(EXIT_FAILURE, "read `%s'", nlistf); 411 } 412 if (sz == MAXSTAB) { 413 errx(EXIT_FAILURE, "symbol table > %d bytes", MAXSTAB); 414 } 415 } else { 416 sz = sb.st_size; 417 elf = mmap(NULL, sz, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0); 418 if (elf == MAP_FAILED) { 419 err(EXIT_FAILURE, "mmap `%s'", nlistf); 420 } 421 } 422 423 /* 424 * Print kernel & crash versions. 425 */ 426 if (kvm_nlist(kd, nl) == -1) { 427 errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd)); 428 } 429 if ((size_t)kvm_read(kd, nl[X_OSRELEASE].n_value, imgrelease, 430 sizeof(imgrelease)) != sizeof(imgrelease)) { 431 errx(EXIT_FAILURE, "cannot read osrelease: %s", 432 kvm_geterr(kd)); 433 } 434 printf("Crash version %s, image version %s.\n", osrelease, imgrelease); 435 if (strcmp(osrelease, imgrelease) != 0) { 436 printf("WARNING: versions differ, you may not be able to " 437 "examine this image.\n"); 438 } 439 #ifdef LOCKDEBUG 440 if ((size_t)kvm_read(kd, nl[X_LOCKDEBUG].n_value, &ld_all, 441 sizeof(ld_all)) != sizeof(ld_all)) 442 printf("Kernel compiled without options LOCKDEBUG.\n"); 443 #endif 444 445 /* 446 * Print the panic string, if any. 447 */ 448 if ((size_t)kvm_read(kd, nl[X_PANICSTR].n_value, &panicstr, 449 sizeof(panicstr)) != sizeof(panicstr)) { 450 errx(EXIT_FAILURE, "cannot read panicstr: %s", 451 kvm_geterr(kd)); 452 } 453 if (strcmp(memf, _PATH_MEM) == 0) { 454 printf("Output from a running system is unreliable.\n"); 455 } else if (panicstr == 0) { 456 printf("System does not appear to have panicked.\n"); 457 } else { 458 printf("System panicked: "); 459 for (;;) { 460 if ((size_t)kvm_read(kd, panicstr, &c, sizeof(c)) != 461 sizeof(c)) { 462 errx(EXIT_FAILURE, "cannot read *panicstr: %s", 463 kvm_geterr(kd)); 464 } 465 if (c == '\0') { 466 break; 467 } 468 putchar(c); 469 panicstr++; 470 } 471 putchar('\n'); 472 } 473 474 /* 475 * Initialize line editing. 476 */ 477 hist = history_init(); 478 history(hist, &he, H_SETSIZE, 100); 479 elptr = el_init(getprogname(), stdin, stdout, stderr); 480 el_set(elptr, EL_EDITOR, "emacs"); 481 el_set(elptr, EL_SIGNAL, 1); 482 el_set(elptr, EL_HIST, history, hist); 483 el_set(elptr, EL_PROMPT, prompt); 484 el_source(elptr, NULL); 485 486 atexit(cleanup); 487 488 /* 489 * Initialize ddb. 490 */ 491 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) { 492 db_max_width = ws.ws_col; 493 } 494 db_mach_init(kd); 495 ddb_init(sz, elf, (char *)elf + sz); 496 497 /* 498 * Debug it! 499 */ 500 db_command_loop(); 501 502 /* 503 * Finish. 504 */ 505 return EXIT_SUCCESS; 506 } 507