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