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