diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/dumpadm/dconf.c | 40 | ||||
-rw-r--r-- | usr/src/cmd/dumpadm/dconf.h | 12 | ||||
-rw-r--r-- | usr/src/cmd/dumpadm/main.c | 14 | ||||
-rw-r--r-- | usr/src/cmd/file/file.c | 34 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_kvm.c | 14 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_main.c | 40 | ||||
-rw-r--r-- | usr/src/cmd/savecore/Makefile.com | 26 | ||||
-rw-r--r-- | usr/src/cmd/savecore/savecore.c | 1252 |
8 files changed, 1306 insertions, 126 deletions
diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c index 3cd5b725da..f4e59563f9 100644 --- a/usr/src/cmd/dumpadm/dconf.c +++ b/usr/src/cmd/dumpadm/dconf.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/stat.h> #include <sys/swap.h> @@ -56,15 +54,19 @@ static int print_device(const dumpconf_t *, FILE *); static int print_savdir(const dumpconf_t *, FILE *); static int print_content(const dumpconf_t *, FILE *); static int print_enable(const dumpconf_t *, FILE *); +static int print_csave(const dumpconf_t *, FILE *); static const dc_token_t tokens[] = { { "DUMPADM_DEVICE", dconf_str2device, print_device }, { "DUMPADM_SAVDIR", dconf_str2savdir, print_savdir }, { "DUMPADM_CONTENT", dconf_str2content, print_content }, { "DUMPADM_ENABLE", dconf_str2enable, print_enable }, + { "DUMPADM_CSAVE", dconf_str2csave, print_csave }, { NULL, NULL, NULL } }; +static const char DC_STR_ON[] = "on"; /* On string */ +static const char DC_STR_OFF[] = "off"; /* Off string */ static const char DC_STR_YES[] = "yes"; /* Enable on string */ static const char DC_STR_NO[] = "no"; /* Enable off string */ static const char DC_STR_SWAP[] = "swap"; /* Default dump device */ @@ -100,10 +102,12 @@ dconf_init(dumpconf_t *dcp, int dcmode) } /* - * Default is contents kernel, and savecore enabled on reboot. + * Default is contents kernel, savecore enabled on reboot, + * savecore saves compressed core files. */ dcp->dc_cflags = DUMP_KERNEL; dcp->dc_enable = DC_ON; + dcp->dc_csave = DC_COMPRESSED; dcp->dc_mode = dcmode; dcp->dc_conf_fp = NULL; @@ -529,6 +533,10 @@ dconf_print(dumpconf_t *dcp, FILE *fp) (void) fprintf(fp, gettext(" Savecore enabled: %s\n"), (dcp->dc_enable == DC_OFF) ? gettext("no") : gettext("yes")); + + (void) fprintf(fp, gettext(" Save compressed: %s\n"), + (dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") : + gettext("on")); } int @@ -598,6 +606,23 @@ dconf_str2enable(dumpconf_t *dcp, char *buf) return (-1); } +int +dconf_str2csave(dumpconf_t *dcp, char *buf) +{ + if (strcasecmp(buf, DC_STR_ON) == 0) { + dcp->dc_csave = DC_COMPRESSED; + return (0); + } + + if (strcasecmp(buf, DC_STR_OFF) == 0) { + dcp->dc_csave = DC_UNCOMPRESSED; + return (0); + } + + warn(gettext("invalid save compressed value -- %s\n"), buf); + return (-1); +} + static int print_content(const dumpconf_t *dcp, FILE *fp) { @@ -628,6 +653,13 @@ print_enable(const dumpconf_t *dcp, FILE *fp) } static int +print_csave(const dumpconf_t *dcp, FILE *fp) +{ + return (fprintf(fp, "%s\n", (dcp->dc_csave == DC_COMPRESSED) ? + DC_STR_ON : DC_STR_OFF)); +} + +static int print_savdir(const dumpconf_t *dcp, FILE *fp) { return (fprintf(fp, "%s\n", dcp->dc_savdir)); diff --git a/usr/src/cmd/dumpadm/dconf.h b/usr/src/cmd/dumpadm/dconf.h index ccf0943df9..a74e0a0c20 100644 --- a/usr/src/cmd/dumpadm/dconf.h +++ b/usr/src/cmd/dumpadm/dconf.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DCONF_H #define _DCONF_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/param.h> #include <stdio.h> @@ -41,6 +39,7 @@ typedef struct dumpconf { char dc_savdir[MAXPATHLEN]; /* Savecore dir path */ int dc_cflags; /* Config flags (see <sys/dumpadm.h>) */ int dc_enable; /* Run savecore on boot? (see below) */ + int dc_csave; /* Save dump compressed? (see below) */ int dc_mode; /* Mode flags (see below) */ FILE *dc_conf_fp; /* File pointer for config file */ int dc_conf_fd; /* File descriptor for config file */ @@ -55,6 +54,12 @@ typedef struct dumpconf { #define DC_ON 1 /* Savecore enabled */ /* + * Values for dc_csave (savecore compressed) property: + */ +#define DC_UNCOMPRESSED 0 /* Savecore uncompresses the dump */ +#define DC_COMPRESSED 1 /* Savecore leaves dump compressed */ + +/* * Values for dconf_open mode: */ #define DC_CURRENT 1 /* Kernel overrides file settings */ @@ -71,6 +76,7 @@ extern int dconf_str2device(dumpconf_t *, char *); extern int dconf_str2savdir(dumpconf_t *, char *); extern int dconf_str2content(dumpconf_t *, char *); extern int dconf_str2enable(dumpconf_t *, char *); +extern int dconf_str2csave(dumpconf_t *, char *); #ifdef __cplusplus } diff --git a/usr/src/cmd/dumpadm/main.c b/usr/src/cmd/dumpadm/main.c index 7fa72af9c1..48cf9d692b 100644 --- a/usr/src/cmd/dumpadm/main.c +++ b/usr/src/cmd/dumpadm/main.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/stat.h> #include <locale.h> #include <unistd.h> @@ -37,9 +35,9 @@ static const char USAGE[] = "\ Usage: %s [-nuy] [-c kernel | curproc | all ] [-d dump-device | swap ]\n\ - [-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir]\n"; + [-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir] [-z on|off]\n"; -static const char OPTS[] = "nuyc:d:m:s:r:"; +static const char OPTS[] = "nuyc:d:m:s:r:z:"; static const char PATH_DEVICE[] = "/dev/dump"; static const char PATH_CONFIG[] = "/etc/dumpadm.conf"; @@ -148,6 +146,12 @@ main(int argc, char *argv[]) dc.dc_enable = DC_ON; modified++; break; + + case 'z': + if (dconf_str2csave(&dc, optarg) == -1) + return (E_USAGE); + modified++; + break; } } } diff --git a/usr/src/cmd/file/file.c b/usr/src/cmd/file/file.c index 3bfe6280ce..70cb43c005 100644 --- a/usr/src/cmd/file/file.c +++ b/usr/src/cmd/file/file.c @@ -26,12 +26,10 @@ /* All Rights Reserved */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #define _LARGEFILE64_SOURCE /* Get definitions for the relocation types supported. */ @@ -1142,14 +1140,14 @@ print_elf_flags(Elf_Info EI) (void) printf("%s", gettext(", V8+ Required")); if (flags & EF_SPARC_SUN_US3) { (void) printf("%s", - gettext(", UltraSPARC3 Extensions Required")); + gettext(", UltraSPARC3 Extensions Required")); } else if (flags & EF_SPARC_SUN_US1) { (void) printf("%s", - gettext(", UltraSPARC1 Extensions Required")); + gettext(", UltraSPARC1 Extensions Required")); } if (flags & EF_SPARC_HAL_R1) (void) printf("%s", - gettext(", HaL R1 Extensions Required")); + gettext(", HaL R1 Extensions Required")); break; default: break; @@ -1319,7 +1317,8 @@ lookup(char **tab) i++; for (j = 0; tab[j] != 0; j++) { l = 0; - for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++); + for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++) + ; if (r == '\0') if (fbuf[k] == ' ' || fbuf[k] == '\n' || fbuf[k] == '\t' || fbuf[k] == '{' || @@ -1626,11 +1625,16 @@ print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t), */ if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t), (off_t)0) == sizeof (dumphdr_t)) { + const char *c = swap(dh.dump_flags) & DF_COMPRESSED ? + "compressed " : ""; + const char *l = swap(dh.dump_flags) & DF_LIVE ? + "live" : "crash"; + (void) printf(gettext( - "%s %s %s %u-bit %s crash dump from '%s'\n"), + "%s %s %s %u-bit %s %s%s dump from '%s'\n"), dh.dump_utsname.sysname, dh.dump_utsname.release, dh.dump_utsname.version, swap(dh.dump_wordsize), isa, - dh.dump_utsname.nodename); + c, l, dh.dump_utsname.nodename); } else { (void) printf(gettext("SunOS %u-bit %s crash dump\n"), swap(dhp->dump_wordsize), isa); @@ -1641,11 +1645,11 @@ static void usage(void) { (void) fprintf(stderr, gettext( - "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n" - " file [-dh] [-M mfile] [-m mfile] -f ffile\n" - " file -i [-h] [-f ffile] file ...\n" - " file -i [-h] -f ffile\n" - " file -c [-d] [-M mfile] [-m mfile]\n")); + "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n" + " file [-dh] [-M mfile] [-m mfile] -f ffile\n" + " file -i [-h] [-f ffile] file ...\n" + " file -i [-h] -f ffile\n" + " file -c [-d] [-M mfile] [-m mfile]\n")); exit(2); } @@ -1683,7 +1687,7 @@ is_in_list(char *str) */ for (i = 0; debug_sections[i] != NULL; i++) { if (strncmp(debug_sections[i], str, - strlen(debug_sections[i])) == 0) { + strlen(debug_sections[i])) == 0) { return (1); } } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c index 0c1cc673fa..5d0da621cd 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Libkvm Kernel Target * @@ -1563,3 +1561,13 @@ err: mdb_free(kt, sizeof (kt_data_t)); return (-1); } + +int +mdb_kvm_is_compressed_dump(mdb_io_t *io) +{ + dumphdr_t h; + + return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) && + h.dump_magic == DUMP_MAGIC && + (h.dump_flags & DF_COMPRESSED) != 0); +} diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c index b8c6144b80..1fb07f6893 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_main.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -401,6 +401,7 @@ identify_xvm_file(const char *file, int *longmode) int main(int argc, char *argv[], char *envp[]) { + extern int mdb_kvm_is_compressed_dump(mdb_io_t *); mdb_tgt_ctor_f *tgt_ctor = NULL; const char **tgt_argv = alloca(argc * sizeof (char *)); int tgt_argc = 0; @@ -821,6 +822,19 @@ main(int argc, char *argv[], char *envp[]) (void) strcpy((char *)tgt_argv[1], "vmcore."); (void) strcat((char *)tgt_argv[1], object); + if (access(tgt_argv[0], F_OK) == -1 && + access(tgt_argv[1], F_OK) == -1) { + (void) strcpy((char *)tgt_argv[1], "vmdump."); + (void) strcat((char *)tgt_argv[1], object); + if (access(tgt_argv[1], F_OK) == 0) { + mdb_iob_printf(mdb.m_err, + "cannot open compressed dump; " + "decompress using savecore -f %s\n", + tgt_argv[1]); + terminate(0); + } + } + tgt_argc = 2; } @@ -833,6 +847,20 @@ main(int argc, char *argv[], char *envp[]) die("failed to open %s", tgt_argv[0]); /* + * Check for a single vmdump.N compressed dump file, + * and give a helpful message. + */ + if (tgt_argc == 1) { + if (mdb_kvm_is_compressed_dump(io)) { + mdb_iob_printf(mdb.m_err, + "cannot open compressed dump; " + "decompress using savecore -f %s\n", + tgt_argv[0]); + terminate(0); + } + } + + /* * If the target is unknown or is not the rawfile target, do * a gelf_check to determine if the file is an ELF file. If * it is not and the target is unknown, use the rawfile tgt. @@ -889,6 +917,16 @@ main(int argc, char *argv[], char *envp[]) if (access(tgt_argv[1], F_OK) == -1) die("failed to access %s", tgt_argv[1]); + /* *.N case: drop vmdump.N from the list */ + if (tgt_argc == 3) { + if ((io = mdb_fdio_create_path(NULL, + tgt_argv[2], O_RDONLY, 0)) == NULL) + die("failed to open %s", tgt_argv[2]); + if (mdb_kvm_is_compressed_dump(io)) + tgt_argv[--tgt_argc] = NULL; + mdb_io_destroy(io); + } + if ((io = mdb_fdio_create_path(NULL, tgt_argv[1], O_RDONLY, 0)) == NULL) die("failed to open %s", tgt_argv[1]); diff --git a/usr/src/cmd/savecore/Makefile.com b/usr/src/cmd/savecore/Makefile.com index 4b9faf5683..32bb2ea7a2 100644 --- a/usr/src/cmd/savecore/Makefile.com +++ b/usr/src/cmd/savecore/Makefile.com @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# PROG= savecore SRCS= ../savecore.c ../../../uts/common/os/compress.c @@ -33,27 +31,39 @@ include ../../Makefile.cmd CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) -CPPFLAGS += -D_LARGEFILE64_SOURCE=1 +CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -DBZ_NO_STDIO -I$(SRC)/uts/common + +BZIP2OBJS = bz2blocksort.o \ + bz2compress.o \ + bz2decompress.o \ + bz2randtable.o \ + bz2bzlib.o \ + bz2crctable.o \ + bz2huffman.o .KEEP_STATE: all: $(PROG) -$(PROG): $(OBJS) - $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) +$(PROG): $(OBJS) $(BZIP2OBJS) + $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(LDLIBS) $(POST_PROCESS) clean: - $(RM) $(OBJS) + $(RM) $(OBJS) $(BZIP2OBJS) lint: lint_SRCS include ../../Makefile.targ %.o: ../%.c - $(COMPILE.c) $< + $(COMPILE.c) -I$(SRC)/common $< $(POST_PROCESS_O) %.o: ../../../uts/common/os/%.c $(COMPILE.c) $< $(POST_PROCESS_O) + +bz2%.o: ../../../common/bzip2/%.c + $(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/bzip2 $< + $(POST_PROCESS_O) diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c index da8d6ee441..62dd900ce0 100644 --- a/usr/src/cmd/savecore/savecore.c +++ b/usr/src/cmd/savecore/savecore.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -36,16 +34,32 @@ #include <time.h> #include <syslog.h> #include <stropts.h> +#include <pthread.h> +#include <limits.h> +#include <atomic.h> #include <sys/mem.h> #include <sys/statvfs.h> #include <sys/dumphdr.h> #include <sys/dumpadm.h> #include <sys/compress.h> #include <sys/sysmacros.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <bzip2/bzlib.h> + +/* fread/fwrite buffer size */ +#define FBUFSIZE (1ULL << 20) + +/* minimum size for output buffering */ +#define MINCOREBLKSIZE (1ULL << 17) + +/* create this file if metrics collection is enabled in the kernel */ +#define METRICSFILE "METRICS.csv" static char progname[9] = "savecore"; static char *savedir; /* savecore directory */ static char *dumpfile; /* source of raw crash dump */ +static long bounds; /* numeric suffix */ static long pagesize; /* dump pagesize */ static int dumpfd = -1; /* dumpfile descriptor */ static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */ @@ -53,6 +67,15 @@ static offset_t endoff; /* offset of end-of-dump header */ static int verbose; /* chatty mode */ static int disregard_valid_flag; /* disregard valid flag */ static int livedump; /* dump the current running system */ +static int interactive; /* user invoked; no syslog */ +static int csave; /* save dump compressed */ +static int filemode; /* processing file, not dump device */ +static int percent_done; /* progress indicator */ +static hrtime_t startts; /* timestamp at start */ +static volatile uint64_t saved; /* count of pages written */ +static volatile uint64_t zpages; /* count of zero pages not written */ +static dumpdatahdr_t datahdr; /* compression info */ +static long coreblksize; /* preferred write size (st_blksize) */ static void usage(void) @@ -70,9 +93,9 @@ logprint(int logpri, int showmsg, int exitcode, char *message, ...) if (showmsg) { va_start(args, message); - (void) vsnprintf(buf, 1024, message, args); + (void) vsnprintf(buf, sizeof (buf), message, args); (void) fprintf(stderr, "%s: %s\n", progname, buf); - if (logpri >= 0) + if (!interactive && logpri >= 0) syslog(logpri, buf); va_end(args); } @@ -95,14 +118,60 @@ Open(const char *name, int oflags, mode_t mode) } static void +Fread(void *buf, size_t size, FILE *f) +{ + if (fread(buf, size, 1, f) != 1) + logprint(LOG_ERR, 1, 1, "fread: ferror %d feof %d", + ferror(f), feof(f)); +} + +static void +Fwrite(void *buf, size_t size, FILE *f) +{ + if (fwrite(buf, size, 1, f) != 1) + logprint(LOG_ERR, 1, 1, "fwrite: %s", strerror(errno)); +} + +static void +Fseek(offset_t off, FILE *f) +{ + if (fseeko64(f, off, SEEK_SET) != 0) + logprint(LOG_ERR, 1, 1, "fseeko64: %s", strerror(errno)); +} + +typedef struct stat64 Stat_t; + +static void +Fstat(int fd, Stat_t *sb, const char *fname) +{ + if (fstat64(fd, sb) != 0) + logprint(LOG_ERR, 1, 1, "fstat(\"%s\"): %s", fname, + strerror(errno)); +} + +static void +Stat(const char *fname, Stat_t *sb) +{ + if (stat64(fname, sb) != 0) + logprint(LOG_ERR, 1, 1, "stat(\"%s\"): %s", fname, + strerror(errno)); +} + +static void Pread(int fd, void *buf, size_t size, offset_t off) { - if (pread64(fd, buf, size, off) != size) - logprint(LOG_ERR, 1, 1, "pread: %s", strerror(errno)); + ssize_t sz = pread64(fd, buf, size, off); + + if (sz < 0) + logprint(LOG_ERR, 1, 1, + "pread: %s", strerror(errno)); + else if (sz != size) + logprint(LOG_ERR, 1, 1, + "pread: size %ld != %ld", sz, size); } static void -Pwrite(int fd, void *buf, size_t size, offset_t off) +Pwrite(int fd, void *buf, size_t size, off64_t off) { if (pwrite64(fd, buf, size, off) != size) logprint(LOG_ERR, 1, 1, "pwrite: %s", strerror(errno)); @@ -134,28 +203,44 @@ read_number_from_file(const char *filename, long default_value) static void read_dumphdr(void) { - dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); + if (filemode) + dumpfd = Open(dumpfile, O_RDONLY, 0644); + else + dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET; Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr)); pagesize = dumphdr.dump_pagesize; - if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) - logprint(-1, verbose, 0, "dump already processed"); - if (dumphdr.dump_magic != DUMP_MAGIC) - logprint(-1, verbose, 0, "bad magic number %x", + logprint(-1, 1, 0, "bad magic number %x", dumphdr.dump_magic); + if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) + logprint(-1, verbose, 0, "dump already processed"); + if (dumphdr.dump_version != DUMP_VERSION) logprint(-1, verbose, 0, "dump version (%d) != %s version (%d)", dumphdr.dump_version, progname, DUMP_VERSION); if (dumphdr.dump_wordsize != DUMP_WORDSIZE) - logprint(LOG_WARNING, 1, 0, + logprint(-1, 1, 0, "dump is from %u-bit kernel - cannot save on %u-bit kernel", dumphdr.dump_wordsize, DUMP_WORDSIZE); + + if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) { + if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION) + logprint(-1, verbose, 0, + "dump data version (%d) != %s data version (%d)", + datahdr.dump_datahdr_version, progname, + DUMP_DATAHDR_VERSION); + } else { + memset(&datahdr, 0, sizeof (datahdr)); + datahdr.dump_maxcsize = pagesize; + } + /* * Read the initial header, clear the valid bits, and compare headers. * The main header may have been overwritten by swapping if we're @@ -170,22 +255,28 @@ read_dumphdr(void) /* * Clear valid bit so we don't complain on every invocation. */ - Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); logprint(LOG_ERR, 1, 1, "initial dump header corrupt"); } } static void -check_space(void) +check_space(int csave) { struct statvfs fsb; - int64_t spacefree, dumpsize, minfree; + int64_t spacefree, dumpsize, minfree, datasize; if (statvfs(".", &fsb) < 0) logprint(LOG_ERR, 1, 1, "statvfs: %s", strerror(errno)); - dumpsize = (dumphdr.dump_data - dumphdr.dump_start) + - (int64_t)dumphdr.dump_npages * pagesize; + dumpsize = dumphdr.dump_data - dumphdr.dump_start; + datasize = dumphdr.dump_npages * pagesize; + if (!csave) + dumpsize += datasize; + else + dumpsize += datahdr.dump_data_csize; + spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize; minfree = 1024LL * read_number_from_file("minfree", 1024); if (spacefree < minfree + dumpsize) @@ -202,6 +293,11 @@ build_dump_map(int corefd, const pfn_t *pfn_table) size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t); mem_vtop_t vtop; dump_map_t *dmp = Zalloc(dump_mapsize); + char *inbuf = Zalloc(FBUFSIZE); + FILE *in = fdopen(dup(dumpfd), "rb"); + + setvbuf(in, inbuf, _IOFBF, FBUFSIZE); + Fseek(dumphdr.dump_map, in); corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize); @@ -212,9 +308,7 @@ build_dump_map(int corefd, const pfn_t *pfn_table) pfn_t pfn; uintptr_t h; - Pread(dumpfd, &vtop, sizeof (mem_vtop_t), - dumphdr.dump_map + i * sizeof (mem_vtop_t)); - + Fread(&vtop, sizeof (mem_vtop_t), in); while (last >= first) { middle = (first + last) / 2; pfn = pfn_table[middle]; @@ -245,30 +339,875 @@ build_dump_map(int corefd, const pfn_t *pfn_table) Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map); free(dmp); + fclose(in); + free(inbuf); +} + +/* + * Copy whole sections of the dump device to the file. + */ +static void +Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf, + size_t sz) +{ + size_t nr; + offset_t off = *offp; + + while (nb > 0) { + nr = sz < nb ? sz : (size_t)nb; + Pread(dumpfd, buf, nr, dumpoff); + Pwrite(fd, buf, nr, off); + off += nr; + dumpoff += nr; + nb -= nr; + } + *offp = off; +} + +/* + * Copy pages when the dump data header is missing. + * This supports older kernels with latest savecore. + */ +static void +CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz) +{ + uint32_t csize; + FILE *in = fdopen(dup(dumpfd), "rb"); + FILE *out = fdopen(dup(fd), "wb"); + char *cbuf = Zalloc(pagesize); + char *outbuf = Zalloc(FBUFSIZE); + pgcnt_t np = dumphdr.dump_npages; + + setvbuf(out, outbuf, _IOFBF, FBUFSIZE); + setvbuf(in, buf, _IOFBF, sz); + Fseek(dumphdr.dump_data, in); + + Fseek(*offp, out); + while (np > 0) { + Fread(&csize, sizeof (uint32_t), in); + Fwrite(&csize, sizeof (uint32_t), out); + *offp += sizeof (uint32_t); + if (csize > pagesize || csize == 0) { + logprint(LOG_ERR, 1, -1, + "CopyPages: page %lu csize %d (0x%x) pagesize %d", + dumphdr.dump_npages - np, csize, csize, + pagesize); + break; + } + Fread(cbuf, csize, in); + Fwrite(cbuf, csize, out); + *offp += csize; + np--; + } + fclose(in); + fclose(out); + free(outbuf); + free(buf); +} + +/* + * Concatenate dump contents into a new file. + * Update corehdr with new offsets. + */ +static void +copy_crashfile(const char *corefile) +{ + int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + size_t bufsz = FBUFSIZE; + char *inbuf = Zalloc(bufsz); + offset_t coreoff; + size_t nb; + + logprint(LOG_ERR, verbose, -1, + "Copying %s to %s/%s\n", dumpfile, savedir, corefile); + + /* + * This dump file is still compressed + */ + corehdr.dump_flags |= DF_COMPRESSED | DF_VALID; + + /* + * Leave room for corehdr, it is updated and written last + */ + corehdr.dump_start = 0; + coreoff = sizeof (corehdr); + + /* + * Read in the compressed symbol table, copy it to corefile. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_ksyms = coreoff; + Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd, + inbuf, bufsz); + + /* + * Save the pfn table. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_pfn = coreoff; + Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff, + corefd, inbuf, bufsz); + + /* + * Save the dump map. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_map = coreoff; + Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t), + &coreoff, corefd, inbuf, bufsz); + + /* + * Save the data pages. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_data = coreoff; + if (datahdr.dump_data_csize != 0) + Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff, + corefd, inbuf, bufsz); + else + CopyPages(dumphdr.dump_data, &coreoff, corefd, inbuf, bufsz); + + /* + * Now write the modified dump header to front and end of the copy. + * Make it look like a valid dump device. + * + * From dumphdr.h: Two headers are written out: one at the + * beginning of the dump, and the other at the very end of the + * dump device. The terminal header is at a known location + * (end of device) so we can always find it. + * + * Pad with zeros to each DUMP_OFFSET boundary. + */ + memset(inbuf, 0, DUMP_OFFSET); + + nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); + if (nb > 0) { + Pwrite(corefd, inbuf, nb, coreoff); + coreoff += nb; + } + + Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff); + coreoff += sizeof (corehdr); + + Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff); + coreoff += sizeof (datahdr); + + nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); + if (nb > 0) { + Pwrite(corefd, inbuf, nb, coreoff); + } + + free(inbuf); + Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start); + + /* + * Write out the modified dump header to the dump device. + * The dump device has been processed, so DF_VALID is clear. + */ + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + + (void) close(corefd); +} + +/* + * compressed streams + */ +typedef struct blockhdr blockhdr_t; +typedef struct block block_t; + +struct blockhdr { + block_t *head; + block_t *tail; +}; + +struct block { + block_t *next; + char *block; + int size; +}; + +typedef enum streamstate { + STREAMSTART, + STREAMPAGES +} streamstate_t; + +typedef struct stream { + streamstate_t state; + int init; + int tag; + int bound; + int nout; + char *blkbuf; + blockhdr_t blocks; + pgcnt_t pagenum; + pgcnt_t curpage; + pgcnt_t npages; + pgcnt_t done; + bz_stream strm; + dumpcsize_t sc; + dumpstreamhdr_t sh; +} stream_t; + +static stream_t *streams; +static stream_t *endstreams; + +const int cs = sizeof (dumpcsize_t); + +typedef struct tinfo { + pthread_t tid; + int corefd; +} tinfo_t; + +static int threads_stop; +static int threads_active; +static tinfo_t *tinfo; +static tinfo_t *endtinfo; + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER; + +static blockhdr_t freeblocks; + +static void +enqt(blockhdr_t *h, block_t *b) +{ + b->next = NULL; + if (h->tail == NULL) + h->head = b; + else + h->tail->next = b; + h->tail = b; +} + +static block_t * +deqh(blockhdr_t *h) +{ + block_t *b = h->head; + + if (b != NULL) { + h->head = b->next; + if (h->head == NULL) + h->tail = NULL; + } + return (b); +} + +static void *runstreams(void *arg); + +static void +initstreams(int corefd, int nstreams, int maxcsize) +{ + int nthreads; + int nblocks; + int i; + block_t *b; + tinfo_t *t; + + nthreads = sysconf(_SC_NPROCESSORS_ONLN); + if (nstreams < nthreads) + nthreads = nstreams; + if (nthreads < 1) + nthreads = 1; + nblocks = nthreads * 2; + + tinfo = Zalloc(nthreads * sizeof (tinfo_t)); + endtinfo = &tinfo[nthreads]; + + /* init streams */ + streams = Zalloc(nstreams * sizeof (stream_t)); + endstreams = &streams[nstreams]; + + /* init stream block buffers */ + for (i = 0; i < nblocks; i++) { + b = Zalloc(sizeof (block_t)); + b->block = Zalloc(maxcsize); + enqt(&freeblocks, b); + } + + /* init worker threads */ + pthread_mutex_lock(&lock); + threads_active = 1; + threads_stop = 0; + for (t = tinfo; t != endtinfo; t++) { + t->corefd = dup(corefd); + if (t->corefd < 0) { + nthreads = t - tinfo; + endtinfo = t; + break; + } + if (pthread_create(&t->tid, NULL, runstreams, t) != 0) + logprint(LOG_ERR, 1, 1, "pthread_create: %s", + strerror(errno)); + } + pthread_mutex_unlock(&lock); +} + +static void +sbarrier() +{ + stream_t *s; + + pthread_mutex_lock(&lock); + for (s = streams; s != endstreams; s++) { + while (s->bound || s->blocks.head != NULL) + pthread_cond_wait(&cvbarrier, &lock); + } + pthread_mutex_unlock(&lock); +} + +static void +stopstreams() +{ + tinfo_t *t; + + if (threads_active) { + sbarrier(); + pthread_mutex_lock(&lock); + threads_stop = 1; + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + for (t = tinfo; t != endtinfo; t++) + pthread_join(t->tid, NULL); + free(tinfo); + tinfo = NULL; + threads_active = 0; + } +} + +static block_t * +getfreeblock() +{ + block_t *b; + + pthread_mutex_lock(&lock); + while ((b = deqh(&freeblocks)) == NULL) + pthread_cond_wait(&cvfree, &lock); + pthread_mutex_unlock(&lock); + return (b); +} + +/* data page offset from page number */ +#define BTOP(b) ((b) >> dumphdr.dump_pageshift) +#define PTOB(p) ((p) << dumphdr.dump_pageshift) +#define DATAOFF(p) (corehdr.dump_data + PTOB(p)) + +/* check for coreblksize boundary */ +static int +isblkbnd(pgcnt_t pgnum) +{ + return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0); +} + +static int +iszpage(char *buf) +{ + size_t sz; + uint64_t *pl; + + pl = (uint64_t *)(buf); + for (sz = 0; sz < pagesize; sz += sizeof (*pl)) + if (*pl++ != 0) + return (0); + return (1); +} + +volatile uint_t *hist; + +/* write pages to the core file */ +static void +putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np) +{ + atomic_inc_uint(&hist[np]); + if (np > 0) + Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum)); +} + +/* + * Process one lzjb block. + * No object (stream header or page) will be split over a block boundary. + */ +static void +lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz) +{ + int rc = 0; + int in = 0; + int csize; + int doflush; + char *out; + size_t dsize; + dumpcsize_t sc; + dumpstreamhdr_t sh; + + if (!s->init) { + s->init = 1; + if (s->blkbuf == NULL) + s->blkbuf = Zalloc(coreblksize); + s->state = STREAMSTART; + } + while (in < blocksz) { + switch (s->state) { + case STREAMSTART: + memcpy(&sh, block + in, sizeof (sh)); + in += sizeof (sh); + if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMSTART: bad stream header"); + if (sh.stream_npages > datahdr.dump_maxrange) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMSTART: bad range: %d > %d", + sh.stream_npages, datahdr.dump_maxrange); + s->pagenum = sh.stream_pagenum; + s->npages = sh.stream_npages; + s->curpage = s->pagenum; + s->nout = 0; + s->done = 0; + s->state = STREAMPAGES; + break; + case STREAMPAGES: + memcpy(&sc, block + in, cs); + in += cs; + csize = DUMP_GET_CSIZE(sc); + if (csize > pagesize) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMPAGES: bad csize=%d", csize); + + out = s->blkbuf + PTOB(s->nout); + dsize = decompress(block + in, out, csize, pagesize); + + if (dsize != pagesize) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMPAGES: dsize %d != pagesize %d", + dsize, pagesize); + + in += csize; + atomic_inc_64(&saved); + + doflush = 0; + if (s->nout == 0 && iszpage(out)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++s->nout >= BTOP(coreblksize) || + isblkbnd(s->curpage + s->nout)) { + doflush = 1; + } + if (++s->done >= s->npages) { + s->state = STREAMSTART; + doflush = 1; + } + if (doflush) { + putpage(corefd, s->blkbuf, s->curpage, s->nout); + s->nout = 0; + s->curpage = s->pagenum + s->done; + } + break; + } + } +} + +/* bzlib library reports errors with this callback */ +void +bz_internal_error(int errcode) +{ + logprint(LOG_ERR, 1, 1, "bz_internal_error: err %s\n", + BZ2_bzErrorString(errcode)); +} + +/* + * Return one object in the stream. + * + * An object (stream header or page) will likely span an input block + * of compression data. Return non-zero when an entire object has been + * retrieved from the stream. + */ +static int +bz2decompress(stream_t *s, void *buf, size_t size) +{ + int rc; + + if (s->strm.avail_out == 0) { + s->strm.next_out = buf; + s->strm.avail_out = size; + } + while (s->strm.avail_in > 0) { + rc = BZ2_bzDecompress(&s->strm); + if (rc == BZ_STREAM_END) { + rc = BZ2_bzDecompressReset(&s->strm); + if (rc != BZ_OK) + logprint(LOG_ERR, 1, 1, + "BZ2_bzDecompressReset: %s", + BZ2_bzErrorString(rc)); + continue; + } + + if (s->strm.avail_out == 0) + break; + } + return (s->strm.avail_out == 0); +} + +/* + * Process one bzip2 block. + * The interface is documented here: + * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html + */ +static void +bz2block(int corefd, stream_t *s, char *block, size_t blocksz) +{ + int rc = 0; + int doflush; + char *out; + + if (!s->init) { + s->init = 1; + rc = BZ2_bzDecompressInit(&s->strm, 0, 0); + if (rc != BZ_OK) + logprint(LOG_ERR, 1, 1, + "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc)); + if (s->blkbuf == NULL) + s->blkbuf = Zalloc(coreblksize); + s->strm.avail_out = 0; + s->state = STREAMSTART; + } + s->strm.next_in = block; + s->strm.avail_in = blocksz; + + while (s->strm.avail_in > 0) { + switch (s->state) { + case STREAMSTART: + if (!bz2decompress(s, &s->sh, sizeof (s->sh))) + return; + if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0) + logprint(LOG_ERR, 1, 1, + "BZ2 STREAMSTART: bad stream header"); + if (s->sh.stream_npages > datahdr.dump_maxrange) + logprint(LOG_ERR, 1, 1, + "BZ2 STREAMSTART: bad range: %d > %d", + s->sh.stream_npages, datahdr.dump_maxrange); + s->pagenum = s->sh.stream_pagenum; + s->npages = s->sh.stream_npages; + s->curpage = s->pagenum; + s->nout = 0; + s->done = 0; + s->state = STREAMPAGES; + break; + case STREAMPAGES: + out = s->blkbuf + PTOB(s->nout); + if (!bz2decompress(s, out, pagesize)) + return; + + atomic_inc_64(&saved); + + doflush = 0; + if (s->nout == 0 && iszpage(out)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++s->nout >= BTOP(coreblksize) || + isblkbnd(s->curpage + s->nout)) { + doflush = 1; + } + if (++s->done >= s->npages) { + s->state = STREAMSTART; + doflush = 1; + } + if (doflush) { + putpage(corefd, s->blkbuf, s->curpage, s->nout); + s->nout = 0; + s->curpage = s->pagenum + s->done; + } + break; + } + } +} + +/* report progress */ +static void +report_progress() +{ + int sec, percent; + + if (!interactive) + return; + + percent = saved * 100LL / corehdr.dump_npages; + if (percent > percent_done) { + sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + (void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60, + percent); + (void) fflush(stdout); + percent_done = percent; + } +} + +/* thread body */ +static void * +runstreams(void *arg) +{ + tinfo_t *t = arg; + stream_t *s; + block_t *b; + int bound; + + pthread_mutex_lock(&lock); + while (!threads_stop) { + bound = 0; + for (s = streams; s != endstreams; s++) { + if (s->bound || s->blocks.head == NULL) + continue; + s->bound = 1; + bound = 1; + pthread_cond_signal(&cvwork); + while (s->blocks.head != NULL) { + b = deqh(&s->blocks); + pthread_mutex_unlock(&lock); + + if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2) + lzjbblock(t->corefd, s, b->block, + b->size); + else + bz2block(t->corefd, s, b->block, + b->size); + + pthread_mutex_lock(&lock); + enqt(&freeblocks, b); + pthread_cond_signal(&cvfree); + + report_progress(); + } + s->bound = 0; + pthread_cond_signal(&cvbarrier); + } + if (!bound && !threads_stop) + pthread_cond_wait(&cvwork, &lock); + } + close(t->corefd); + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + return (arg); +} + +/* + * Process compressed pages. + * + * The old format, now called single-threaded lzjb, is a 32-bit size + * word followed by 'size' bytes of lzjb compression data for one + * page. The new format extends this by storing a 12-bit "tag" in the + * upper bits of the size word. When the size word is pagesize or + * less, it is assumed to be one lzjb page. When the size word is + * greater than pagesize, it is assumed to be a "stream block", + * belonging to up to 4095 streams. In practice, the number of streams + * is set to one less than the number of CPUs running at crash + * time. One CPU processes the crash dump, the remaining CPUs + * separately process groups of data pages. + * + * savecore creates a thread per stream, but never more threads than + * the number of CPUs running savecore. This is because savecore can + * be processing a crash file from a remote machine, which may have + * more CPUs. + * + * When the kernel uses parallel lzjb or parallel bzip2, we expect a + * series of 128KB blocks of compression data. In this case, each + * block has a "tag", in the range 1-4095. Each block is handed off to + * to the threads running "runstreams". The dump format is either lzjb + * or bzip2, never a mixture. These threads, in turn, process the + * compression data for groups of pages. Groups of pages are delimited + * by a "stream header", which indicates a starting pfn and number of + * pages. When a stream block has been read, the condition variable + * "cvwork" is signalled, which causes one of the avaiable threads to + * wake up and process the stream. + * + * In the parallel case there will be streams blocks encoding all data + * pages. The stream of blocks is terminated by a zero size + * word. There can be a few lzjb pages tacked on the end, depending on + * the architecture. The sbarrier function ensures that all stream + * blocks have been processed so that the page number for the few + * single pages at the end can be known. + */ +static void +decompress_pages(int corefd) +{ + char *cpage = NULL; + char *dpage = NULL; + char *out; + pgcnt_t curpage; + block_t *b; + FILE *dumpf; + FILE *tracef = NULL; + stream_t *s; + size_t dsize; + size_t insz = FBUFSIZE; + char *inbuf = Zalloc(insz); + uint32_t csize; + dumpcsize_t dcsize; + dumpstreamhdr_t sh; + int nstreams = datahdr.dump_nstreams; + int maxcsize = datahdr.dump_maxcsize; + int nout, tag, doflush; + + dumpf = fdopen(dup(dumpfd), "rb"); + if (dumpf == NULL) + logprint(LOG_ERR, 1, 1, "fdopen: %s", strerror(errno)); + + setvbuf(dumpf, inbuf, _IOFBF, insz); + Fseek(dumphdr.dump_data, dumpf); + + while (1) { + + /* + * The csize word delimits stream blocks. + * See dumphdr.h for a description. + */ + Fread(&dcsize, sizeof (dcsize), dumpf); + + tag = DUMP_GET_TAG(dcsize); + csize = DUMP_GET_CSIZE(dcsize); + + if (tag != 0) { /* a stream block */ + + if (nstreams == 0) + logprint(LOG_ERR, 1, 1, + "starting data header is missing"); + + if (tag > nstreams) + logprint(LOG_ERR, 1, 1, + "stream tag %d not in range 1..%d", + tag, nstreams); + + if (csize > maxcsize) + logprint(LOG_ERR, 1, 1, + "block size 0x%x > max csize 0x%x", + csize, maxcsize); + + if (streams == NULL) + initstreams(corefd, nstreams, maxcsize); + s = &streams[tag - 1]; + s->tag = tag; + + b = getfreeblock(); + b->size = csize; + Fread(b->block, csize, dumpf); + + pthread_mutex_lock(&lock); + enqt(&s->blocks, b); + if (!s->bound) + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + + } else if (csize > 0) { /* one lzjb page */ + + if (csize > pagesize) + logprint(LOG_ERR, 1, 1, + "csize 0x%x > pagesize 0x%x", + csize, pagesize); + + if (cpage == NULL) + cpage = Zalloc(pagesize); + if (dpage == NULL) { + dpage = Zalloc(coreblksize); + nout = 0; + } + + Fread(cpage, csize, dumpf); + + out = dpage + PTOB(nout); + dsize = decompress(cpage, out, csize, pagesize); + + if (dsize != pagesize) + logprint(LOG_ERR, 1, 1, + "dsize 0x%x != pagesize 0x%x", + dsize, pagesize); + + /* + * wait for streams to flush so that 'saved' is correct + */ + if (threads_active) + sbarrier(); + + doflush = 0; + if (nout == 0) + curpage = saved; + + atomic_inc_64(&saved); + + if (nout == 0 && iszpage(dpage)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++nout >= BTOP(coreblksize) || + isblkbnd(curpage + nout) || + saved >= dumphdr.dump_npages) { + doflush = 1; + } + + if (doflush) { + putpage(corefd, dpage, curpage, nout); + nout = 0; + } + + report_progress(); + + /* + * Non-streams lzjb does not use blocks. Stop + * here if all the pages have been decompressed. + */ + if (saved >= dumphdr.dump_npages) + break; + + } else { + break; /* end of data */ + } + } + + stopstreams(); + if (tracef != NULL) + fclose(tracef); + fclose(dumpf); + if (inbuf) + free(inbuf); + if (cpage) + free(cpage); + if (dpage) + free(dpage); + if (streams) + free(streams); } static void build_corefile(const char *namelist, const char *corefile) { - char *inbuf = Zalloc(pagesize); - char *outbuf = Zalloc(pagesize); size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t); size_t ksyms_size = dumphdr.dump_ksyms_size; size_t ksyms_csize = dumphdr.dump_ksyms_csize; - pfn_t *pfn_table = Zalloc(pfn_table_size); + pfn_t *pfn_table; char *ksyms_base = Zalloc(ksyms_size); char *ksyms_cbase = Zalloc(ksyms_csize); + size_t ksyms_dsize; + Stat_t st; int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644); - offset_t dumpoff; - int percent_done = 0; - pgcnt_t saved = 0; - uint32_t csize; - size_t dsize; (void) printf("Constructing namelist %s/%s\n", savedir, namelist); /* + * Determine the optimum write size for the core file + */ + Fstat(corefd, &st, corefile); + + if (verbose > 1) + printf("%s: %ld block size\n", corefile, st.st_blksize); + coreblksize = st.st_blksize; + if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize)) + coreblksize = MINCOREBLKSIZE; + + hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1); + + /* + * This dump file is now uncompressed + */ + corehdr.dump_flags &= ~DF_COMPRESSED; + + /* * Read in the compressed symbol table, copy it to corefile, * decompress it, and write the result to namelist. */ @@ -276,10 +1215,12 @@ build_corefile(const char *namelist, const char *corefile) Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms); Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms); - if ((dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, - ksyms_size)) != ksyms_size) - logprint(LOG_WARNING, 1, -1, "bad data in symbol table, %lu of %lu" - " bytes saved", dsize, ksyms_size); + ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, + ksyms_size); + if (ksyms_dsize != ksyms_size) + logprint(LOG_WARNING, 1, -1, + "bad data in symbol table, %lu of %lu bytes saved", + ksyms_dsize, ksyms_size); Pwrite(namefd, ksyms_base, ksyms_size, 0); (void) close(namefd); @@ -291,6 +1232,7 @@ build_corefile(const char *namelist, const char *corefile) /* * Read in and write out the pfn table. */ + pfn_table = Zalloc(pfn_table_size); corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize); Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn); Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn); @@ -300,29 +1242,19 @@ build_corefile(const char *namelist, const char *corefile) */ corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize); build_dump_map(corefd, pfn_table); + free(pfn_table); /* - * Decompress and save the pages. + * Decompress the pages */ - dumpoff = dumphdr.dump_data; - while (saved < dumphdr.dump_npages) { - Pread(dumpfd, &csize, sizeof (uint32_t), dumpoff); - dumpoff += sizeof (uint32_t); - if (csize > pagesize) - break; - Pread(dumpfd, inbuf, csize, dumpoff); - dumpoff += csize; - if (decompress(inbuf, outbuf, csize, pagesize) != pagesize) - break; - Pwrite(corefd, outbuf, pagesize, - corehdr.dump_data + saved * pagesize); - if (++saved * 100LL / dumphdr.dump_npages > percent_done) { - (void) printf("\r%3d%% done", ++percent_done); - (void) fflush(stdout); - } - } + decompress_pages(corefd); + (void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved, + dumphdr.dump_npages); - (void) printf(": %ld of %ld pages saved\n", saved, dumphdr.dump_npages); + if (verbose) + (void) printf("%ld (%ld%%) zero pages were not written\n", + (pgcnt_t)zpages, (pgcnt_t)zpages * 100 / + dumphdr.dump_npages); if (saved != dumphdr.dump_npages) logprint(LOG_WARNING, 1, -1, "bad data after page %ld", saved); @@ -331,10 +1263,10 @@ build_corefile(const char *namelist, const char *corefile) * Write out the modified dump headers. */ Pwrite(corefd, &corehdr, sizeof (corehdr), 0); - Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); (void) close(corefd); - (void) close(dumpfd); } /* @@ -382,16 +1314,18 @@ message_save(void) break; if (ld.ld_magic != LOG_MAGIC) - logprint(-1, verbose, 0, "bad magic %x", ld.ld_magic); + logprint(LOG_ERR, verbose, 0, "bad magic %x", + ld.ld_magic); if (dat.len >= DUMP_LOGSIZE) - logprint(-1, verbose, 0, "bad size %d", ld.ld_msgsize); + logprint(LOG_ERR, verbose, 0, "bad size %d", + ld.ld_msgsize); Pread(dumpfd, ctl.buf, ctl.len, dumpoff); dumpoff += ctl.len; if (ld.ld_csum != checksum32(ctl.buf, ctl.len)) - logprint(-1, verbose, 0, "bad log_ctl checksum"); + logprint(LOG_ERR, verbose, 0, "bad log_ctl checksum"); lc.flags |= SL_LOGONLY; @@ -399,7 +1333,7 @@ message_save(void) dumpoff += dat.len; if (ld.ld_msum != checksum32(dat.buf, dat.len)) - logprint(-1, verbose, 0, "bad message checksum"); + logprint(LOG_ERR, verbose, 0, "bad message checksum"); if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1) logprint(LOG_ERR, 1, 1, "putpmsg: %s", strerror(errno)); @@ -410,17 +1344,37 @@ message_save(void) return (0); } +static long +getbounds(const char *f) +{ + long b = -1; + const char *p = strrchr(f, '/'); + + sscanf(p ? p + 1 : f, "vmdump.%ld", &b); + return (b); +} + int main(int argc, char *argv[]) { - int c, bfd; + int i, n, c, bfd; int mflag = 0; - long bounds; - char namelist[30], corefile[30], boundstr[30]; + Stat_t st; + struct rlimit rl; + long filebounds = -1; + char namelist[30], corefile[30], boundstr[30], metricsname[PATH_MAX]; + + startts = gethrtime(); + + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = rl.rlim_max; + setrlimit(RLIMIT_NOFILE, &rl); openlog(progname, LOG_ODELAY, LOG_AUTH); + (void) defopen("/etc/dumpadm.conf"); - savedir = defread("DUMPADM_SAVDIR="); + savedir = strdup(defread("DUMPADM_SAVDIR=")); + sprintf(metricsname, "%s/%s", savedir, METRICSFILE); while ((c = getopt(argc, argv, "Lvdmf:")) != EOF) { switch (c) { @@ -438,29 +1392,23 @@ main(int argc, char *argv[]) break; case 'f': dumpfile = optarg; + filebounds = getbounds(dumpfile); break; case '?': usage(); } } + interactive = isatty(STDOUT_FILENO); + if (dumpfile == NULL || livedump) dumpfd = Open("/dev/dump", O_RDONLY, 0444); if (dumpfile == NULL) { dumpfile = Zalloc(MAXPATHLEN); - if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) { - /* - * If this isn't an interactive session, we are running - * as part of the boot process. If this is the case, - * don't complain about the lack of dump device. - */ - if (isatty(STDOUT_FILENO)) - logprint(LOG_ERR, 1, 1, - "no dump device configured"); - else - return (1); - } + if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) + logprint(-1, interactive, 1, + "no dump device configured"); } if (mflag) @@ -468,6 +1416,7 @@ main(int argc, char *argv[]) if (optind == argc - 1) savedir = argv[optind]; + if (savedir == NULL || optind < argc - 1) usage(); @@ -475,6 +1424,14 @@ main(int argc, char *argv[]) logprint(-1, 1, 1, "dedicated dump device required"); (void) close(dumpfd); + dumpfd = -1; + + Stat(dumpfile, &st); + + filemode = S_ISREG(st.st_mode); + + if (!filemode && defread("DUMPADM_CSAVE=off") == NULL) + csave = 1; read_dumphdr(); @@ -520,24 +1477,145 @@ main(int argc, char *argv[]) if ((dumphdr.dump_flags & DF_COMPLETE) == 0) logprint(LOG_WARNING, 1, -1, "incomplete dump on dump device"); - (void) printf("System dump time: %s", ctime(&dumphdr.dump_crashtime)); + logprint(LOG_WARNING, 1, -1, "System dump time: %s", + ctime(&dumphdr.dump_crashtime)); + + check_space(csave); + + if (filebounds < 0) + bounds = read_number_from_file("bounds", 0); + else + bounds = filebounds; + + if (csave) { + size_t metrics_size = datahdr.dump_metrics; + + (void) sprintf(corefile, "vmdump.%ld", bounds); + + datahdr.dump_metrics = 0; + + logprint(LOG_ERR, 1, -1, + "Saving compressed system crash dump in %s/%s", + savedir, corefile); + + copy_crashfile(corefile); + + if (metrics_size > 0) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + FILE *mfile = fopen(metricsname, "a"); + char *metrics = Zalloc(metrics_size + 1); + + Pread(dumpfd, metrics, metrics_size, endoff + + sizeof (dumphdr) + sizeof (datahdr)); + + if (mfile == NULL) { + logprint(LOG_WARNING, 1, -1, + "Can't create %s:\n%s", + metricsname, metrics); + } else { + fprintf(mfile, "[[[[,,,"); + for (i = 0; i < argc; i++) + fprintf(mfile, "%s ", argv[i]); + fprintf(mfile, "\n"); + fprintf(mfile, ",,,%s %s %s %s %s\n", + dumphdr.dump_utsname.sysname, + dumphdr.dump_utsname.nodename, + dumphdr.dump_utsname.release, + dumphdr.dump_utsname.version, + dumphdr.dump_utsname.machine); + fprintf(mfile, ",,,%s dump time %s\n", + dumphdr.dump_flags & DF_LIVE ? "Live" : + "Crash", ctime(&dumphdr.dump_crashtime)); + fprintf(mfile, ",,,%s/%s\n", savedir, corefile); + fprintf(mfile, "Metrics:\n%s\n", metrics); + fprintf(mfile, "Copy pages,%ld\n", dumphdr. + dump_npages); + fprintf(mfile, "Copy time,%d\n", sec); + fprintf(mfile, "Copy pages/sec,%ld\n", + dumphdr.dump_npages / sec); + fprintf(mfile, "]]]]\n"); + fclose(mfile); + } + free(metrics); + } - check_space(); + logprint(LOG_ERR, 1, -1, + "Decompress the crash dump with " + "\n'savecore -vf %s/%s'", + savedir, corefile); + + } else { + (void) sprintf(namelist, "unix.%ld", bounds); + (void) sprintf(corefile, "vmcore.%ld", bounds); + + if (interactive && filebounds >= 0 && access(corefile, F_OK) + == 0) + logprint(-1, 1, 1, + "%s already exists: remove with " + "'rm -f %s/{unix,vmcore}.%ld'", + corefile, savedir, bounds); + + logprint(LOG_ERR, 1, -1, + "saving system crash dump in %s/{unix,vmcore}.%ld", + savedir, bounds); + + build_corefile(namelist, corefile); + + if (access(metricsname, F_OK) == 0) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + FILE *mfile = fopen(metricsname, "a"); + + fprintf(mfile, "[[[[,,,"); + for (i = 0; i < argc; i++) + fprintf(mfile, "%s ", argv[i]); + fprintf(mfile, "\n"); + fprintf(mfile, ",,,%s/%s\n", savedir, corefile); + fprintf(mfile, ",,,%s %s %s %s %s\n", + dumphdr.dump_utsname.sysname, + dumphdr.dump_utsname.nodename, + dumphdr.dump_utsname.release, + dumphdr.dump_utsname.version, + dumphdr.dump_utsname.machine); + fprintf(mfile, "Uncompress pages,%ld\n", saved); + fprintf(mfile, "Uncompress time,%d\n", sec); + fprintf(mfile, "Uncompress pages/sec,%ld\n", + saved / sec); + fprintf(mfile, "]]]]\n"); + fclose(mfile); + } + } - bounds = read_number_from_file("bounds", 0); + if (filebounds < 0) { + (void) sprintf(boundstr, "%ld\n", bounds + 1); + bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); + Pwrite(bfd, boundstr, strlen(boundstr), 0); + (void) close(bfd); + } - (void) sprintf(namelist, "unix.%ld", bounds); - (void) sprintf(corefile, "vmcore.%ld", bounds); + if (verbose) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; - syslog(LOG_ERR, "saving system crash dump in %s/*.%ld", - savedir, bounds); + printf("%d:%02d dump %s is done\n", + sec / 60, sec % 60, + csave ? "copy" : "decompress"); + } - build_corefile(namelist, corefile); + if (verbose > 1 && hist != NULL) { + int i, nw; + + for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i) + nw += hist[i] * i; + printf("pages count %%\n"); + for (i = 0; i <= BTOP(coreblksize); ++i) { + if (hist[i] == 0) + continue; + printf("%3d %5u %6.2f\n", + i, hist[i], 100.0 * hist[i] * i / nw); + } + } - (void) sprintf(boundstr, "%ld\n", bounds + 1); - bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); - Pwrite(bfd, boundstr, strlen(boundstr), 0); - (void) close(bfd); + (void) close(dumpfd); + dumpfd = -1; return (0); } |