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