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