1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stdarg.h> 28 #include <unistd.h> 29 #include <fcntl.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <deflt.h> 33 #include <time.h> 34 #include <syslog.h> 35 #include <stropts.h> 36 #include <pthread.h> 37 #include <limits.h> 38 #include <atomic.h> 39 #include <libnvpair.h> 40 #include <libintl.h> 41 #include <sys/mem.h> 42 #include <sys/statvfs.h> 43 #include <sys/dumphdr.h> 44 #include <sys/dumpadm.h> 45 #include <sys/compress.h> 46 #include <sys/panic.h> 47 #include <sys/sysmacros.h> 48 #include <sys/stat.h> 49 #include <sys/resource.h> 50 #include <bzip2/bzlib.h> 51 #include <sys/fm/util.h> 52 #include <fm/libfmevent.h> 53 #include <sys/int_fmtio.h> 54 55 56 /* fread/fwrite buffer size */ 57 #define FBUFSIZE (1ULL << 20) 58 59 /* minimum size for output buffering */ 60 #define MINCOREBLKSIZE (1ULL << 17) 61 62 /* create this file if metrics collection is enabled in the kernel */ 63 #define METRICSFILE "METRICS.csv" 64 65 static char progname[9] = "savecore"; 66 static char *savedir; /* savecore directory */ 67 static char *dumpfile; /* source of raw crash dump */ 68 static long bounds = -1; /* numeric suffix */ 69 static long pagesize; /* dump pagesize */ 70 static int dumpfd = -1; /* dumpfile descriptor */ 71 static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */ 72 static boolean_t dump_incomplete; /* dumphdr indicates incomplete */ 73 static boolean_t fm_panic; /* dump is the result of fm_panic */ 74 static offset_t endoff; /* offset of end-of-dump header */ 75 static int verbose; /* chatty mode */ 76 static int disregard_valid_flag; /* disregard valid flag */ 77 static int livedump; /* dump the current running system */ 78 static int interactive; /* user invoked; no syslog */ 79 static int csave; /* save dump compressed */ 80 static int filemode; /* processing file, not dump device */ 81 static int percent_done; /* progress indicator */ 82 static hrtime_t startts; /* timestamp at start */ 83 static volatile uint64_t saved; /* count of pages written */ 84 static volatile uint64_t zpages; /* count of zero pages not written */ 85 static dumpdatahdr_t datahdr; /* compression info */ 86 static long coreblksize; /* preferred write size (st_blksize) */ 87 static int cflag; /* run as savecore -c */ 88 static int mflag; /* run as savecore -m */ 89 90 /* 91 * Payload information for the events we raise. These are used 92 * in raise_event to determine what payload to include. 93 */ 94 #define SC_PAYLOAD_SAVEDIR 0x0001 /* Include savedir in event */ 95 #define SC_PAYLOAD_INSTANCE 0x0002 /* Include bounds instance number */ 96 #define SC_PAYLOAD_IMAGEUUID 0x0004 /* Include dump OS instance uuid */ 97 #define SC_PAYLOAD_CRASHTIME 0x0008 /* Include epoch crashtime */ 98 #define SC_PAYLOAD_PANICSTR 0x0010 /* Include panic string */ 99 #define SC_PAYLOAD_PANICSTACK 0x0020 /* Include panic string */ 100 #define SC_PAYLOAD_FAILREASON 0x0040 /* Include failure reason */ 101 #define SC_PAYLOAD_DUMPCOMPLETE 0x0080 /* Include completeness indicator */ 102 #define SC_PAYLOAD_ISCOMPRESSED 0x0100 /* Dump is in vmdump.N form */ 103 #define SC_PAYLOAD_DUMPADM_EN 0x0200 /* Is dumpadm enabled or not? */ 104 #define SC_PAYLOAD_FM_PANIC 0x0400 /* Panic initiated by FMA */ 105 #define SC_PAYLOAD_JUSTCHECKING 0x0800 /* Run with -c flag? */ 106 107 enum sc_event_type { 108 SC_EVENT_DUMP_PENDING, 109 SC_EVENT_SAVECORE_FAILURE, 110 SC_EVENT_DUMP_AVAILABLE 111 }; 112 113 /* 114 * Common payload 115 */ 116 #define _SC_PAYLOAD_CMN \ 117 SC_PAYLOAD_IMAGEUUID | \ 118 SC_PAYLOAD_CRASHTIME | \ 119 SC_PAYLOAD_PANICSTR | \ 120 SC_PAYLOAD_PANICSTACK | \ 121 SC_PAYLOAD_DUMPCOMPLETE | \ 122 SC_PAYLOAD_FM_PANIC | \ 123 SC_PAYLOAD_SAVEDIR 124 125 static const struct { 126 const char *sce_subclass; 127 uint32_t sce_payload; 128 } sc_event[] = { 129 /* 130 * SC_EVENT_DUMP_PENDING 131 */ 132 { 133 "dump_pending_on_device", 134 _SC_PAYLOAD_CMN | SC_PAYLOAD_DUMPADM_EN | 135 SC_PAYLOAD_JUSTCHECKING 136 }, 137 138 /* 139 * SC_EVENT_SAVECORE_FAILURE 140 */ 141 { 142 "savecore_failure", 143 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_FAILREASON 144 }, 145 146 /* 147 * SC_EVENT_DUMP_AVAILABLE 148 */ 149 { 150 "dump_available", 151 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED 152 }, 153 }; 154 155 static void raise_event(enum sc_event_type, char *); 156 157 static void 158 usage(void) 159 { 160 (void) fprintf(stderr, 161 "usage: %s [-Lvd] [-f dumpfile] [dirname]\n", progname); 162 exit(1); 163 } 164 165 #define SC_SL_NONE 0x0001 /* no syslog */ 166 #define SC_SL_ERR 0x0002 /* syslog if !interactive, LOG_ERR */ 167 #define SC_SL_WARN 0x0004 /* syslog if !interactive, LOG_WARNING */ 168 #define SC_IF_VERBOSE 0x0008 /* message only if -v */ 169 #define SC_IF_ISATTY 0x0010 /* message only if interactive */ 170 #define SC_EXIT_OK 0x0020 /* exit(0) */ 171 #define SC_EXIT_ERR 0x0040 /* exit(1) */ 172 #define SC_EXIT_PEND 0x0080 /* exit(2) */ 173 #define SC_EXIT_FM 0x0100 /* exit(3) */ 174 175 #define _SC_ALLEXIT (SC_EXIT_OK | SC_EXIT_ERR | SC_EXIT_PEND | SC_EXIT_FM) 176 177 static void 178 logprint(uint32_t flags, char *message, ...) 179 { 180 va_list args; 181 char buf[1024]; 182 int do_always = ((flags & (SC_IF_VERBOSE | SC_IF_ISATTY)) == 0); 183 int do_ifverb = (flags & SC_IF_VERBOSE) && verbose; 184 int do_ifisatty = (flags & SC_IF_ISATTY) && interactive; 185 int code; 186 static int logprint_raised = 0; 187 188 if (do_always || do_ifverb || do_ifisatty) { 189 va_start(args, message); 190 /*LINTED: E_SEC_PRINTF_VAR_FMT*/ 191 (void) vsnprintf(buf, sizeof (buf), message, args); 192 (void) fprintf(stderr, "%s: %s\n", progname, buf); 193 if (!interactive) { 194 switch (flags & (SC_SL_NONE | SC_SL_ERR | SC_SL_WARN)) { 195 case SC_SL_ERR: 196 /*LINTED: E_SEC_PRINTF_VAR_FMT*/ 197 syslog(LOG_ERR, buf); 198 break; 199 200 case SC_SL_WARN: 201 /*LINTED: E_SEC_PRINTF_VAR_FMT*/ 202 syslog(LOG_WARNING, buf); 203 break; 204 205 default: 206 break; 207 } 208 } 209 va_end(args); 210 } 211 212 switch (flags & _SC_ALLEXIT) { 213 case 0: 214 return; 215 216 case SC_EXIT_OK: 217 code = 0; 218 break; 219 220 case SC_EXIT_PEND: 221 code = 2; 222 break; 223 224 case SC_EXIT_FM: 225 code = 3; 226 break; 227 228 case SC_EXIT_ERR: 229 default: 230 /* 231 * Raise an ireport saying why we are exiting. Do not 232 * raise if run as savecore -m. If something in the 233 * raise_event codepath calls logprint avoid recursion. 234 */ 235 if (!mflag && logprint_raised++ == 0) 236 raise_event(SC_EVENT_SAVECORE_FAILURE, buf); 237 code = 1; 238 break; 239 } 240 241 exit(code); 242 } 243 244 /* 245 * System call / libc wrappers that exit on error. 246 */ 247 static int 248 Open(const char *name, int oflags, mode_t mode) 249 { 250 int fd; 251 252 if ((fd = open64(name, oflags, mode)) == -1) 253 logprint(SC_SL_ERR | SC_EXIT_ERR, "open(\"%s\"): %s", 254 name, strerror(errno)); 255 return (fd); 256 } 257 258 static void 259 Fread(void *buf, size_t size, FILE *f) 260 { 261 if (fread(buf, size, 1, f) != 1) 262 logprint(SC_SL_ERR | SC_EXIT_ERR, "fread: ferror %d feof %d", 263 ferror(f), feof(f)); 264 } 265 266 static void 267 Fwrite(void *buf, size_t size, FILE *f) 268 { 269 if (fwrite(buf, size, 1, f) != 1) 270 logprint(SC_SL_ERR | SC_EXIT_ERR, "fwrite: %s", 271 strerror(errno)); 272 } 273 274 static void 275 Fseek(offset_t off, FILE *f) 276 { 277 if (fseeko64(f, off, SEEK_SET) != 0) 278 logprint(SC_SL_ERR | SC_EXIT_ERR, "fseeko64: %s", 279 strerror(errno)); 280 } 281 282 typedef struct stat64 Stat_t; 283 284 static void 285 Fstat(int fd, Stat_t *sb, const char *fname) 286 { 287 if (fstat64(fd, sb) != 0) 288 logprint(SC_SL_ERR | SC_EXIT_ERR, "fstat(\"%s\"): %s", fname, 289 strerror(errno)); 290 } 291 292 static void 293 Stat(const char *fname, Stat_t *sb) 294 { 295 if (stat64(fname, sb) != 0) 296 logprint(SC_SL_ERR | SC_EXIT_ERR, "stat(\"%s\"): %s", fname, 297 strerror(errno)); 298 } 299 300 static void 301 Pread(int fd, void *buf, size_t size, offset_t off) 302 { 303 ssize_t sz = pread64(fd, buf, size, off); 304 305 if (sz < 0) 306 logprint(SC_SL_ERR | SC_EXIT_ERR, 307 "pread: %s", strerror(errno)); 308 else if (sz != size) 309 logprint(SC_SL_ERR | SC_EXIT_ERR, 310 "pread: size %ld != %ld", sz, size); 311 } 312 313 static void 314 Pwrite(int fd, void *buf, size_t size, off64_t off) 315 { 316 if (pwrite64(fd, buf, size, off) != size) 317 logprint(SC_SL_ERR | SC_EXIT_ERR, "pwrite: %s", 318 strerror(errno)); 319 } 320 321 static void * 322 Zalloc(size_t size) 323 { 324 void *buf; 325 326 if ((buf = calloc(size, 1)) == NULL) 327 logprint(SC_SL_ERR | SC_EXIT_ERR, "calloc: %s", 328 strerror(errno)); 329 return (buf); 330 } 331 332 static long 333 read_number_from_file(const char *filename, long default_value) 334 { 335 long file_value = -1; 336 FILE *fp; 337 338 if ((fp = fopen(filename, "r")) != NULL) { 339 (void) fscanf(fp, "%ld", &file_value); 340 (void) fclose(fp); 341 } 342 return (file_value < 0 ? default_value : file_value); 343 } 344 345 static void 346 read_dumphdr(void) 347 { 348 if (filemode) 349 dumpfd = Open(dumpfile, O_RDONLY, 0644); 350 else 351 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); 352 endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET; 353 Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 354 Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr)); 355 356 pagesize = dumphdr.dump_pagesize; 357 358 if (dumphdr.dump_magic != DUMP_MAGIC) 359 logprint(SC_SL_NONE | SC_EXIT_OK, "bad magic number %x", 360 dumphdr.dump_magic); 361 362 if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) 363 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK, 364 "dump already processed"); 365 366 if (dumphdr.dump_version != DUMP_VERSION) 367 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK, 368 "dump version (%d) != %s version (%d)", 369 dumphdr.dump_version, progname, DUMP_VERSION); 370 371 if (dumphdr.dump_wordsize != DUMP_WORDSIZE) 372 logprint(SC_SL_NONE | SC_EXIT_OK, 373 "dump is from %u-bit kernel - cannot save on %u-bit kernel", 374 dumphdr.dump_wordsize, DUMP_WORDSIZE); 375 376 if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) { 377 if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION) 378 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK, 379 "dump data version (%d) != %s data version (%d)", 380 datahdr.dump_datahdr_version, progname, 381 DUMP_DATAHDR_VERSION); 382 } else { 383 (void) memset(&datahdr, 0, sizeof (datahdr)); 384 datahdr.dump_maxcsize = pagesize; 385 } 386 387 /* 388 * Read the initial header, clear the valid bits, and compare headers. 389 * The main header may have been overwritten by swapping if we're 390 * using a swap partition as the dump device, in which case we bail. 391 */ 392 Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start); 393 394 corehdr.dump_flags &= ~DF_VALID; 395 dumphdr.dump_flags &= ~DF_VALID; 396 397 if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) { 398 /* 399 * Clear valid bit so we don't complain on every invocation. 400 */ 401 if (!filemode) 402 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 403 logprint(SC_SL_ERR | SC_EXIT_ERR, 404 "initial dump header corrupt"); 405 } 406 } 407 408 static void 409 check_space(int csave) 410 { 411 struct statvfs fsb; 412 int64_t spacefree, dumpsize, minfree, datasize; 413 414 if (statvfs(".", &fsb) < 0) 415 logprint(SC_SL_ERR | SC_EXIT_ERR, "statvfs: %s", 416 strerror(errno)); 417 418 dumpsize = dumphdr.dump_data - dumphdr.dump_start; 419 datasize = dumphdr.dump_npages * pagesize; 420 if (!csave) 421 dumpsize += datasize; 422 else 423 dumpsize += datahdr.dump_data_csize; 424 425 spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize; 426 minfree = 1024LL * read_number_from_file("minfree", 1024); 427 if (spacefree < minfree + dumpsize) { 428 logprint(SC_SL_ERR | SC_EXIT_ERR, 429 "not enough space in %s (%lld MB avail, %lld MB needed)", 430 savedir, spacefree >> 20, (minfree + dumpsize) >> 20); 431 } 432 } 433 434 static void 435 build_dump_map(int corefd, const pfn_t *pfn_table) 436 { 437 long i; 438 static long misses = 0; 439 size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t); 440 mem_vtop_t vtop; 441 dump_map_t *dmp = Zalloc(dump_mapsize); 442 char *inbuf = Zalloc(FBUFSIZE); 443 FILE *in = fdopen(dup(dumpfd), "rb"); 444 445 (void) setvbuf(in, inbuf, _IOFBF, FBUFSIZE); 446 Fseek(dumphdr.dump_map, in); 447 448 corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize); 449 450 for (i = 0; i < corehdr.dump_nvtop; i++) { 451 long first = 0; 452 long last = corehdr.dump_npages - 1; 453 long middle; 454 pfn_t pfn; 455 uintptr_t h; 456 457 Fread(&vtop, sizeof (mem_vtop_t), in); 458 while (last >= first) { 459 middle = (first + last) / 2; 460 pfn = pfn_table[middle]; 461 if (pfn == vtop.m_pfn) 462 break; 463 if (pfn < vtop.m_pfn) 464 first = middle + 1; 465 else 466 last = middle - 1; 467 } 468 if (pfn != vtop.m_pfn) { 469 if (++misses <= 10) 470 (void) fprintf(stderr, 471 "pfn %ld not found for as=%p, va=%p\n", 472 vtop.m_pfn, (void *)vtop.m_as, vtop.m_va); 473 continue; 474 } 475 476 dmp[i].dm_as = vtop.m_as; 477 dmp[i].dm_va = (uintptr_t)vtop.m_va; 478 dmp[i].dm_data = corehdr.dump_data + 479 ((uint64_t)middle << corehdr.dump_pageshift); 480 481 h = DUMP_HASH(&corehdr, dmp[i].dm_as, dmp[i].dm_va); 482 dmp[i].dm_next = dmp[h].dm_first; 483 dmp[h].dm_first = corehdr.dump_map + i * sizeof (dump_map_t); 484 } 485 486 Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map); 487 free(dmp); 488 (void) fclose(in); 489 free(inbuf); 490 } 491 492 /* 493 * Copy whole sections of the dump device to the file. 494 */ 495 static void 496 Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf, 497 size_t sz) 498 { 499 size_t nr; 500 offset_t off = *offp; 501 502 while (nb > 0) { 503 nr = sz < nb ? sz : (size_t)nb; 504 Pread(dumpfd, buf, nr, dumpoff); 505 Pwrite(fd, buf, nr, off); 506 off += nr; 507 dumpoff += nr; 508 nb -= nr; 509 } 510 *offp = off; 511 } 512 513 /* 514 * Copy pages when the dump data header is missing. 515 * This supports older kernels with latest savecore. 516 */ 517 static void 518 CopyPages(offset_t *offp, int fd, char *buf, size_t sz) 519 { 520 uint32_t csize; 521 FILE *in = fdopen(dup(dumpfd), "rb"); 522 FILE *out = fdopen(dup(fd), "wb"); 523 char *cbuf = Zalloc(pagesize); 524 char *outbuf = Zalloc(FBUFSIZE); 525 pgcnt_t np = dumphdr.dump_npages; 526 527 (void) setvbuf(out, outbuf, _IOFBF, FBUFSIZE); 528 (void) setvbuf(in, buf, _IOFBF, sz); 529 Fseek(dumphdr.dump_data, in); 530 531 Fseek(*offp, out); 532 while (np > 0) { 533 Fread(&csize, sizeof (uint32_t), in); 534 Fwrite(&csize, sizeof (uint32_t), out); 535 *offp += sizeof (uint32_t); 536 if (csize > pagesize || csize == 0) { 537 logprint(SC_SL_ERR, 538 "CopyPages: page %lu csize %d (0x%x) pagesize %d", 539 dumphdr.dump_npages - np, csize, csize, 540 pagesize); 541 break; 542 } 543 Fread(cbuf, csize, in); 544 Fwrite(cbuf, csize, out); 545 *offp += csize; 546 np--; 547 } 548 (void) fclose(in); 549 (void) fclose(out); 550 free(outbuf); 551 free(buf); 552 } 553 554 /* 555 * Concatenate dump contents into a new file. 556 * Update corehdr with new offsets. 557 */ 558 static void 559 copy_crashfile(const char *corefile) 560 { 561 int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 562 size_t bufsz = FBUFSIZE; 563 char *inbuf = Zalloc(bufsz); 564 offset_t coreoff; 565 size_t nb; 566 567 logprint(SC_SL_ERR | SC_IF_VERBOSE, 568 "Copying %s to %s/%s\n", dumpfile, savedir, corefile); 569 570 /* 571 * This dump file is still compressed 572 */ 573 corehdr.dump_flags |= DF_COMPRESSED | DF_VALID; 574 575 /* 576 * Leave room for corehdr, it is updated and written last 577 */ 578 corehdr.dump_start = 0; 579 coreoff = sizeof (corehdr); 580 581 /* 582 * Read in the compressed symbol table, copy it to corefile. 583 */ 584 coreoff = roundup(coreoff, pagesize); 585 corehdr.dump_ksyms = coreoff; 586 Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd, 587 inbuf, bufsz); 588 589 /* 590 * Save the pfn table. 591 */ 592 coreoff = roundup(coreoff, pagesize); 593 corehdr.dump_pfn = coreoff; 594 Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff, 595 corefd, inbuf, bufsz); 596 597 /* 598 * Save the dump map. 599 */ 600 coreoff = roundup(coreoff, pagesize); 601 corehdr.dump_map = coreoff; 602 Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t), 603 &coreoff, corefd, inbuf, bufsz); 604 605 /* 606 * Save the data pages. 607 */ 608 coreoff = roundup(coreoff, pagesize); 609 corehdr.dump_data = coreoff; 610 if (datahdr.dump_data_csize != 0) 611 Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff, 612 corefd, inbuf, bufsz); 613 else 614 CopyPages(&coreoff, corefd, inbuf, bufsz); 615 616 /* 617 * Now write the modified dump header to front and end of the copy. 618 * Make it look like a valid dump device. 619 * 620 * From dumphdr.h: Two headers are written out: one at the 621 * beginning of the dump, and the other at the very end of the 622 * dump device. The terminal header is at a known location 623 * (end of device) so we can always find it. 624 * 625 * Pad with zeros to each DUMP_OFFSET boundary. 626 */ 627 (void) memset(inbuf, 0, DUMP_OFFSET); 628 629 nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); 630 if (nb > 0) { 631 Pwrite(corefd, inbuf, nb, coreoff); 632 coreoff += nb; 633 } 634 635 Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff); 636 coreoff += sizeof (corehdr); 637 638 Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff); 639 coreoff += sizeof (datahdr); 640 641 nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); 642 if (nb > 0) { 643 Pwrite(corefd, inbuf, nb, coreoff); 644 } 645 646 free(inbuf); 647 Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start); 648 649 /* 650 * Write out the modified dump header to the dump device. 651 * The dump device has been processed, so DF_VALID is clear. 652 */ 653 if (!filemode) 654 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 655 656 (void) close(corefd); 657 } 658 659 /* 660 * compressed streams 661 */ 662 typedef struct blockhdr blockhdr_t; 663 typedef struct block block_t; 664 665 struct blockhdr { 666 block_t *head; 667 block_t *tail; 668 }; 669 670 struct block { 671 block_t *next; 672 char *block; 673 int size; 674 }; 675 676 typedef enum streamstate { 677 STREAMSTART, 678 STREAMPAGES 679 } streamstate_t; 680 681 typedef struct stream { 682 streamstate_t state; 683 int init; 684 int tag; 685 int bound; 686 int nout; 687 char *blkbuf; 688 blockhdr_t blocks; 689 pgcnt_t pagenum; 690 pgcnt_t curpage; 691 pgcnt_t npages; 692 pgcnt_t done; 693 bz_stream strm; 694 dumpcsize_t sc; 695 dumpstreamhdr_t sh; 696 } stream_t; 697 698 static stream_t *streams; 699 static stream_t *endstreams; 700 701 const int cs = sizeof (dumpcsize_t); 702 703 typedef struct tinfo { 704 pthread_t tid; 705 int corefd; 706 } tinfo_t; 707 708 static int threads_stop; 709 static int threads_active; 710 static tinfo_t *tinfo; 711 static tinfo_t *endtinfo; 712 713 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 714 static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER; 715 static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER; 716 static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER; 717 718 static blockhdr_t freeblocks; 719 720 static void 721 enqt(blockhdr_t *h, block_t *b) 722 { 723 b->next = NULL; 724 if (h->tail == NULL) 725 h->head = b; 726 else 727 h->tail->next = b; 728 h->tail = b; 729 } 730 731 static block_t * 732 deqh(blockhdr_t *h) 733 { 734 block_t *b = h->head; 735 736 if (b != NULL) { 737 h->head = b->next; 738 if (h->head == NULL) 739 h->tail = NULL; 740 } 741 return (b); 742 } 743 744 static void *runstreams(void *arg); 745 746 static void 747 initstreams(int corefd, int nstreams, int maxcsize) 748 { 749 int nthreads; 750 int nblocks; 751 int i; 752 block_t *b; 753 tinfo_t *t; 754 755 nthreads = sysconf(_SC_NPROCESSORS_ONLN); 756 if (nstreams < nthreads) 757 nthreads = nstreams; 758 if (nthreads < 1) 759 nthreads = 1; 760 nblocks = nthreads * 2; 761 762 tinfo = Zalloc(nthreads * sizeof (tinfo_t)); 763 endtinfo = &tinfo[nthreads]; 764 765 /* init streams */ 766 streams = Zalloc(nstreams * sizeof (stream_t)); 767 endstreams = &streams[nstreams]; 768 769 /* init stream block buffers */ 770 for (i = 0; i < nblocks; i++) { 771 b = Zalloc(sizeof (block_t)); 772 b->block = Zalloc(maxcsize); 773 enqt(&freeblocks, b); 774 } 775 776 /* init worker threads */ 777 (void) pthread_mutex_lock(&lock); 778 threads_active = 1; 779 threads_stop = 0; 780 for (t = tinfo; t != endtinfo; t++) { 781 t->corefd = dup(corefd); 782 if (t->corefd < 0) { 783 nthreads = t - tinfo; 784 endtinfo = t; 785 break; 786 } 787 if (pthread_create(&t->tid, NULL, runstreams, t) != 0) 788 logprint(SC_SL_ERR | SC_EXIT_ERR, "pthread_create: %s", 789 strerror(errno)); 790 } 791 (void) pthread_mutex_unlock(&lock); 792 } 793 794 static void 795 sbarrier() 796 { 797 stream_t *s; 798 799 (void) pthread_mutex_lock(&lock); 800 for (s = streams; s != endstreams; s++) { 801 while (s->bound || s->blocks.head != NULL) 802 (void) pthread_cond_wait(&cvbarrier, &lock); 803 } 804 (void) pthread_mutex_unlock(&lock); 805 } 806 807 static void 808 stopstreams() 809 { 810 tinfo_t *t; 811 812 if (threads_active) { 813 sbarrier(); 814 (void) pthread_mutex_lock(&lock); 815 threads_stop = 1; 816 (void) pthread_cond_signal(&cvwork); 817 (void) pthread_mutex_unlock(&lock); 818 for (t = tinfo; t != endtinfo; t++) 819 (void) pthread_join(t->tid, NULL); 820 free(tinfo); 821 tinfo = NULL; 822 threads_active = 0; 823 } 824 } 825 826 static block_t * 827 getfreeblock() 828 { 829 block_t *b; 830 831 (void) pthread_mutex_lock(&lock); 832 while ((b = deqh(&freeblocks)) == NULL) 833 (void) pthread_cond_wait(&cvfree, &lock); 834 (void) pthread_mutex_unlock(&lock); 835 return (b); 836 } 837 838 /* data page offset from page number */ 839 #define BTOP(b) ((b) >> dumphdr.dump_pageshift) 840 #define PTOB(p) ((p) << dumphdr.dump_pageshift) 841 #define DATAOFF(p) (corehdr.dump_data + PTOB(p)) 842 843 /* check for coreblksize boundary */ 844 static int 845 isblkbnd(pgcnt_t pgnum) 846 { 847 return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0); 848 } 849 850 static int 851 iszpage(char *buf) 852 { 853 size_t sz; 854 uint64_t *pl; 855 856 /*LINTED:E_BAD_PTR_CAST_ALIGN*/ 857 pl = (uint64_t *)(buf); 858 for (sz = 0; sz < pagesize; sz += sizeof (*pl)) 859 if (*pl++ != 0) 860 return (0); 861 return (1); 862 } 863 864 volatile uint_t *hist; 865 866 /* write pages to the core file */ 867 static void 868 putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np) 869 { 870 atomic_inc_uint(&hist[np]); 871 if (np > 0) 872 Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum)); 873 } 874 875 /* 876 * Process one lzjb block. 877 * No object (stream header or page) will be split over a block boundary. 878 */ 879 static void 880 lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz) 881 { 882 int in = 0; 883 int csize; 884 int doflush; 885 char *out; 886 size_t dsize; 887 dumpcsize_t sc; 888 dumpstreamhdr_t sh; 889 890 if (!s->init) { 891 s->init = 1; 892 if (s->blkbuf == NULL) 893 s->blkbuf = Zalloc(coreblksize); 894 s->state = STREAMSTART; 895 } 896 while (in < blocksz) { 897 switch (s->state) { 898 case STREAMSTART: 899 (void) memcpy(&sh, block + in, sizeof (sh)); 900 in += sizeof (sh); 901 if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0) 902 logprint(SC_SL_ERR | SC_EXIT_ERR, 903 "LZJB STREAMSTART: bad stream header"); 904 if (sh.stream_npages > datahdr.dump_maxrange) 905 logprint(SC_SL_ERR | SC_EXIT_ERR, 906 "LZJB STREAMSTART: bad range: %d > %d", 907 sh.stream_npages, datahdr.dump_maxrange); 908 s->pagenum = sh.stream_pagenum; 909 s->npages = sh.stream_npages; 910 s->curpage = s->pagenum; 911 s->nout = 0; 912 s->done = 0; 913 s->state = STREAMPAGES; 914 break; 915 case STREAMPAGES: 916 (void) memcpy(&sc, block + in, cs); 917 in += cs; 918 csize = DUMP_GET_CSIZE(sc); 919 if (csize > pagesize) 920 logprint(SC_SL_ERR | SC_EXIT_ERR, 921 "LZJB STREAMPAGES: bad csize=%d", csize); 922 923 out = s->blkbuf + PTOB(s->nout); 924 dsize = decompress(block + in, out, csize, pagesize); 925 926 if (dsize != pagesize) 927 logprint(SC_SL_ERR | SC_EXIT_ERR, 928 "LZJB STREAMPAGES: dsize %d != pagesize %d", 929 dsize, pagesize); 930 931 in += csize; 932 atomic_inc_64(&saved); 933 934 doflush = 0; 935 if (s->nout == 0 && iszpage(out)) { 936 doflush = 1; 937 atomic_inc_64(&zpages); 938 } else if (++s->nout >= BTOP(coreblksize) || 939 isblkbnd(s->curpage + s->nout)) { 940 doflush = 1; 941 } 942 if (++s->done >= s->npages) { 943 s->state = STREAMSTART; 944 doflush = 1; 945 } 946 if (doflush) { 947 putpage(corefd, s->blkbuf, s->curpage, s->nout); 948 s->nout = 0; 949 s->curpage = s->pagenum + s->done; 950 } 951 break; 952 } 953 } 954 } 955 956 /* bzlib library reports errors with this callback */ 957 void 958 bz_internal_error(int errcode) 959 { 960 logprint(SC_SL_ERR | SC_EXIT_ERR, "bz_internal_error: err %s\n", 961 BZ2_bzErrorString(errcode)); 962 } 963 964 /* 965 * Return one object in the stream. 966 * 967 * An object (stream header or page) will likely span an input block 968 * of compression data. Return non-zero when an entire object has been 969 * retrieved from the stream. 970 */ 971 static int 972 bz2decompress(stream_t *s, void *buf, size_t size) 973 { 974 int rc; 975 976 if (s->strm.avail_out == 0) { 977 s->strm.next_out = buf; 978 s->strm.avail_out = size; 979 } 980 while (s->strm.avail_in > 0) { 981 rc = BZ2_bzDecompress(&s->strm); 982 if (rc == BZ_STREAM_END) { 983 rc = BZ2_bzDecompressReset(&s->strm); 984 if (rc != BZ_OK) 985 logprint(SC_SL_ERR | SC_EXIT_ERR, 986 "BZ2_bzDecompressReset: %s", 987 BZ2_bzErrorString(rc)); 988 continue; 989 } 990 991 if (s->strm.avail_out == 0) 992 break; 993 } 994 return (s->strm.avail_out == 0); 995 } 996 997 /* 998 * Process one bzip2 block. 999 * The interface is documented here: 1000 * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html 1001 */ 1002 static void 1003 bz2block(int corefd, stream_t *s, char *block, size_t blocksz) 1004 { 1005 int rc = 0; 1006 int doflush; 1007 char *out; 1008 1009 if (!s->init) { 1010 s->init = 1; 1011 rc = BZ2_bzDecompressInit(&s->strm, 0, 0); 1012 if (rc != BZ_OK) 1013 logprint(SC_SL_ERR | SC_EXIT_ERR, 1014 "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc)); 1015 if (s->blkbuf == NULL) 1016 s->blkbuf = Zalloc(coreblksize); 1017 s->strm.avail_out = 0; 1018 s->state = STREAMSTART; 1019 } 1020 s->strm.next_in = block; 1021 s->strm.avail_in = blocksz; 1022 1023 while (s->strm.avail_in > 0) { 1024 switch (s->state) { 1025 case STREAMSTART: 1026 if (!bz2decompress(s, &s->sh, sizeof (s->sh))) 1027 return; 1028 if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0) 1029 logprint(SC_SL_ERR | SC_EXIT_ERR, 1030 "BZ2 STREAMSTART: bad stream header"); 1031 if (s->sh.stream_npages > datahdr.dump_maxrange) 1032 logprint(SC_SL_ERR | SC_EXIT_ERR, 1033 "BZ2 STREAMSTART: bad range: %d > %d", 1034 s->sh.stream_npages, datahdr.dump_maxrange); 1035 s->pagenum = s->sh.stream_pagenum; 1036 s->npages = s->sh.stream_npages; 1037 s->curpage = s->pagenum; 1038 s->nout = 0; 1039 s->done = 0; 1040 s->state = STREAMPAGES; 1041 break; 1042 case STREAMPAGES: 1043 out = s->blkbuf + PTOB(s->nout); 1044 if (!bz2decompress(s, out, pagesize)) 1045 return; 1046 1047 atomic_inc_64(&saved); 1048 1049 doflush = 0; 1050 if (s->nout == 0 && iszpage(out)) { 1051 doflush = 1; 1052 atomic_inc_64(&zpages); 1053 } else if (++s->nout >= BTOP(coreblksize) || 1054 isblkbnd(s->curpage + s->nout)) { 1055 doflush = 1; 1056 } 1057 if (++s->done >= s->npages) { 1058 s->state = STREAMSTART; 1059 doflush = 1; 1060 } 1061 if (doflush) { 1062 putpage(corefd, s->blkbuf, s->curpage, s->nout); 1063 s->nout = 0; 1064 s->curpage = s->pagenum + s->done; 1065 } 1066 break; 1067 } 1068 } 1069 } 1070 1071 /* report progress */ 1072 static void 1073 report_progress() 1074 { 1075 int sec, percent; 1076 1077 if (!interactive) 1078 return; 1079 1080 percent = saved * 100LL / corehdr.dump_npages; 1081 if (percent > percent_done) { 1082 sec = (gethrtime() - startts) / 1000 / 1000 / 1000; 1083 (void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60, 1084 percent); 1085 (void) fflush(stdout); 1086 percent_done = percent; 1087 } 1088 } 1089 1090 /* thread body */ 1091 static void * 1092 runstreams(void *arg) 1093 { 1094 tinfo_t *t = arg; 1095 stream_t *s; 1096 block_t *b; 1097 int bound; 1098 1099 (void) pthread_mutex_lock(&lock); 1100 while (!threads_stop) { 1101 bound = 0; 1102 for (s = streams; s != endstreams; s++) { 1103 if (s->bound || s->blocks.head == NULL) 1104 continue; 1105 s->bound = 1; 1106 bound = 1; 1107 (void) pthread_cond_signal(&cvwork); 1108 while (s->blocks.head != NULL) { 1109 b = deqh(&s->blocks); 1110 (void) pthread_mutex_unlock(&lock); 1111 1112 if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2) 1113 lzjbblock(t->corefd, s, b->block, 1114 b->size); 1115 else 1116 bz2block(t->corefd, s, b->block, 1117 b->size); 1118 1119 (void) pthread_mutex_lock(&lock); 1120 enqt(&freeblocks, b); 1121 (void) pthread_cond_signal(&cvfree); 1122 1123 report_progress(); 1124 } 1125 s->bound = 0; 1126 (void) pthread_cond_signal(&cvbarrier); 1127 } 1128 if (!bound && !threads_stop) 1129 (void) pthread_cond_wait(&cvwork, &lock); 1130 } 1131 (void) close(t->corefd); 1132 (void) pthread_cond_signal(&cvwork); 1133 (void) pthread_mutex_unlock(&lock); 1134 return (arg); 1135 } 1136 1137 /* 1138 * Process compressed pages. 1139 * 1140 * The old format, now called single-threaded lzjb, is a 32-bit size 1141 * word followed by 'size' bytes of lzjb compression data for one 1142 * page. The new format extends this by storing a 12-bit "tag" in the 1143 * upper bits of the size word. When the size word is pagesize or 1144 * less, it is assumed to be one lzjb page. When the size word is 1145 * greater than pagesize, it is assumed to be a "stream block", 1146 * belonging to up to 4095 streams. In practice, the number of streams 1147 * is set to one less than the number of CPUs running at crash 1148 * time. One CPU processes the crash dump, the remaining CPUs 1149 * separately process groups of data pages. 1150 * 1151 * savecore creates a thread per stream, but never more threads than 1152 * the number of CPUs running savecore. This is because savecore can 1153 * be processing a crash file from a remote machine, which may have 1154 * more CPUs. 1155 * 1156 * When the kernel uses parallel lzjb or parallel bzip2, we expect a 1157 * series of 128KB blocks of compression data. In this case, each 1158 * block has a "tag", in the range 1-4095. Each block is handed off to 1159 * to the threads running "runstreams". The dump format is either lzjb 1160 * or bzip2, never a mixture. These threads, in turn, process the 1161 * compression data for groups of pages. Groups of pages are delimited 1162 * by a "stream header", which indicates a starting pfn and number of 1163 * pages. When a stream block has been read, the condition variable 1164 * "cvwork" is signalled, which causes one of the avaiable threads to 1165 * wake up and process the stream. 1166 * 1167 * In the parallel case there will be streams blocks encoding all data 1168 * pages. The stream of blocks is terminated by a zero size 1169 * word. There can be a few lzjb pages tacked on the end, depending on 1170 * the architecture. The sbarrier function ensures that all stream 1171 * blocks have been processed so that the page number for the few 1172 * single pages at the end can be known. 1173 */ 1174 static void 1175 decompress_pages(int corefd) 1176 { 1177 char *cpage = NULL; 1178 char *dpage = NULL; 1179 char *out; 1180 pgcnt_t curpage; 1181 block_t *b; 1182 FILE *dumpf; 1183 FILE *tracef = NULL; 1184 stream_t *s; 1185 size_t dsize; 1186 size_t insz = FBUFSIZE; 1187 char *inbuf = Zalloc(insz); 1188 uint32_t csize; 1189 dumpcsize_t dcsize; 1190 int nstreams = datahdr.dump_nstreams; 1191 int maxcsize = datahdr.dump_maxcsize; 1192 int nout, tag, doflush; 1193 1194 dumpf = fdopen(dup(dumpfd), "rb"); 1195 if (dumpf == NULL) 1196 logprint(SC_SL_ERR | SC_EXIT_ERR, "fdopen: %s", 1197 strerror(errno)); 1198 1199 (void) setvbuf(dumpf, inbuf, _IOFBF, insz); 1200 Fseek(dumphdr.dump_data, dumpf); 1201 1202 /*LINTED: E_CONSTANT_CONDITION*/ 1203 while (1) { 1204 1205 /* 1206 * The csize word delimits stream blocks. 1207 * See dumphdr.h for a description. 1208 */ 1209 Fread(&dcsize, sizeof (dcsize), dumpf); 1210 1211 tag = DUMP_GET_TAG(dcsize); 1212 csize = DUMP_GET_CSIZE(dcsize); 1213 1214 if (tag != 0) { /* a stream block */ 1215 1216 if (nstreams == 0) 1217 logprint(SC_SL_ERR | SC_EXIT_ERR, 1218 "starting data header is missing"); 1219 1220 if (tag > nstreams) 1221 logprint(SC_SL_ERR | SC_EXIT_ERR, 1222 "stream tag %d not in range 1..%d", 1223 tag, nstreams); 1224 1225 if (csize > maxcsize) 1226 logprint(SC_SL_ERR | SC_EXIT_ERR, 1227 "block size 0x%x > max csize 0x%x", 1228 csize, maxcsize); 1229 1230 if (streams == NULL) 1231 initstreams(corefd, nstreams, maxcsize); 1232 s = &streams[tag - 1]; 1233 s->tag = tag; 1234 1235 b = getfreeblock(); 1236 b->size = csize; 1237 Fread(b->block, csize, dumpf); 1238 1239 (void) pthread_mutex_lock(&lock); 1240 enqt(&s->blocks, b); 1241 if (!s->bound) 1242 (void) pthread_cond_signal(&cvwork); 1243 (void) pthread_mutex_unlock(&lock); 1244 1245 } else if (csize > 0) { /* one lzjb page */ 1246 1247 if (csize > pagesize) 1248 logprint(SC_SL_ERR | SC_EXIT_ERR, 1249 "csize 0x%x > pagesize 0x%x", 1250 csize, pagesize); 1251 1252 if (cpage == NULL) 1253 cpage = Zalloc(pagesize); 1254 if (dpage == NULL) { 1255 dpage = Zalloc(coreblksize); 1256 nout = 0; 1257 } 1258 1259 Fread(cpage, csize, dumpf); 1260 1261 out = dpage + PTOB(nout); 1262 dsize = decompress(cpage, out, csize, pagesize); 1263 1264 if (dsize != pagesize) 1265 logprint(SC_SL_ERR | SC_EXIT_ERR, 1266 "dsize 0x%x != pagesize 0x%x", 1267 dsize, pagesize); 1268 1269 /* 1270 * wait for streams to flush so that 'saved' is correct 1271 */ 1272 if (threads_active) 1273 sbarrier(); 1274 1275 doflush = 0; 1276 if (nout == 0) 1277 curpage = saved; 1278 1279 atomic_inc_64(&saved); 1280 1281 if (nout == 0 && iszpage(dpage)) { 1282 doflush = 1; 1283 atomic_inc_64(&zpages); 1284 } else if (++nout >= BTOP(coreblksize) || 1285 isblkbnd(curpage + nout) || 1286 saved >= dumphdr.dump_npages) { 1287 doflush = 1; 1288 } 1289 1290 if (doflush) { 1291 putpage(corefd, dpage, curpage, nout); 1292 nout = 0; 1293 } 1294 1295 report_progress(); 1296 1297 /* 1298 * Non-streams lzjb does not use blocks. Stop 1299 * here if all the pages have been decompressed. 1300 */ 1301 if (saved >= dumphdr.dump_npages) 1302 break; 1303 1304 } else { 1305 break; /* end of data */ 1306 } 1307 } 1308 1309 stopstreams(); 1310 if (tracef != NULL) 1311 (void) fclose(tracef); 1312 (void) fclose(dumpf); 1313 if (inbuf) 1314 free(inbuf); 1315 if (cpage) 1316 free(cpage); 1317 if (dpage) 1318 free(dpage); 1319 if (streams) 1320 free(streams); 1321 } 1322 1323 static void 1324 build_corefile(const char *namelist, const char *corefile) 1325 { 1326 size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t); 1327 size_t ksyms_size = dumphdr.dump_ksyms_size; 1328 size_t ksyms_csize = dumphdr.dump_ksyms_csize; 1329 pfn_t *pfn_table; 1330 char *ksyms_base = Zalloc(ksyms_size); 1331 char *ksyms_cbase = Zalloc(ksyms_csize); 1332 size_t ksyms_dsize; 1333 Stat_t st; 1334 int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1335 int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1336 1337 (void) printf("Constructing namelist %s/%s\n", savedir, namelist); 1338 1339 /* 1340 * Determine the optimum write size for the core file 1341 */ 1342 Fstat(corefd, &st, corefile); 1343 1344 if (verbose > 1) 1345 (void) printf("%s: %ld block size\n", corefile, 1346 (long)st.st_blksize); 1347 coreblksize = st.st_blksize; 1348 if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize)) 1349 coreblksize = MINCOREBLKSIZE; 1350 1351 hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1); 1352 1353 /* 1354 * This dump file is now uncompressed 1355 */ 1356 corehdr.dump_flags &= ~DF_COMPRESSED; 1357 1358 /* 1359 * Read in the compressed symbol table, copy it to corefile, 1360 * decompress it, and write the result to namelist. 1361 */ 1362 corehdr.dump_ksyms = pagesize; 1363 Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms); 1364 Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms); 1365 1366 ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, 1367 ksyms_size); 1368 if (ksyms_dsize != ksyms_size) 1369 logprint(SC_SL_WARN, 1370 "bad data in symbol table, %lu of %lu bytes saved", 1371 ksyms_dsize, ksyms_size); 1372 1373 Pwrite(namefd, ksyms_base, ksyms_size, 0); 1374 (void) close(namefd); 1375 free(ksyms_cbase); 1376 free(ksyms_base); 1377 1378 (void) printf("Constructing corefile %s/%s\n", savedir, corefile); 1379 1380 /* 1381 * Read in and write out the pfn table. 1382 */ 1383 pfn_table = Zalloc(pfn_table_size); 1384 corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize); 1385 Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn); 1386 Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn); 1387 1388 /* 1389 * Convert the raw translation data into a hashed dump map. 1390 */ 1391 corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize); 1392 build_dump_map(corefd, pfn_table); 1393 free(pfn_table); 1394 1395 /* 1396 * Decompress the pages 1397 */ 1398 decompress_pages(corefd); 1399 (void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved, 1400 dumphdr.dump_npages); 1401 1402 if (verbose) 1403 (void) printf("%ld (%ld%%) zero pages were not written\n", 1404 (pgcnt_t)zpages, (pgcnt_t)zpages * 100 / 1405 dumphdr.dump_npages); 1406 1407 if (saved != dumphdr.dump_npages) 1408 logprint(SC_SL_WARN, "bad data after page %ld", saved); 1409 1410 /* 1411 * Write out the modified dump headers. 1412 */ 1413 Pwrite(corefd, &corehdr, sizeof (corehdr), 0); 1414 if (!filemode) 1415 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 1416 1417 (void) close(corefd); 1418 } 1419 1420 /* 1421 * When the system panics, the kernel saves all undelivered messages (messages 1422 * that never made it out to syslogd(1M)) in the dump. At a mimimum, the 1423 * panic message itself will always fall into this category. Upon reboot, 1424 * the syslog startup script runs savecore -m to recover these messages. 1425 * 1426 * To do this, we read the unsent messages from the dump and send them to 1427 * /dev/conslog on priority band 1. This has the effect of prepending them 1428 * to any already-accumulated messages in the console backlog, thus preserving 1429 * temporal ordering across the reboot. 1430 * 1431 * Note: since savecore -m is used *only* for this purpose, it does *not* 1432 * attempt to save the crash dump. The dump will be saved later, after 1433 * syslogd(1M) starts, by the savecore startup script. 1434 */ 1435 static int 1436 message_save(void) 1437 { 1438 offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE); 1439 offset_t ldoff; 1440 log_dump_t ld; 1441 log_ctl_t lc; 1442 struct strbuf ctl, dat; 1443 int logfd; 1444 1445 logfd = Open("/dev/conslog", O_WRONLY, 0644); 1446 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); 1447 dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET; 1448 1449 ctl.buf = (void *)&lc; 1450 ctl.len = sizeof (log_ctl_t); 1451 1452 dat.buf = Zalloc(DUMP_LOGSIZE); 1453 1454 for (;;) { 1455 ldoff = dumpoff; 1456 1457 Pread(dumpfd, &ld, sizeof (log_dump_t), dumpoff); 1458 dumpoff += sizeof (log_dump_t); 1459 dat.len = ld.ld_msgsize; 1460 1461 if (ld.ld_magic == 0) 1462 break; 1463 1464 if (ld.ld_magic != LOG_MAGIC) 1465 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR, 1466 "bad magic %x", ld.ld_magic); 1467 1468 if (dat.len >= DUMP_LOGSIZE) 1469 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR, 1470 "bad size %d", ld.ld_msgsize); 1471 1472 Pread(dumpfd, ctl.buf, ctl.len, dumpoff); 1473 dumpoff += ctl.len; 1474 1475 if (ld.ld_csum != checksum32(ctl.buf, ctl.len)) 1476 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK, 1477 "bad log_ctl checksum"); 1478 1479 lc.flags |= SL_LOGONLY; 1480 1481 Pread(dumpfd, dat.buf, dat.len, dumpoff); 1482 dumpoff += dat.len; 1483 1484 if (ld.ld_msum != checksum32(dat.buf, dat.len)) 1485 logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK, 1486 "bad message checksum"); 1487 1488 if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1) 1489 logprint(SC_SL_ERR | SC_EXIT_ERR, "putpmsg: %s", 1490 strerror(errno)); 1491 1492 ld.ld_magic = 0; /* clear magic so we never save twice */ 1493 Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff); 1494 } 1495 return (0); 1496 } 1497 1498 static long 1499 getbounds(const char *f) 1500 { 1501 long b = -1; 1502 const char *p = strrchr(f, '/'); 1503 1504 (void) sscanf(p ? p + 1 : f, "vmdump.%ld", &b); 1505 return (b); 1506 } 1507 1508 static void 1509 stack_retrieve(char *stack) 1510 { 1511 summary_dump_t sd; 1512 offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE + 1513 DUMP_ERPTSIZE); 1514 dumpoff -= DUMP_SUMMARYSIZE; 1515 1516 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); 1517 dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET; 1518 1519 Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff); 1520 dumpoff += sizeof (summary_dump_t); 1521 1522 if (sd.sd_magic == 0) { 1523 *stack = '\0'; 1524 return; 1525 } 1526 1527 if (sd.sd_magic != SUMMARY_MAGIC) { 1528 *stack = '\0'; 1529 logprint(SC_SL_NONE | SC_IF_VERBOSE, 1530 "bad summary magic %x", sd.sd_magic); 1531 return; 1532 } 1533 Pread(dumpfd, stack, STACK_BUF_SIZE, dumpoff); 1534 if (sd.sd_ssum != checksum32(stack, STACK_BUF_SIZE)) 1535 logprint(SC_SL_NONE | SC_IF_VERBOSE, "bad stack checksum"); 1536 } 1537 1538 static void 1539 raise_event(enum sc_event_type evidx, char *warn_string) 1540 { 1541 uint32_t pl = sc_event[evidx].sce_payload; 1542 char panic_stack[STACK_BUF_SIZE]; 1543 nvlist_t *attr = NULL; 1544 char uuidbuf[36 + 1]; 1545 int err = 0; 1546 1547 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0) 1548 goto publish; /* try to send payload-free event */ 1549 1550 if (pl & SC_PAYLOAD_SAVEDIR && savedir != NULL) 1551 err |= nvlist_add_string(attr, "dumpdir", savedir); 1552 1553 if (pl & SC_PAYLOAD_INSTANCE && bounds != -1) 1554 err |= nvlist_add_int64(attr, "instance", bounds); 1555 1556 if (pl & SC_PAYLOAD_ISCOMPRESSED) { 1557 err |= nvlist_add_boolean_value(attr, "compressed", 1558 csave ? B_TRUE : B_FALSE); 1559 } 1560 1561 if (pl & SC_PAYLOAD_DUMPADM_EN) { 1562 char *disabled = defread("DUMPADM_ENABLE=no"); 1563 1564 err |= nvlist_add_boolean_value(attr, "savecore-enabled", 1565 disabled ? B_FALSE : B_TRUE); 1566 } 1567 1568 if (pl & SC_PAYLOAD_IMAGEUUID) { 1569 (void) strncpy(uuidbuf, corehdr.dump_uuid, 36); 1570 uuidbuf[36] = '\0'; 1571 err |= nvlist_add_string(attr, "os-instance-uuid", uuidbuf); 1572 } 1573 1574 if (pl & SC_PAYLOAD_CRASHTIME) { 1575 err |= nvlist_add_int64(attr, "crashtime", 1576 (int64_t)corehdr.dump_crashtime); 1577 } 1578 1579 if (pl & SC_PAYLOAD_PANICSTR && corehdr.dump_panicstring[0] != '\0') { 1580 err |= nvlist_add_string(attr, "panicstr", 1581 corehdr.dump_panicstring); 1582 } 1583 1584 if (pl & SC_PAYLOAD_PANICSTACK) { 1585 stack_retrieve(panic_stack); 1586 1587 if (panic_stack[0] != '\0') { 1588 /* 1589 * The summary page may not be present if the dump 1590 * was previously recorded compressed. 1591 */ 1592 (void) nvlist_add_string(attr, "panicstack", 1593 panic_stack); 1594 } 1595 } 1596 1597 /* add warning string if this is an ireport for dump failure */ 1598 if (pl & SC_PAYLOAD_FAILREASON && warn_string != NULL) 1599 (void) nvlist_add_string(attr, "failure-reason", warn_string); 1600 1601 if (pl & SC_PAYLOAD_DUMPCOMPLETE) 1602 err |= nvlist_add_boolean_value(attr, "dump-incomplete", 1603 dump_incomplete ? B_TRUE : B_FALSE); 1604 1605 if (pl & SC_PAYLOAD_FM_PANIC) { 1606 err |= nvlist_add_boolean_value(attr, "fm-panic", 1607 fm_panic ? B_TRUE : B_FALSE); 1608 } 1609 1610 if (pl & SC_PAYLOAD_JUSTCHECKING) { 1611 err |= nvlist_add_boolean_value(attr, "will-attempt-savecore", 1612 cflag ? B_FALSE : B_TRUE); 1613 } 1614 1615 if (err) 1616 logprint(SC_SL_WARN, "Errors while constructing '%s' " 1617 "event payload; will try to publish anyway."); 1618 publish: 1619 if (fmev_rspublish_nvl(FMEV_RULESET_ON_SUNOS, 1620 "panic", sc_event[evidx].sce_subclass, FMEV_HIPRI, 1621 attr) != FMEV_SUCCESS) { 1622 logprint(SC_SL_ERR, "failed to publish '%s' event: %s", 1623 sc_event[evidx].sce_subclass, fmev_strerror(fmev_errno)); 1624 nvlist_free(attr); 1625 } 1626 1627 } 1628 1629 1630 int 1631 main(int argc, char *argv[]) 1632 { 1633 int i, c, bfd; 1634 Stat_t st; 1635 struct rlimit rl; 1636 long filebounds = -1; 1637 char namelist[30], corefile[30], boundstr[30]; 1638 1639 startts = gethrtime(); 1640 1641 (void) getrlimit(RLIMIT_NOFILE, &rl); 1642 rl.rlim_cur = rl.rlim_max; 1643 (void) setrlimit(RLIMIT_NOFILE, &rl); 1644 1645 openlog(progname, LOG_ODELAY, LOG_AUTH); 1646 1647 (void) defopen("/etc/dumpadm.conf"); 1648 savedir = defread("DUMPADM_SAVDIR="); 1649 if (savedir != NULL) 1650 savedir = strdup(savedir); 1651 1652 while ((c = getopt(argc, argv, "Lvcdmf:")) != EOF) { 1653 switch (c) { 1654 case 'L': 1655 livedump++; 1656 break; 1657 case 'v': 1658 verbose++; 1659 break; 1660 case 'c': 1661 cflag++; 1662 break; 1663 case 'd': 1664 disregard_valid_flag++; 1665 break; 1666 case 'm': 1667 mflag++; 1668 break; 1669 case 'f': 1670 dumpfile = optarg; 1671 filebounds = getbounds(dumpfile); 1672 break; 1673 case '?': 1674 usage(); 1675 } 1676 } 1677 1678 if (geteuid() != 0 && filebounds < 0) { 1679 (void) fprintf(stderr, "%s: %s %s\n", progname, 1680 gettext("you must be root to use"), progname); 1681 exit(1); 1682 } 1683 1684 interactive = isatty(STDOUT_FILENO); 1685 1686 if (cflag && livedump) 1687 usage(); 1688 1689 if (dumpfile == NULL || livedump) 1690 dumpfd = Open("/dev/dump", O_RDONLY, 0444); 1691 1692 if (dumpfile == NULL) { 1693 dumpfile = Zalloc(MAXPATHLEN); 1694 if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) 1695 logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR, 1696 "no dump device configured"); 1697 } 1698 1699 if (mflag) 1700 return (message_save()); 1701 1702 if (optind == argc - 1) 1703 savedir = argv[optind]; 1704 1705 if (savedir == NULL || optind < argc - 1) 1706 usage(); 1707 1708 if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1) 1709 logprint(SC_SL_NONE | SC_EXIT_ERR, 1710 "dedicated dump device required"); 1711 1712 (void) close(dumpfd); 1713 dumpfd = -1; 1714 1715 Stat(dumpfile, &st); 1716 1717 filemode = S_ISREG(st.st_mode); 1718 1719 if (!filemode && defread("DUMPADM_CSAVE=off") == NULL) 1720 csave = 1; 1721 1722 read_dumphdr(); 1723 1724 /* 1725 * We want this message to go to the log file, but not the console. 1726 * There's no good way to do that with the existing syslog facility. 1727 * We could extend it to handle this, but there doesn't seem to be 1728 * a general need for it, so we isolate the complexity here instead. 1729 */ 1730 if (dumphdr.dump_panicstring[0] != '\0') { 1731 int logfd = Open("/dev/conslog", O_WRONLY, 0644); 1732 log_ctl_t lc; 1733 struct strbuf ctl, dat; 1734 char msg[DUMP_PANICSIZE + 100]; 1735 char fmt[] = "reboot after panic: %s"; 1736 uint32_t msgid; 1737 1738 STRLOG_MAKE_MSGID(fmt, msgid); 1739 1740 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */ 1741 (void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ", 1742 progname, msgid); 1743 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 1744 (void) sprintf(msg + strlen(msg), fmt, 1745 dumphdr.dump_panicstring); 1746 1747 lc.pri = LOG_AUTH | LOG_ERR; 1748 lc.flags = SL_CONSOLE | SL_LOGONLY; 1749 lc.level = 0; 1750 1751 ctl.buf = (void *)&lc; 1752 ctl.len = sizeof (log_ctl_t); 1753 1754 dat.buf = (void *)msg; 1755 dat.len = strlen(msg) + 1; 1756 1757 (void) putmsg(logfd, &ctl, &dat, 0); 1758 (void) close(logfd); 1759 } 1760 1761 if ((dumphdr.dump_flags & DF_COMPLETE) == 0) { 1762 logprint(SC_SL_WARN, "incomplete dump on dump device"); 1763 dump_incomplete = B_TRUE; 1764 } 1765 1766 if (dumphdr.dump_fm_panic) 1767 fm_panic = B_TRUE; 1768 1769 /* 1770 * We have a valid dump on a dump device and know as much about 1771 * it as we're going to at this stage. Raise an event for 1772 * logging and so that FMA can open a case for this panic. 1773 * Avoid this step for FMA-initiated panics - FMA will replay 1774 * ereports off the dump device independently of savecore and 1775 * will make a diagnosis, so we don't want to open two cases 1776 * for the same event. Also avoid raising an event for a 1777 * livedump, or when we inflating a compressed dump. 1778 */ 1779 if (!fm_panic && !livedump && !filemode) 1780 raise_event(SC_EVENT_DUMP_PENDING, NULL); 1781 1782 logprint(SC_SL_WARN, "System dump time: %s", 1783 ctime(&dumphdr.dump_crashtime)); 1784 1785 /* 1786 * Option -c is designed for use from svc-dumpadm where we know 1787 * that dumpadm -n is in effect but run savecore -c just to 1788 * get the above dump_pending_on_device event raised. If it is run 1789 * interactively then just print further panic details. 1790 */ 1791 if (cflag) { 1792 char *disabled = defread("DUMPADM_ENABLE=no"); 1793 int lvl = interactive ? SC_SL_WARN : SC_SL_ERR; 1794 int ec = fm_panic ? SC_EXIT_FM : SC_EXIT_PEND; 1795 1796 logprint(lvl | ec, 1797 "Panic crashdump pending on dump device%s " 1798 "run savecore(1M) manually to extract. " 1799 "Image UUID %s%s.", 1800 disabled ? " but dumpadm -n in effect;" : ";", 1801 corehdr.dump_uuid, 1802 fm_panic ? "(fault-management initiated)" : ""); 1803 /*NOTREACHED*/ 1804 } 1805 1806 if (chdir(savedir) == -1) 1807 logprint(SC_SL_ERR | SC_EXIT_ERR, "chdir(\"%s\"): %s", 1808 savedir, strerror(errno)); 1809 1810 check_space(csave); 1811 1812 if (filebounds < 0) 1813 bounds = read_number_from_file("bounds", 0); 1814 else 1815 bounds = filebounds; 1816 1817 if (csave) { 1818 size_t metrics_size = datahdr.dump_metrics; 1819 1820 (void) sprintf(corefile, "vmdump.%ld", bounds); 1821 1822 datahdr.dump_metrics = 0; 1823 1824 logprint(SC_SL_ERR, 1825 "Saving compressed system crash dump in %s/%s", 1826 savedir, corefile); 1827 1828 copy_crashfile(corefile); 1829 1830 /* 1831 * Raise a fault management event that indicates the system 1832 * has panicked. We know a reasonable amount about the 1833 * condition at this time, but the dump is still compressed. 1834 */ 1835 if (!livedump && !fm_panic) 1836 raise_event(SC_EVENT_DUMP_AVAILABLE, NULL); 1837 1838 if (metrics_size > 0) { 1839 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; 1840 FILE *mfile = fopen(METRICSFILE, "a"); 1841 char *metrics = Zalloc(metrics_size + 1); 1842 1843 Pread(dumpfd, metrics, metrics_size, endoff + 1844 sizeof (dumphdr) + sizeof (datahdr)); 1845 1846 if (sec < 1) 1847 sec = 1; 1848 1849 if (mfile == NULL) { 1850 logprint(SC_SL_WARN, 1851 "Can't create %s:\n%s", 1852 METRICSFILE, metrics); 1853 } else { 1854 (void) fprintf(mfile, "[[[[,,,"); 1855 for (i = 0; i < argc; i++) 1856 (void) fprintf(mfile, "%s ", argv[i]); 1857 (void) fprintf(mfile, "\n"); 1858 (void) fprintf(mfile, ",,,%s %s %s %s %s\n", 1859 dumphdr.dump_utsname.sysname, 1860 dumphdr.dump_utsname.nodename, 1861 dumphdr.dump_utsname.release, 1862 dumphdr.dump_utsname.version, 1863 dumphdr.dump_utsname.machine); 1864 (void) fprintf(mfile, ",,,%s dump time %s\n", 1865 dumphdr.dump_flags & DF_LIVE ? "Live" : 1866 "Crash", ctime(&dumphdr.dump_crashtime)); 1867 (void) fprintf(mfile, ",,,%s/%s\n", savedir, 1868 corefile); 1869 (void) fprintf(mfile, "Metrics:\n%s\n", 1870 metrics); 1871 (void) fprintf(mfile, "Copy pages,%ld\n", 1872 dumphdr. dump_npages); 1873 (void) fprintf(mfile, "Copy time,%d\n", sec); 1874 (void) fprintf(mfile, "Copy pages/sec,%ld\n", 1875 dumphdr.dump_npages / sec); 1876 (void) fprintf(mfile, "]]]]\n"); 1877 (void) fclose(mfile); 1878 } 1879 free(metrics); 1880 } 1881 1882 logprint(SC_SL_ERR, 1883 "Decompress the crash dump with " 1884 "\n'savecore -vf %s/%s'", 1885 savedir, corefile); 1886 1887 } else { 1888 (void) sprintf(namelist, "unix.%ld", bounds); 1889 (void) sprintf(corefile, "vmcore.%ld", bounds); 1890 1891 if (interactive && filebounds >= 0 && access(corefile, F_OK) 1892 == 0) 1893 logprint(SC_SL_NONE | SC_EXIT_ERR, 1894 "%s already exists: remove with " 1895 "'rm -f %s/{unix,vmcore}.%ld'", 1896 corefile, savedir, bounds); 1897 1898 logprint(SC_SL_ERR, 1899 "saving system crash dump in %s/{unix,vmcore}.%ld", 1900 savedir, bounds); 1901 1902 build_corefile(namelist, corefile); 1903 1904 if (!livedump && !filemode && !fm_panic) 1905 raise_event(SC_EVENT_DUMP_AVAILABLE, NULL); 1906 1907 if (access(METRICSFILE, F_OK) == 0) { 1908 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; 1909 FILE *mfile = fopen(METRICSFILE, "a"); 1910 1911 if (sec < 1) 1912 sec = 1; 1913 1914 (void) fprintf(mfile, "[[[[,,,"); 1915 for (i = 0; i < argc; i++) 1916 (void) fprintf(mfile, "%s ", argv[i]); 1917 (void) fprintf(mfile, "\n"); 1918 (void) fprintf(mfile, ",,,%s/%s\n", savedir, corefile); 1919 (void) fprintf(mfile, ",,,%s %s %s %s %s\n", 1920 dumphdr.dump_utsname.sysname, 1921 dumphdr.dump_utsname.nodename, 1922 dumphdr.dump_utsname.release, 1923 dumphdr.dump_utsname.version, 1924 dumphdr.dump_utsname.machine); 1925 (void) fprintf(mfile, "Uncompress pages,%"PRIu64"\n", 1926 saved); 1927 (void) fprintf(mfile, "Uncompress time,%d\n", sec); 1928 (void) fprintf(mfile, "Uncompress pages/sec,%" 1929 PRIu64"\n", saved / sec); 1930 (void) fprintf(mfile, "]]]]\n"); 1931 (void) fclose(mfile); 1932 } 1933 } 1934 1935 if (filebounds < 0) { 1936 (void) sprintf(boundstr, "%ld\n", bounds + 1); 1937 bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); 1938 Pwrite(bfd, boundstr, strlen(boundstr), 0); 1939 (void) close(bfd); 1940 } 1941 1942 if (verbose) { 1943 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; 1944 1945 (void) printf("%d:%02d dump %s is done\n", 1946 sec / 60, sec % 60, 1947 csave ? "copy" : "decompress"); 1948 } 1949 1950 if (verbose > 1 && hist != NULL) { 1951 int i, nw; 1952 1953 for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i) 1954 nw += hist[i] * i; 1955 (void) printf("pages count %%\n"); 1956 for (i = 0; i <= BTOP(coreblksize); ++i) { 1957 if (hist[i] == 0) 1958 continue; 1959 (void) printf("%3d %5u %6.2f\n", 1960 i, hist[i], 100.0 * hist[i] * i / nw); 1961 } 1962 } 1963 1964 (void) close(dumpfd); 1965 dumpfd = -1; 1966 1967 return (0); 1968 }