summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/dumpadm/dconf.c40
-rw-r--r--usr/src/cmd/dumpadm/dconf.h12
-rw-r--r--usr/src/cmd/dumpadm/main.c14
-rw-r--r--usr/src/cmd/file/file.c34
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c14
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c40
-rw-r--r--usr/src/cmd/savecore/Makefile.com26
-rw-r--r--usr/src/cmd/savecore/savecore.c1252
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);
}