1*1a32d96eSskrll /* $NetBSD: crash.c,v 1.17 2024/11/21 07:20:10 skrll Exp $ */ 2101a9782Sad 3101a9782Sad /*- 4101a9782Sad * Copyright (c) 2009 The NetBSD Foundation, Inc. 5101a9782Sad * All rights reserved. 6101a9782Sad * 7101a9782Sad * This code is derived from software contributed to The NetBSD Foundation 8101a9782Sad * by Andrew Doran. 9101a9782Sad * 10101a9782Sad * Redistribution and use in source and binary forms, with or without 11101a9782Sad * modification, are permitted provided that the following conditions 12101a9782Sad * are met: 13101a9782Sad * 1. Redistributions of source code must retain the above copyright 14101a9782Sad * notice, this list of conditions and the following disclaimer. 15101a9782Sad * 2. Redistributions in binary form must reproduce the above copyright 16101a9782Sad * notice, this list of conditions and the following disclaimer in the 17101a9782Sad * documentation and/or other materials provided with the distribution. 18101a9782Sad * 19101a9782Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20101a9782Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21101a9782Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22101a9782Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23101a9782Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24101a9782Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25101a9782Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26101a9782Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27101a9782Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28101a9782Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29101a9782Sad * POSSIBILITY OF SUCH DAMAGE. 30101a9782Sad */ 31101a9782Sad 32101a9782Sad #include <sys/cdefs.h> 33101a9782Sad #ifndef lint 34*1a32d96eSskrll __RCSID("$NetBSD: crash.c,v 1.17 2024/11/21 07:20:10 skrll Exp $"); 35101a9782Sad #endif /* not lint */ 36101a9782Sad 37f55073dfSriastradh #include <sys/types.h> 38101a9782Sad 39101a9782Sad #include <sys/fcntl.h> 40101a9782Sad #include <sys/mman.h> 4122e2f968Schristos #include <sys/stat.h> 42101a9782Sad #include <sys/ioctl.h> 43101a9782Sad 444d62e245Smrg #ifndef __mips__ 45101a9782Sad #include <machine/frame.h> 464d62e245Smrg #endif 47101a9782Sad 48f55073dfSriastradh #include <ddb/ddb.h> 49f55073dfSriastradh 50101a9782Sad #include <stdarg.h> 51101a9782Sad #include <stdlib.h> 52101a9782Sad #include <unistd.h> 53101a9782Sad #include <getopt.h> 54101a9782Sad #include <errno.h> 55101a9782Sad #include <histedit.h> 56101a9782Sad #include <paths.h> 57101a9782Sad #include <kvm.h> 58101a9782Sad #include <err.h> 59101a9782Sad #include <ctype.h> 60bee149d1Schristos #include <util.h> 61101a9782Sad 62101a9782Sad #include "extern.h" 63101a9782Sad 64101a9782Sad #define MAXSTAB (16 * 1024 * 1024) 65101a9782Sad 66101a9782Sad db_regs_t ddb_regs; 67bee149d1Schristos 68bee149d1Schristos static kvm_t *kd; 69bee149d1Schristos static History *hist; 70bee149d1Schristos static HistEvent he; 71bee149d1Schristos static EditLine *elptr; 72bee149d1Schristos static char imgrelease[16]; 73bee149d1Schristos static FILE *ofp; 74101a9782Sad 75101a9782Sad static struct nlist nl[] = { 76101a9782Sad #define X_OSRELEASE 0 77101a9782Sad { .n_name = "_osrelease" }, 78101a9782Sad #define X_PANICSTR 1 79101a9782Sad { .n_name = "_panicstr" }, 80fbd525a3Schristos #ifdef LOCKDEBUG 81fbd525a3Schristos #define X_LOCKDEBUG 2 82fbd525a3Schristos { .n_name = "ld_all" }, 83fbd525a3Schristos #endif 84101a9782Sad { .n_name = NULL }, 85101a9782Sad }; 86101a9782Sad 87fbd525a3Schristos #ifdef LOCKDEBUG 88fbd525a3Schristos struct lockdebug; 89fbd525a3Schristos TAILQ_HEAD(, lockdebug) ld_all; 90fbd525a3Schristos #else 91fbd525a3Schristos void lockdebug_lock_print(void); 92fbd525a3Schristos void lockdebug_lock_print(void) { 93fbd525a3Schristos warnx("No lockdebug support compiled in"); 94fbd525a3Schristos } 95fbd525a3Schristos #endif 96fbd525a3Schristos 97bee149d1Schristos static void 98bee149d1Schristos cleanup(void) 99bee149d1Schristos { 100bee149d1Schristos if (ofp != stdout) { 101bee149d1Schristos (void)fflush(ofp); 102bee149d1Schristos (void)pclose(ofp); 103bee149d1Schristos ofp = stdout; 104bee149d1Schristos } 105bee149d1Schristos el_end(elptr); 106bee149d1Schristos history_end(hist); 107bee149d1Schristos } 108bee149d1Schristos 109101a9782Sad void 110101a9782Sad db_vprintf(const char *fmt, va_list ap) 111101a9782Sad { 112101a9782Sad char buf[1024]; 113101a9782Sad int b, c; 114101a9782Sad 115101a9782Sad c = vsnprintf(buf, sizeof(buf), fmt, ap); 116101a9782Sad for (b = 0; b < c; b++) { 117101a9782Sad db_putchar(buf[b]); 118101a9782Sad } 119101a9782Sad } 120101a9782Sad 121101a9782Sad void 122101a9782Sad db_printf(const char *fmt, ...) 123101a9782Sad { 124101a9782Sad va_list ap; 125101a9782Sad 126101a9782Sad va_start(ap, fmt); 127101a9782Sad db_vprintf(fmt, ap); 128101a9782Sad va_end(ap); 129101a9782Sad } 130101a9782Sad 131101a9782Sad void 132101a9782Sad db_write_bytes(db_addr_t addr, size_t size, const char *str) 133101a9782Sad { 134101a9782Sad 1352269fc7fSlukem if ((size_t)kvm_write(kd, addr, str, size) != size) { 136101a9782Sad warnx("kvm_write(%p, %zd): %s", (void *)addr, size, 137101a9782Sad kvm_geterr(kd)); 138101a9782Sad longjmp(db_recover); 139101a9782Sad } 140101a9782Sad } 141101a9782Sad 142101a9782Sad void 143101a9782Sad db_read_bytes(db_addr_t addr, size_t size, char *str) 144101a9782Sad { 145101a9782Sad 1462269fc7fSlukem if ((size_t)kvm_read(kd, addr, str, size) != size) { 147101a9782Sad warnx("kvm_read(%p, %zd): %s", (void *)addr, size, 148101a9782Sad kvm_geterr(kd)); 149101a9782Sad longjmp(db_recover); 150101a9782Sad } 151101a9782Sad } 152101a9782Sad 153101a9782Sad void * 154101a9782Sad db_alloc(size_t sz) 155101a9782Sad { 156101a9782Sad 157bee149d1Schristos return emalloc(sz); 158101a9782Sad } 159101a9782Sad 160101a9782Sad void * 161101a9782Sad db_zalloc(size_t sz) 162101a9782Sad { 163101a9782Sad 164bee149d1Schristos return ecalloc(1, sz); 165101a9782Sad } 166101a9782Sad 167101a9782Sad void 168101a9782Sad db_free(void *p, size_t sz) 169101a9782Sad { 170101a9782Sad 171101a9782Sad free(p); 172101a9782Sad } 173101a9782Sad 174101a9782Sad static void 175101a9782Sad punt(void) 176101a9782Sad { 177101a9782Sad 178101a9782Sad db_printf("This command can only be used in-kernel.\n"); 179101a9782Sad } 180101a9782Sad 181101a9782Sad void 182101a9782Sad db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 183101a9782Sad const char *modif) 184101a9782Sad { 185101a9782Sad 186101a9782Sad punt(); 187101a9782Sad } 188101a9782Sad 189101a9782Sad void 190101a9782Sad db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 191101a9782Sad const char *modif) 192101a9782Sad { 193101a9782Sad 194101a9782Sad db_cmd_loop_done = true; 195101a9782Sad } 196101a9782Sad 197101a9782Sad void 198101a9782Sad db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 199101a9782Sad const char *modif) 200101a9782Sad { 201101a9782Sad 202101a9782Sad punt(); 203101a9782Sad } 204101a9782Sad 205101a9782Sad void 206101a9782Sad db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 207101a9782Sad const char *modif) 208101a9782Sad { 209101a9782Sad 210101a9782Sad punt(); 211101a9782Sad } 212101a9782Sad 213101a9782Sad 214101a9782Sad void 215101a9782Sad db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 216101a9782Sad const char *modif) 217101a9782Sad { 218101a9782Sad 219101a9782Sad punt(); 220101a9782Sad } 221101a9782Sad 222101a9782Sad 223101a9782Sad void 224101a9782Sad db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 225101a9782Sad const char *modif) 226101a9782Sad { 227101a9782Sad 228101a9782Sad punt(); 229101a9782Sad } 230101a9782Sad 231101a9782Sad 232101a9782Sad void 233101a9782Sad db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 234101a9782Sad const char *modif) 235101a9782Sad { 236101a9782Sad 237101a9782Sad punt(); 238101a9782Sad } 239101a9782Sad 240101a9782Sad 241101a9782Sad void 242101a9782Sad db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 243101a9782Sad const char *modif) 244101a9782Sad { 245101a9782Sad 246101a9782Sad punt(); 247101a9782Sad } 248101a9782Sad 249101a9782Sad int 250101a9782Sad db_readline(char *lstart, int lsize) 251101a9782Sad { 252101a9782Sad const char *el; 253101a9782Sad char *pcmd; 254101a9782Sad int cnt; 255101a9782Sad 256101a9782Sad db_force_whitespace(); 257101a9782Sad 258101a9782Sad /* Close any open pipe. */ 259101a9782Sad if (ofp != stdout) { 260101a9782Sad (void)fflush(ofp); 261101a9782Sad (void)pclose(ofp); 262101a9782Sad ofp = stdout; 263101a9782Sad } 264101a9782Sad 265101a9782Sad /* Read next command. */ 266101a9782Sad el = el_gets(elptr, &cnt); 267bee149d1Schristos if (el == NULL) { /* EOF */ 268bee149d1Schristos exit(EXIT_SUCCESS); 269101a9782Sad } 270101a9782Sad 271101a9782Sad /* Save to history, and copy to caller's buffer. */ 272101a9782Sad history(hist, &he, H_ENTER, el); 273101a9782Sad strlcpy(lstart, el, lsize); 274101a9782Sad if (cnt >= lsize) { 275101a9782Sad cnt = lsize - 1; 276101a9782Sad } 277101a9782Sad lstart[cnt] = '\0'; 278101a9782Sad if (cnt > 0 && lstart[cnt - 1] == '\n') { 279101a9782Sad lstart[cnt - 1] = '\0'; 280101a9782Sad } 281101a9782Sad 282101a9782Sad /* Need to open a pipe? If not, return now. */ 283101a9782Sad pcmd = strchr(lstart, '|'); 284101a9782Sad if (pcmd == NULL) { 285101a9782Sad return strlen(lstart); 286101a9782Sad } 287101a9782Sad 288101a9782Sad /* Open a pipe to specified command, redirect output. */ 289101a9782Sad assert(ofp == stdout); 290bee149d1Schristos for (*pcmd++ = '\0'; isspace((unsigned char)*pcmd); pcmd++) { 291101a9782Sad /* nothing */ 292101a9782Sad } 293101a9782Sad errno = 0; 294101a9782Sad ofp = popen(pcmd, "w"); 295101a9782Sad if (ofp == NULL) { 296101a9782Sad warn("opening pipe for command `%s'", pcmd); 297101a9782Sad *lstart = '\0'; 298101a9782Sad } 299101a9782Sad return strlen(lstart); 300101a9782Sad } 301101a9782Sad 302101a9782Sad void 303101a9782Sad db_check_interrupt(void) 304101a9782Sad { 305101a9782Sad 306101a9782Sad } 307101a9782Sad 308101a9782Sad int 309101a9782Sad cngetc(void) 310101a9782Sad { 311832b6478Smlelstv char ch; 312101a9782Sad 313832b6478Smlelstv if (el_getc(elptr, &ch) <= 0) 314832b6478Smlelstv return 0; 315832b6478Smlelstv return (unsigned char)ch; 316101a9782Sad } 317101a9782Sad 318101a9782Sad void 319101a9782Sad cnputc(int c) 320101a9782Sad { 321101a9782Sad 322101a9782Sad putc(c, ofp); 323101a9782Sad } 324101a9782Sad 325bec77c5fSjoerg __dead static void 326101a9782Sad usage(void) 327101a9782Sad { 328101a9782Sad 329101a9782Sad fprintf(stderr, 33004f07660Swiz "usage: %s [-w] [-M core] [-N kernel]\n\n" 33134564de1Schristos "-M core\tspecify memory file (default /dev/mem)\n" 33204f07660Swiz "-N kernel\tspecify name list file (default /dev/ksyms)\n", 333101a9782Sad getprogname()); 334101a9782Sad exit(EXIT_FAILURE); 335101a9782Sad } 336101a9782Sad 337101a9782Sad static const char * 338101a9782Sad prompt(void) 339101a9782Sad { 340101a9782Sad 341101a9782Sad return "crash> "; 342101a9782Sad } 343101a9782Sad 344101a9782Sad int 345101a9782Sad main(int argc, char **argv) 346101a9782Sad { 347101a9782Sad const char *nlistf, *memf; 348101a9782Sad uintptr_t panicstr; 349101a9782Sad struct winsize ws; 350101a9782Sad struct stat sb; 351101a9782Sad size_t sz; 352101a9782Sad void *elf; 353f7bf36a3Schristos int fd, ch, flags; 354101a9782Sad char c; 355101a9782Sad 356101a9782Sad nlistf = _PATH_KSYMS; 357101a9782Sad memf = _PATH_MEM; 358101a9782Sad ofp = stdout; 359f7bf36a3Schristos flags = O_RDONLY; 360101a9782Sad 361bee149d1Schristos setprogname(argv[0]); 362bee149d1Schristos 363101a9782Sad /* 364101a9782Sad * Parse options. 365101a9782Sad */ 366f7bf36a3Schristos while ((ch = getopt(argc, argv, "M:N:w")) != -1) { 367101a9782Sad switch (ch) { 368101a9782Sad case 'M': 369101a9782Sad memf = optarg; 370101a9782Sad break; 371101a9782Sad case 'N': 372101a9782Sad nlistf = optarg; 373101a9782Sad break; 374f7bf36a3Schristos case 'w': 375f7bf36a3Schristos flags = O_RDWR; 376f7bf36a3Schristos break; 377101a9782Sad default: 378101a9782Sad usage(); 379101a9782Sad } 380101a9782Sad } 381101a9782Sad argc -= optind; 382101a9782Sad argv += optind; 383101a9782Sad 384101a9782Sad /* 385101a9782Sad * Print a list of images, and allow user to select. 386101a9782Sad */ 387101a9782Sad /* XXX */ 388101a9782Sad 389101a9782Sad /* 390101a9782Sad * Open the images (crash dump and symbol table). 391101a9782Sad */ 392f7bf36a3Schristos kd = kvm_open(nlistf, memf, NULL, flags, getprogname()); 393101a9782Sad if (kd == NULL) { 394101a9782Sad return EXIT_FAILURE; 395101a9782Sad } 396101a9782Sad fd = open(nlistf, O_RDONLY); 397bee149d1Schristos if (fd == -1) { 398bee149d1Schristos err(EXIT_FAILURE, "open `%s'", nlistf); 399101a9782Sad } 400bee149d1Schristos if (fstat(fd, &sb) == -1) { 401bee149d1Schristos err(EXIT_FAILURE, "stat `%s'", nlistf); 402101a9782Sad } 403101a9782Sad if ((sb.st_mode & S_IFMT) != S_IFREG) { /* XXX ksyms */ 404101a9782Sad sz = MAXSTAB; 405101a9782Sad elf = malloc(sz); 406101a9782Sad if (elf == NULL) { 407bee149d1Schristos err(EXIT_FAILURE, "malloc(%zu)", sz); 408101a9782Sad } 409101a9782Sad sz = read(fd, elf, sz); 410bee149d1Schristos if ((ssize_t)sz == -1) { 411bee149d1Schristos err(EXIT_FAILURE, "read `%s'", nlistf); 412101a9782Sad } 413101a9782Sad if (sz == MAXSTAB) { 414bee149d1Schristos errx(EXIT_FAILURE, "symbol table > %d bytes", MAXSTAB); 415101a9782Sad } 416101a9782Sad } else { 417101a9782Sad sz = sb.st_size; 41867f79fd4Schristos elf = mmap(NULL, sz, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0); 419101a9782Sad if (elf == MAP_FAILED) { 420bee149d1Schristos err(EXIT_FAILURE, "mmap `%s'", nlistf); 421101a9782Sad } 422101a9782Sad } 423101a9782Sad 424101a9782Sad /* 425101a9782Sad * Print kernel & crash versions. 426101a9782Sad */ 427101a9782Sad if (kvm_nlist(kd, nl) == -1) { 428101a9782Sad errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd)); 429101a9782Sad } 430101a9782Sad if ((size_t)kvm_read(kd, nl[X_OSRELEASE].n_value, imgrelease, 431101a9782Sad sizeof(imgrelease)) != sizeof(imgrelease)) { 432101a9782Sad errx(EXIT_FAILURE, "cannot read osrelease: %s", 433101a9782Sad kvm_geterr(kd)); 434101a9782Sad } 435101a9782Sad printf("Crash version %s, image version %s.\n", osrelease, imgrelease); 436101a9782Sad if (strcmp(osrelease, imgrelease) != 0) { 437101a9782Sad printf("WARNING: versions differ, you may not be able to " 438101a9782Sad "examine this image.\n"); 439101a9782Sad } 44071e41cecSad #ifdef LOCKDEBUG 44171e41cecSad if ((size_t)kvm_read(kd, nl[X_LOCKDEBUG].n_value, &ld_all, 44271e41cecSad sizeof(ld_all)) != sizeof(ld_all)) 44371e41cecSad printf("Kernel compiled without options LOCKDEBUG.\n"); 44471e41cecSad #endif 445101a9782Sad 446101a9782Sad /* 447101a9782Sad * Print the panic string, if any. 448101a9782Sad */ 449101a9782Sad if ((size_t)kvm_read(kd, nl[X_PANICSTR].n_value, &panicstr, 450101a9782Sad sizeof(panicstr)) != sizeof(panicstr)) { 451101a9782Sad errx(EXIT_FAILURE, "cannot read panicstr: %s", 452101a9782Sad kvm_geterr(kd)); 453101a9782Sad } 454101a9782Sad if (strcmp(memf, _PATH_MEM) == 0) { 455101a9782Sad printf("Output from a running system is unreliable.\n"); 456101a9782Sad } else if (panicstr == 0) { 457101a9782Sad printf("System does not appear to have panicked.\n"); 458101a9782Sad } else { 459101a9782Sad printf("System panicked: "); 460101a9782Sad for (;;) { 461101a9782Sad if ((size_t)kvm_read(kd, panicstr, &c, sizeof(c)) != 462101a9782Sad sizeof(c)) { 463101a9782Sad errx(EXIT_FAILURE, "cannot read *panicstr: %s", 464101a9782Sad kvm_geterr(kd)); 465101a9782Sad } 466101a9782Sad if (c == '\0') { 467101a9782Sad break; 468101a9782Sad } 469101a9782Sad putchar(c); 470101a9782Sad panicstr++; 471101a9782Sad } 472101a9782Sad putchar('\n'); 473101a9782Sad } 474101a9782Sad 475101a9782Sad /* 476101a9782Sad * Initialize line editing. 477101a9782Sad */ 478101a9782Sad hist = history_init(); 479101a9782Sad history(hist, &he, H_SETSIZE, 100); 480101a9782Sad elptr = el_init(getprogname(), stdin, stdout, stderr); 481101a9782Sad el_set(elptr, EL_EDITOR, "emacs"); 482101a9782Sad el_set(elptr, EL_SIGNAL, 1); 483101a9782Sad el_set(elptr, EL_HIST, history, hist); 484101a9782Sad el_set(elptr, EL_PROMPT, prompt); 485101a9782Sad el_source(elptr, NULL); 486101a9782Sad 487bee149d1Schristos atexit(cleanup); 488bee149d1Schristos 489101a9782Sad /* 490101a9782Sad * Initialize ddb. 491101a9782Sad */ 492101a9782Sad if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) { 493101a9782Sad db_max_width = ws.ws_col; 494101a9782Sad } 495101a9782Sad db_mach_init(kd); 496101a9782Sad ddb_init(sz, elf, (char *)elf + sz); 497101a9782Sad 498101a9782Sad /* 499101a9782Sad * Debug it! 500101a9782Sad */ 501101a9782Sad db_command_loop(); 502101a9782Sad 503101a9782Sad /* 504bee149d1Schristos * Finish. 505101a9782Sad */ 506bee149d1Schristos return EXIT_SUCCESS; 507101a9782Sad } 508