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