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