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