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