diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdumplog | |
download | pcp-47e6e7c84f008a53061e661f31ae96629bc694ef.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdumplog')
-rw-r--r-- | src/pmdumplog/GNUmakefile | 31 | ||||
-rw-r--r-- | src/pmdumplog/pmdumplog.c | 901 |
2 files changed, 932 insertions, 0 deletions
diff --git a/src/pmdumplog/GNUmakefile b/src/pmdumplog/GNUmakefile new file mode 100644 index 0000000..db1e1c0 --- /dev/null +++ b/src/pmdumplog/GNUmakefile @@ -0,0 +1,31 @@ +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../.. +include $(TOPDIR)/src/include/builddefs + +CFILES = pmdumplog.c +CMDTARGET = pmdumplog$(EXECSUFFIX) +LLDLIBS = $(PCPLIB) + +default: $(CMDTARGET) + +include $(BUILDRULES) + +install: $(CMDTARGET) + $(INSTALL) -m 755 $(CMDTARGET) $(PCP_BIN_DIR)/$(CMDTARGET) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdumplog/pmdumplog.c b/src/pmdumplog/pmdumplog.c new file mode 100644 index 0000000..eac8c1e --- /dev/null +++ b/src/pmdumplog/pmdumplog.c @@ -0,0 +1,901 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include <ctype.h> +#include <limits.h> +#include <sys/stat.h> + +static struct timeval tv; +static char timebuf[32]; /* for pmCtime result + .xxx */ +static int numpmid; +static pmID *pmid; +static pmID pmid_flags; +static pmID pmid_missed; +static int sflag; +static int xflag; /* for -x (long timestamps) */ + +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + { "all", 0, 'a', 0, "dump everything" }, + { "descs", 0, 'd', 0, "dump metric descriptions" }, + { "insts", 0, 'i', 0, "dump instance domain descriptions" }, + { "", 0, 'L', 0, "more verbose form of archive label dump" }, + { "label", 0, 'l', 0, "dump the archive label" }, + { "metrics", 0, 'm', 0, "dump values of the metrics (default)" }, + PMOPT_NAMESPACE, + { "reverse", 0, 'r', 0, "process archive in reverse chronological order" }, + PMOPT_START, + { "sizes", 0, 's', 0, "report size of data records in archive" }, + PMOPT_FINISH, + { "", 0, 't', 0, "dump the temporal index" }, + { "", 1, 'v', "FILE", "verbose hex dump of a physical file in raw format" }, + { "", 0, 'x', 0, "include date in reported timestamps" }, + PMOPT_TIMEZONE, + PMOPT_HOSTZONE, + PMOPT_HELP, + PMAPI_OPTIONS_END +}; + +static int overrides(int, pmOptions *); +static pmOptions opts = { + .flags = PM_OPTFLAG_DONE | PM_OPTFLAG_STDOUT_TZ | PM_OPTFLAG_BOUNDARIES, + .short_options = "aD:dilLmn:rS:sT:tv:xZ:z?", + .long_options = longopts, + .short_usage = "[options] [archive [metricname ...]]", + .override = overrides, +}; + +/* + * return -1, 0 or 1 as the struct timeval's compare + * a < b, a == b or a > b + */ +static int +tvcmp(struct timeval a, struct timeval b) +{ + if (a.tv_sec < b.tv_sec) + return -1; + if (a.tv_sec > b.tv_sec) + return 1; + if (a.tv_usec < b.tv_usec) + return -1; + if (a.tv_usec > b.tv_usec) + return 1; + return 0; +} + +static int +do_size(pmResult *rp) +{ + int nbyte = 0; + int i; + int j; + /* + * Externally the log record looks like this ... + * :----------:-----------:..........:---------: + * | int len | timestamp | pmResult | int len | + * :----------:-----------:..........:---------: + * + * start with sizes of the header len, timestamp, numpmid, and + * trailer len + */ + nbyte = sizeof(int) + sizeof(__pmTimeval) + sizeof(int); + /* len + timestamp + len */ + nbyte += sizeof(int); + /* numpmid */ + for (i = 0; i < rp->numpmid; i++) { + pmValueSet *vsp = rp->vset[i]; + nbyte += sizeof(pmID) + sizeof(int); /* + pmid[i], numval */ + if (vsp->numval > 0) { + nbyte += sizeof(int); /* + valfmt */ + for (j = 0; j < vsp->numval; j++) { + nbyte += sizeof(__pmValue_PDU); /* + pmValue[j] */ + if (vsp->valfmt != PM_VAL_INSITU) + /* + pmValueBlock */ + /* rounded up */ + nbyte += PM_PDU_SIZE_BYTES(vsp->vlist[j].value.pval->vlen); + } + } + } + + return nbyte; +} + +static void +setup_event_derived_metrics(void) +{ + int sts; + + if (pmid_flags == 0) { + /* + * get PMID for event.flags and event.missed + * note that pmUnpackEventRecords() will have called + * __pmRegisterAnon(), so the anon metrics + * should now be in the PMNS + */ + char *name_flags = "event.flags"; + char *name_missed = "event.missed"; + + if ((sts = pmLookupName(1, &name_flags, &pmid_flags)) < 0) { + /* should not happen! */ + fprintf(stderr, "Warning: cannot get PMID for %s: %s\n", + name_flags, pmErrStr(sts)); + /* avoid subsequent warnings ... */ + __pmid_int(&pmid_flags)->item = 1; + } + sts = pmLookupName(1, &name_missed, &pmid_missed); + if (sts < 0) { + /* should not happen! */ + fprintf(stderr, "Warning: cannot get PMID for %s: %s\n", + name_missed, pmErrStr(sts)); + /* avoid subsequent warnings ... */ + __pmid_int(&pmid_missed)->item = 1; + } + } +} + +static int +dump_nrecords(int nrecords, int nmissed) +{ + printf("%d", nrecords); + if (nmissed > 0) + printf(" (and %d missed)", nmissed); + if (nrecords + nmissed == 1) + printf(" event record\n"); + else + printf(" event records\n"); + return 0; +} + +static int +dump_nparams(int numpmid) +{ + if (numpmid == 0) { + printf(" ---\n"); + printf(" No parameters\n"); + return -1; + } + if (numpmid < 0) { + printf(" ---\n"); + printf(" Error: illegal number of parameters (%d)\n", + numpmid); + return -1; + } + return 0; +} + +static void +dump_parameter(pmValueSet *xvsp, int index, int *flagsp) +{ + int sts, flags = *flagsp; + pmDesc desc; + char *name; + + if (pmNameID(xvsp->pmid, &name) >= 0) { + if (index == 0) { + if (xvsp->pmid == pmid_flags) { + flags = *flagsp = xvsp->vlist[0].value.lval; + printf(" flags 0x%x", flags); + printf(" (%s) ---\n", pmEventFlagsStr(flags)); + free(name); + return; + } + printf(" ---\n"); + } + if ((flags & PM_EVENT_FLAG_MISSED) && index == 1 && + (xvsp->pmid == pmid_missed)) { + printf(" ==> %d missed event records\n", + xvsp->vlist[0].value.lval); + free(name); + return; + } + printf(" %s (%s):", pmIDStr(xvsp->pmid), name); + free(name); + } + else + printf(" PMID: %s:", pmIDStr(xvsp->pmid)); + if ((sts = pmLookupDesc(xvsp->pmid, &desc)) < 0) { + printf(" pmLookupDesc: %s\n", pmErrStr(sts)); + return; + } + printf(" value "); + pmPrintValue(stdout, xvsp->valfmt, desc.type, &xvsp->vlist[0], 1); + putchar('\n'); +} + +static void +dump_event(const char *name, pmValueSet *vsp, int index, int indom, int type) +{ + int r; /* event records */ + int p; /* event parameters */ + int flags; + int nrecords; + int nmissed = 0; + int highres = (type == PM_TYPE_HIGHRES_EVENT); + char *iname; + pmValue *vp = &vsp->vlist[index]; + + if (index > 0) + printf(" "); + printf(" %s (%s", pmIDStr(vsp->pmid), name); + if (indom != PM_INDOM_NULL) { + putchar('['); + if (pmNameInDom(indom, vp->inst, &iname) < 0) + printf("%d or ???])", vp->inst); + else { + printf("%d or \"%s\"])", vp->inst, iname); + free(iname); + } + } + else { + printf(")"); + } + printf(": "); + + if (highres) { + pmHighResResult **hr; + + if ((nrecords = pmUnpackHighResEventRecords(vsp, index, &hr)) < 0) + return; + if (nrecords == 0) { + printf("No event records\n"); + pmFreeHighResEventResult(hr); + return; + } + setup_event_derived_metrics(); + + for (r = 0; r < nrecords; r++) { + if (hr[r]->numpmid == 2 && hr[r]->vset[0]->pmid == pmid_flags && + (hr[r]->vset[0]->vlist[0].value.lval & PM_EVENT_FLAG_MISSED) && + hr[r]->vset[1]->pmid == pmid_missed) { + nmissed += hr[r]->vset[1]->vlist[0].value.lval; + } + } + dump_nrecords(nrecords, nmissed); + + for (r = 0; r < nrecords; r++) { + printf(" --- event record [%d] timestamp ", r); + __pmPrintHighResStamp(stdout, &hr[r]->timestamp); + if (dump_nparams(hr[r]->numpmid) < 0) + continue; + flags = 0; + for (p = 0; p < hr[r]->numpmid; p++) + dump_parameter(hr[r]->vset[p], p, &flags); + } + pmFreeHighResEventResult(hr); + } + else { + pmResult **res; + + if ((nrecords = pmUnpackEventRecords(vsp, index, &res)) < 0) + return; + if (nrecords == 0) { + printf("No event records\n"); + pmFreeEventResult(res); + return; + } + setup_event_derived_metrics(); + + for (r = 0; r < nrecords; r++) { + if (res[r]->numpmid == 2 && res[r]->vset[0]->pmid == pmid_flags && + (res[r]->vset[0]->vlist[0].value.lval & PM_EVENT_FLAG_MISSED) && + res[r]->vset[1]->pmid == pmid_missed) { + nmissed += res[r]->vset[1]->vlist[0].value.lval; + } + } + dump_nrecords(nrecords, nmissed); + + for (r = 0; r < nrecords; r++) { + printf(" --- event record [%d] timestamp ", r); + __pmPrintStamp(stdout, &res[r]->timestamp); + if (dump_nparams(res[r]->numpmid) < 0) + continue; + flags = 0; + for (p = 0; p < res[r]->numpmid; p++) + dump_parameter(res[r]->vset[p], p, &flags); + } + pmFreeEventResult(res); + } +} + +static void +dump_metric(const char *mname, pmValueSet *vsp, int index, int indom, int type) +{ + pmValue *vp = &vsp->vlist[index]; + char *iname; + + if (index == 0) { + printf(" %s (%s):", pmIDStr(vsp->pmid), mname); + if (vsp->numval > 1) { + putchar('\n'); + printf(" "); + } + } + else + printf(" "); + + if (indom != PM_INDOM_NULL) { + printf(" inst ["); + if (pmNameInDom(indom, vp->inst, &iname) < 0) + printf("%d or ???]", vp->inst); + else { + printf("%d or \"%s\"]", vp->inst, iname); + free(iname); + } + } + printf(" value "); + pmPrintValue(stdout, vsp->valfmt, type, vp, 1); + putchar('\n'); +} + +static void +dump_result(pmResult *resp) +{ + int i; + int j; + int n; + char *mname = NULL; + pmDesc desc; + + if (sflag) { + int nbyte; + nbyte = do_size(resp); + printf("[%d bytes]\n", nbyte); + } + + if (xflag) { + char *ddmm; + char *yr; + + ddmm = pmCtime(&resp->timestamp.tv_sec, timebuf); + ddmm[10] = '\0'; + yr = &ddmm[20]; + printf("%s ", ddmm); + __pmPrintStamp(stdout, &resp->timestamp); + printf(" %4.4s", yr); + } + else + __pmPrintStamp(stdout, &resp->timestamp); + + if (resp->numpmid == 0) { + printf(" <mark>\n"); + return; + } + + for (i = 0; i < resp->numpmid; i++) { + pmValueSet *vsp = resp->vset[i]; + + if (i > 0) + printf(" "); + if ((n = pmNameID(vsp->pmid, &mname)) < 0) + mname = strdup("<noname>"); + if (vsp->numval == 0) { + printf(" %s (%s): No values returned!\n", pmIDStr(vsp->pmid), mname); + goto next; + } + else if (vsp->numval < 0) { + printf(" %s (%s): %s\n", pmIDStr(vsp->pmid), mname, pmErrStr(vsp->numval)); + goto next; + } + + if (pmLookupDesc(vsp->pmid, &desc) < 0) { + /* don't know, so punt on the most common cases */ + desc.indom = PM_INDOM_NULL; + if (vsp->valfmt == PM_VAL_INSITU) + desc.type = PM_TYPE_32; + else + desc.type = PM_TYPE_AGGREGATE; + } + + for (j = 0; j < vsp->numval; j++) { + if (desc.type == PM_TYPE_EVENT || + desc.type == PM_TYPE_HIGHRES_EVENT) + dump_event(mname, vsp, j, desc.indom, desc.type); + else + dump_metric(mname, vsp, j, desc.indom, desc.type); + } +next: + if (mname) + free(mname); + mname = NULL; + } +} + +static void +dumpDesc(__pmContext *ctxp) +{ + int i; + int sts; + char *p; + __pmHashNode *hp; + pmDesc *dp; + + printf("\nDescriptions for Metrics in the Log ...\n"); + for (i = 0; i < ctxp->c_archctl->ac_log->l_hashpmid.hsize; i++) { + for (hp = ctxp->c_archctl->ac_log->l_hashpmid.hash[i]; hp != NULL; hp = hp->next) { + dp = (pmDesc *)hp->data; + sts = pmNameID(dp->pmid, &p); + if (sts < 0) + printf("PMID: %s (%s)\n", pmIDStr(dp->pmid), "<noname>"); + else { + printf("PMID: %s (%s)\n", pmIDStr(dp->pmid), p); + free(p); + } + __pmPrintDesc(stdout, dp); + } + } +} + +static void +dumpInDom(__pmContext *ctxp) +{ + int i; + int j; + __pmHashNode *hp; + __pmLogInDom *idp; + __pmLogInDom *ldp; + + printf("\nInstance Domains in the Log ...\n"); + for (i = 0; i < ctxp->c_archctl->ac_log->l_hashindom.hsize; i++) { + for (hp = ctxp->c_archctl->ac_log->l_hashindom.hash[i]; hp != NULL; hp = hp->next) { + printf("InDom: %s\n", pmInDomStr((pmInDom)hp->key)); + /* + * in reverse chronological order, so iteration is a bit funny + */ + ldp = NULL; + for ( ; ; ) { + for (idp = (__pmLogInDom *)hp->data; idp->next != ldp; idp =idp->next) + ; + tv.tv_sec = idp->stamp.tv_sec; + tv.tv_usec = idp->stamp.tv_usec; + __pmPrintStamp(stdout, &tv); + printf(" %d instances\n", idp->numinst); + for (j = 0; j < idp->numinst; j++) { + printf(" %d or \"%s\"\n", + idp->instlist[j], idp->namelist[j]); + } + if (idp == (__pmLogInDom *)hp->data) + break; + ldp = idp; + } + } + } +} + +static void +dumpTI(__pmContext *ctxp) +{ + int i; + char path[MAXPATHLEN]; + off_t meta_size = -1; /* initialize to pander to gcc */ + off_t log_size = -1; /* initialize to pander to gcc */ + struct stat sbuf; + __pmLogTI *tip; + __pmLogTI *lastp; + + printf("\nTemporal Index\n"); + printf(" Log Vol end(meta) end(log)\n"); + lastp = NULL; + for (i = 0; i < ctxp->c_archctl->ac_log->l_numti; i++) { + tip = &ctxp->c_archctl->ac_log->l_ti[i]; + tv.tv_sec = tip->ti_stamp.tv_sec; + tv.tv_usec = tip->ti_stamp.tv_usec; + __pmPrintStamp(stdout, &tv); + printf(" %4d %11d %11d\n", tip->ti_vol, tip->ti_meta, tip->ti_log); + if (i == 0) { + sprintf(path, "%s.meta", opts.archives[0]); + if (stat(path, &sbuf) == 0) + meta_size = sbuf.st_size; + else + meta_size = -1; + } + if (lastp == NULL || tip->ti_vol != lastp->ti_vol) { + sprintf(path, "%s.%d", opts.archives[0], tip->ti_vol); + if (stat(path, &sbuf) == 0) + log_size = sbuf.st_size; + else { + log_size = -1; + printf(" Warning: file missing or compressed for log volume %d\n", tip->ti_vol); + } + } + /* + * Integrity Errors + * + * this(tv_sec) < 0 + * this(tv_usec) < 0 || this(tv_usec) > 999999 + * this(timestamp) < last(timestamp) + * this(vol) < last(vol) + * this(vol) == last(vol) && this(meta) <= last(meta) + * this(vol) == last(vol) && this(log) <= last(log) + * file_exists(<base>.meta) && this(meta) > file_size(<base>.meta) + * file_exists(<base>.this(vol)) && + * this(log) > file_size(<base>.this(vol)) + * + * Integrity Warnings + * + * this(vol) != last(vol) && !file_exists(<base>.this(vol)) + */ + if (tip->ti_stamp.tv_sec < 0 || + tip->ti_stamp.tv_usec < 0 || tip->ti_stamp.tv_usec > 999999) + printf(" Error: illegal timestamp value (%d sec, %d usec)\n", + tip->ti_stamp.tv_sec, tip->ti_stamp.tv_usec); + if (meta_size != -1 && tip->ti_meta > meta_size) + printf(" Error: offset to meta file past end of file (%ld)\n", + (long)meta_size); + if (log_size != -1 && tip->ti_log > log_size) + printf(" Error: offset to log file past end of file (%ld)\n", + (long)log_size); + if (i > 0) { + if (tip->ti_stamp.tv_sec < lastp->ti_stamp.tv_sec || + (tip->ti_stamp.tv_sec == lastp->ti_stamp.tv_sec && + tip->ti_stamp.tv_usec < lastp->ti_stamp.tv_usec)) + printf(" Error: timestamp went backwards in time %d.%06d -> %d.%06d\n", + (int)lastp->ti_stamp.tv_sec, (int)lastp->ti_stamp.tv_usec, + (int)tip->ti_stamp.tv_sec, (int)tip->ti_stamp.tv_usec); + if (tip->ti_vol < lastp->ti_vol) + printf(" Error: volume number decreased\n"); + if (tip->ti_vol == lastp->ti_vol && tip->ti_meta < lastp->ti_meta) + printf(" Error: offset to meta file decreased\n"); + if (tip->ti_vol == lastp->ti_vol && tip->ti_log < lastp->ti_log) + printf(" Error: offset to log file decreased\n"); + } + lastp = tip; + } +} + +static void +dumpLabel(int verbose) +{ + pmLogLabel label; + char *ddmm; + char *yr; + int sts; + + if ((sts = pmGetArchiveLabel(&label)) < 0) { + fprintf(stderr, "%s: Cannot get archive label record: %s\n", + pmProgname, pmErrStr(sts)); + exit(1); + } + + printf("Log Label (Log Format Version %d)\n", label.ll_magic & 0xff); + printf("Performance metrics from host %s\n", label.ll_hostname); + + ddmm = pmCtime(&label.ll_start.tv_sec, timebuf); + ddmm[10] = '\0'; + yr = &ddmm[20]; + printf(" commencing %s ", ddmm); + __pmPrintStamp(stdout, &label.ll_start); + printf(" %4.4s\n", yr); + + if (opts.finish.tv_sec == INT_MAX) { + /* pmGetArchiveEnd() failed! */ + printf(" ending UNKNOWN\n"); + } + else { + ddmm = pmCtime(&opts.finish.tv_sec, timebuf); + ddmm[10] = '\0'; + yr = &ddmm[20]; + printf(" ending %s ", ddmm); + __pmPrintStamp(stdout, &opts.finish); + printf(" %4.4s\n", yr); + } + + if (verbose) { + printf("Archive timezone: %s\n", label.ll_tz); + printf("PID for pmlogger: %" FMT_PID "\n", label.ll_pid); + } +} + +static void +rawdump(FILE *f) +{ + long old; + int len; + int check; + int i; + int sts; + + old = ftell(f); + fseek(f, (long)0, SEEK_SET); + + while ((sts = fread(&len, 1, sizeof(len), f)) == sizeof(len)) { + len = ntohl(len); + printf("Dump ... record len: %d @ offset: %ld", len, ftell(f) - sizeof(len)); + len -= 2 * sizeof(len); + for (i = 0; i < len; i++) { + check = fgetc(f); + if (check == EOF) { + printf("Unexpected EOF\n"); + break; + } + if (i % 32 == 0) putchar('\n'); + if (i % 4 == 0) putchar(' '); + printf("%02x", check & 0xff); + } + putchar('\n'); + if ((sts = fread(&check, 1, sizeof(check), f)) != sizeof(check)) { + if (sts == 0) + printf("Unexpected EOF\n"); + break; + } + check = ntohl(check); + len += 2 * sizeof(len); + if (check != len) { + printf("Trailer botch: %d != %d\n", check, len); + break; + } + } + if (sts < 0) + printf("fread fails: %s\n", osstrerror()); + fseek(f, old, SEEK_SET); +} + +static void +dometric(const char *name) +{ + int sts; + + if (*name == '\0') { + printf("PMNS appears to be empty!\n"); + return; + } + numpmid++; + pmid = (pmID *)realloc(pmid, numpmid * sizeof(pmID)); + if ((sts = pmLookupName(1, (char **)&name, &pmid[numpmid-1])) < 0) { + fprintf(stderr, "%s: pmLookupName(%s): %s\n", pmProgname, name, pmErrStr(sts)); + numpmid--; + } +} + +static int +overrides(int opt, pmOptions *opts) +{ + if (opt == 'a' || opt == 'L' || opt == 's' || opt == 't') + return 1; + return 0; +} + +int +main(int argc, char *argv[]) +{ + int c; + int sts; + char *rawfile = NULL; + int i; + int ctxid; + int first = 1; + int dflag = 0; + int iflag = 0; + int Lflag = 0; + int lflag = 0; + int mflag = 0; + int tflag = 0; + int vflag = 0; + int mode = PM_MODE_FORW; + __pmContext *ctxp; + pmResult *result; + struct timeval done; + + while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'a': /* dump everything */ + dflag = iflag = lflag = mflag = sflag = tflag = 1; + break; + + case 'd': /* dump pmDesc structures */ + dflag = 1; + break; + + case 'i': /* dump instance domains */ + iflag = 1; + break; + + case 'L': /* dump label, verbose */ + Lflag = 1; + lflag = 1; + break; + + case 'l': /* dump label */ + lflag = 1; + break; + + case 'm': /* dump metrics in log */ + mflag = 1; + break; + + case 'r': /* read log in reverse chornological order */ + mode = PM_MODE_BACK; + break; + + case 's': /* report data size in log */ + sflag = 1; + break; + + case 't': /* dump temporal index */ + tflag = 1; + break; + + case 'v': /* verbose, dump in raw format */ + vflag = 1; + rawfile = opts.optarg; + break; + + case 'x': /* report Ddd Mmm DD <timestamp> YYYY */ + xflag = 1; + break; + } + } + + if (opts.errors || + (vflag && opts.optind != argc) || + (!vflag && opts.optind > argc - 1)) { + pmUsageMessage(&opts); + exit(1); + } + + if (vflag) { + FILE *f; + if ((f = fopen(rawfile, "r")) == NULL) { + fprintf(stderr, "%s: Cannot open \"%s\": %s\n", pmProgname, rawfile, osstrerror()); + exit(1); + } + printf("Raw dump of physical archive file \"%s\" ...\n", rawfile); + rawdump(f); + exit(0); + } + + if (dflag + iflag + lflag + mflag + tflag == 0) + mflag = 1; /* default */ + + /* delay option end processing until now that we have the archive name */ + __pmAddOptArchive(&opts, argv[opts.optind]); + opts.flags &= ~PM_OPTFLAG_DONE; + __pmEndOptions(&opts); + + if ((sts = ctxid = pmNewContext(PM_CONTEXT_ARCHIVE, opts.archives[0])) < 0) { + fprintf(stderr, "%s: Cannot open archive \"%s\": %s\n", + pmProgname, argv[opts.optind], pmErrStr(sts)); + exit(1); + } + /* complete TZ and time window option (origin) setup */ + if (pmGetContextOptions(ctxid, &opts)) { + pmflush(); + exit(1); + } + + opts.optind++; + numpmid = argc - opts.optind; + if (numpmid) { + numpmid = 0; + pmid = NULL; + for (i = 0; opts.optind < argc; i++, opts.optind++) { + numpmid++; + pmid = (pmID *)realloc(pmid, numpmid * sizeof(pmID)); + if ((sts = pmLookupName(1, &argv[opts.optind], &pmid[numpmid-1])) < 0) { + if (sts == PM_ERR_NONLEAF) { + numpmid--; + if ((sts = pmTraversePMNS(argv[opts.optind], dometric)) < 0) + fprintf(stderr, "%s: pmTraversePMNS(%s): %s\n", + pmProgname, argv[opts.optind], pmErrStr(sts)); + } + else + fprintf(stderr, "%s: pmLookupName(%s): %s\n", + pmProgname, argv[opts.optind], pmErrStr(sts)); + if (sts < 0) + numpmid--; + } + } + if (numpmid == 0) { + fprintf(stderr, "No metric names can be translated, dump abandoned\n"); + exit(1); + } + } + + /* + * Note: ctxp->c_lock remains locked throughout ... __pmHandleToPtr() + * is only called once, and a single context is used throughout + * ... so there is no PM_UNLOCK(ctxp->c_lock) anywhere in the + * pmdumplog code. + * This works because ctxp->c_lock is a recursive lock and + * pmdumplog is single-threaded. + */ + if ((ctxp = __pmHandleToPtr(ctxid)) == NULL) { + fprintf(stderr, "%s: botch: __pmHandleToPtr(%d) returns NULL!\n", + pmProgname, ctxid); + exit(1); + } + + pmSetMode(mode, &opts.start, 0); + + if (lflag) + dumpLabel(Lflag); + + if (dflag) + dumpDesc(ctxp); + + if (iflag) + dumpInDom(ctxp); + + if (tflag) + dumpTI(ctxp); + + if (mflag) { + if (mode == PM_MODE_FORW) { + if (opts.start_optarg != NULL || opts.finish_optarg != NULL) { + /* -S or -T */ + sts = pmSetMode(mode, &opts.start, 0); + done = opts.finish; + } + else { + /* read the whole archive */ + done.tv_sec = 0; + done.tv_usec = 0; + sts = pmSetMode(mode, &done, 0); + done.tv_sec = INT_MAX; + } + } + else { + if (opts.start_optarg != NULL || opts.finish_optarg != NULL) { + /* -S or -T */ + done.tv_sec = INT_MAX; + done.tv_usec = 0; + sts = pmSetMode(mode, &done, 0); + done = opts.start; + } + else { + /* read the whole archive backwards */ + done.tv_sec = INT_MAX; + done.tv_usec = 0; + sts = pmSetMode(mode, &done, 0); + done.tv_sec = 0; + } + } + if (sts < 0) { + fprintf(stderr, "%s: pmSetMode: %s\n", pmProgname, pmErrStr(sts)); + exit(1); + } + sts = 0; + for ( ; ; ) { + if (numpmid == 0) + sts = pmFetchArchive(&result); + else + sts = pmFetch(numpmid, pmid, &result); + if (sts < 0) + break; + if (first && mode == PM_MODE_BACK) { + first = 0; + printf("\nLog finished at %24.24s - dump in reverse order\n", + pmCtime(&result->timestamp.tv_sec, timebuf)); + } + if ((mode == PM_MODE_FORW && tvcmp(result->timestamp, done) > 0) || + (mode == PM_MODE_BACK && tvcmp(result->timestamp, done) < 0)) { + sts = PM_ERR_EOL; + break; + } + putchar('\n'); + dump_result(result); + pmFreeResult(result); + } + if (sts != PM_ERR_EOL) { + fprintf(stderr, "%s: pmFetch: %s\n", pmProgname, pmErrStr(sts)); + exit(1); + } + } + + exit(0); +} |