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