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