diff options
Diffstat (limited to 'src/pmlogextract/pmlogextract.c')
-rw-r--r-- | src/pmlogextract/pmlogextract.c | 2000 |
1 files changed, 2000 insertions, 0 deletions
diff --git a/src/pmlogextract/pmlogextract.c b/src/pmlogextract/pmlogextract.c new file mode 100644 index 0000000..7c20292 --- /dev/null +++ b/src/pmlogextract/pmlogextract.c @@ -0,0 +1,2000 @@ +/* + * pmlogextract - extract desired metrics from PCP archive logs + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1997-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 <math.h> +#include <ctype.h> +#include <sys/stat.h> +#include <assert.h> +#include "pmapi.h" +#include "impl.h" +#include "logger.h" + +#ifdef PCP_DEBUG +long totalmalloc; +#endif +static pmUnits nullunits; +static int desperate; + +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("Options"), + { "config", 1, 'c', "FILE", "file to load configuration from" }, + { "desperate", 0, 'd', 0, "desperate, save output after fatal error" }, + { "first", 0, 'f', 0, "use timezone from first archive [default is last]" }, + PMOPT_START, + { "samples", 1, 's', "NUM", "terminate after NUM log records have been written" }, + PMOPT_FINISH, + { "", 1, 'v', "SAMPLES", "switch log volumes after this many samples" }, + { "", 0, 'w', 0, "ignore day/month/year" }, + PMOPT_TIMEZONE, + PMOPT_HOSTZONE, + PMAPI_OPTIONS_END +}; + +static pmOptions opts = { + .short_options = "c:D:dfS:s:T:v:wZ:z?", + .long_options = longopts, + .short_usage = "[options] input-archive output-archive", +}; + +const char * +metricname(pmID pmid) +{ + static char *name = NULL; + if (name != NULL) { + free(name); + name = NULL; + } + if (pmNameID(pmid, &name) == 0) + return(name); + name = NULL; + return pmIDStr(pmid); +} + +/* + * global constants + */ +#define LOG 0 +#define META 1 +#define LOG_META 2 +#define NUM_SEC_PER_DAY 86400 + +#define NOT_WRITTEN 0 +#define MARK_FOR_WRITE 1 +#define WRITTEN 2 + +/* + * reclist_t is in logger.h + * (list of pdu's to write out at start of time window) + */ + +/* + * Input archive control is in logger.h + */ + + +/* + * PDU for pmResult (PDU_RESULT) + */ +typedef struct { + pmID pmid; + int numval; /* no. of vlist els to follow, or err */ + int valfmt; /* insitu or pointer */ + __pmValue_PDU vlist[1]; /* zero or more */ +} vlist_t; + +/* + * This struct is not needed? + */ +typedef struct { + /* __pmPDUHdr hdr; */ + __pmPDU hdr; + __pmTimeval timestamp; /* when returned */ + int numpmid; /* no. of PMIDs to follow */ + __pmPDU data[1]; /* zero or more */ +} result_t; + +/* + * Mark record + */ +typedef struct { + __pmPDU len; + __pmPDU type; + __pmPDU from; + __pmTimeval timestamp; /* when returned */ + int numpmid; /* zero PMIDs to follow */ +} mark_t; + + +/* + * Global variables + */ +static int exit_status = 0; +static int inarchvers = PM_LOG_VERS02; /* version of input archive */ +static int outarchvers = PM_LOG_VERS02; /* version of output archive */ +static int first_datarec = 1; /* first record flag */ +static int pre_startwin = 1; /* outside time win flag */ +static int written = 0; /* num log writes so far */ +int ml_numpmid = 0; /* num pmid in ml list */ +int ml_size = 0; /* actual size of ml array */ +mlist_t *ml = NULL; /* list of pmids with indoms */ +rlist_t *rl = NULL; /* list of pmResults */ + + +off_t new_log_offset; /* new log offset */ +off_t new_meta_offset; /* new meta offset */ +off_t old_log_offset; /* old log offset */ +off_t old_meta_offset; /* old meta offset */ +static off_t flushsize = 100000; /* bytes before flush */ + + +/* archive control stuff */ +char *outarchname = NULL; /* name of output archive */ +static __pmHashCtl mdesc_hash; /* pmids that have been written */ +static __pmHashCtl mindom_hash; /* indoms that have been written */ +static __pmLogCtl logctl; /* output archive control */ +inarch_t *inarch; /* input archive control(s) */ +int inarchnum; /* number of input archives */ + +int ilog; /* index of earliest log */ + +static reclist_t *rlog; /* log records to be written */ +static reclist_t *rdesc; /* meta desc records to be written */ +static reclist_t *rindom; /* meta indom records to be written */ + +static __pmTimeval curlog; /* most recent timestamp in log */ +static __pmTimeval current; /* most recent timestamp overall */ + +/* time window stuff */ +static struct timeval logstart_tval = {0,0}; /* extracted log start */ +static struct timeval logend_tval = {0,0}; /* extracted log end */ +static struct timeval winstart_tval = {0,0}; /* window start tval*/ +static struct timeval winend_tval = {0,0}; /* window end tval*/ + +static __pmTimeval winstart = {-1,0}; /* window start time */ +static __pmTimeval winend = {-1,0}; /* window end time */ +static __pmTimeval logend = {-1,0}; /* log end time */ + +/* command line args */ +char *configfile = NULL; /* -c arg - name of config file */ +int farg = 0; /* -f arg - use first timezone */ +int sarg = -1; /* -s arg - finish after X samples */ +char *Sarg = NULL; /* -S arg - window start */ +char *Targ = NULL; /* -T arg - window end */ +int varg = -1; /* -v arg - switch log vol every X */ +int warg = 0; /* -w arg - ignore day/month/year */ +int zarg = 0; /* -z arg - use archive timezone */ +char *tz = NULL; /* -Z arg - use timezone from user */ + +/* cmd line args that could exist, but don't (needed for pmParseTimeWin) */ +char *Aarg = NULL; /* -A arg - non-existent */ +char *Oarg = NULL; /* -O arg - non-existent */ + +/*--- START FUNCTIONS -------------------------------------------------------*/ + +/* + * return -1, 0 or 1 as the __pmTimeval's compare + * a < b, a == b or a > b + */ +static int +tvcmp(__pmTimeval a, __pmTimeval 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 void +abandon() +{ + char fname[MAXNAMELEN]; + if (desperate == 0) { + fprintf(stderr, "Archive \"%s\" not created.\n", outarchname); + while (logctl.l_curvol >= 0) { + snprintf(fname, sizeof(fname), "%s.%d", outarchname, logctl.l_curvol); + unlink(fname); + logctl.l_curvol--; + } + snprintf(fname, sizeof(fname), "%s.meta", outarchname); + unlink(fname); + snprintf(fname, sizeof(fname), "%s.index", outarchname); + unlink(fname); + } + exit(1); +} + + +/* + * report that archive is corrupted + */ +static void +_report(FILE *fp) +{ + off_t here; + struct stat sbuf; + + here = lseek(fileno(fp), 0L, SEEK_CUR); + fprintf(stderr, "%s: Error occurred at byte offset %ld into a file of", + pmProgname, (long int)here); + if (fstat(fileno(fp), &sbuf) < 0) + fprintf(stderr, ": stat: %s\n", osstrerror()); + else + fprintf(stderr, " %ld bytes.\n", (long int)sbuf.st_size); + fprintf(stderr, "The last record, and the remainder of this file will not be extracted.\n"); + abandon(); +} + + +/* + * switch output volumes + */ +static void +newvolume(char *base, __pmTimeval *tvp) +{ + FILE *newfp; + int nextvol = logctl.l_curvol + 1; + + if ((newfp = __pmLogNewFile(base, nextvol)) != NULL) { + struct timeval stamp; + fclose(logctl.l_mfp); + logctl.l_mfp = newfp; + logctl.l_label.ill_vol = logctl.l_curvol = nextvol; + __pmLogWriteLabel(logctl.l_mfp, &logctl.l_label); + fflush(logctl.l_mfp); + stamp.tv_sec = ntohl(tvp->tv_sec); + stamp.tv_usec = ntohl(tvp->tv_usec); + fprintf(stderr, "%s: New log volume %d, at ", pmProgname, nextvol); + __pmPrintStamp(stderr, &stamp); + fputc('\n', stderr); + } + else { + fprintf(stderr, "%s: Error: volume %d: %s\n", + pmProgname, nextvol, pmErrStr(-oserror())); + abandon(); + } + flushsize = 100000; +} + + +/* + * construct new external label, and check label records from + * input archives + */ +static void +newlabel(void) +{ + int i; + inarch_t *iap; + __pmLogLabel *lp = &logctl.l_label; + + /* set outarch to inarch[0] to start off with */ + iap = &inarch[0]; + + /* check version number */ + inarchvers = iap->label.ll_magic & 0xff; + outarchvers = inarchvers; + + if (inarchvers != PM_LOG_VERS02) { + fprintf(stderr,"%s: Error: illegal version number %d in archive (%s)\n", + pmProgname, inarchvers, iap->name); + abandon(); + } + + /* copy magic number, pid, host and timezone */ + lp->ill_magic = iap->label.ll_magic; + lp->ill_pid = (int)getpid(); + strncpy(lp->ill_hostname, iap->label.ll_hostname, PM_LOG_MAXHOSTLEN); + lp->ill_hostname[PM_LOG_MAXHOSTLEN-1] = '\0'; + if (farg) { + /* + * use timezone from first archive ... this is the OLD default + */ + strcpy(lp->ill_tz, iap->label.ll_tz); + } + else { + /* + * use timezone from last archive ... this is the NEW default + */ + strcpy(lp->ill_tz, inarch[inarchnum-1].label.ll_tz); + } + + /* reset outarch as appropriate, depending on other input archives */ + for (i=0; i<inarchnum; i++) { + iap = &inarch[i]; + + /* Ensure all archives of the same version number */ + if ((iap->label.ll_magic & 0xff) != inarchvers) { + fprintf(stderr, + "%s: Error: input archives with different version numbers\n" + "archive: %s version: %d\n" + "archive: %s version: %d\n", + pmProgname, inarch[0].name, inarchvers, + iap->name, (iap->label.ll_magic & 0xff)); + abandon(); + } + + /* Ensure all archives of the same host */ + if (strcmp(lp->ill_hostname, iap->label.ll_hostname) != 0) { + fprintf(stderr,"%s: Error: host name mismatch for input archives\n", + pmProgname); + fprintf(stderr, "archive: %s host: %s\n", + inarch[0].name, inarch[0].label.ll_hostname); + fprintf(stderr, "archive: %s host: %s\n", + iap->name, iap->label.ll_hostname); + abandon(); + } + + /* Ensure all archives of the same timezone */ + if (strcmp(lp->ill_tz, iap->label.ll_tz) != 0) { + fprintf(stderr, + "%s: Warning: timezone mismatch for input archives\n", + pmProgname); + if (farg) { + fprintf(stderr, "archive: %s timezone: %s [will be used]\n", + inarch[0].name, lp->ill_tz); + fprintf(stderr, "archive: %s timezone: %s [will be ignored]\n", + iap->name, iap->label.ll_tz); + } + else { + fprintf(stderr, "archive: %s timezone: %s [will be used]\n", + inarch[inarchnum-1].name, lp->ill_tz); + fprintf(stderr, "archive: %s timezone: %s [will be ignored]\n", + iap->name, iap->label.ll_tz); + } + } + } /*for(i)*/ +} + + +/* + * + */ +void +writelabel_metati(int do_rewind) +{ + if (do_rewind) rewind(logctl.l_tifp); + logctl.l_label.ill_vol = PM_LOG_VOL_TI; + __pmLogWriteLabel(logctl.l_tifp, &logctl.l_label); + + if (do_rewind) rewind(logctl.l_mdfp); + logctl.l_label.ill_vol = PM_LOG_VOL_META; + __pmLogWriteLabel(logctl.l_mdfp, &logctl.l_label); +} + + +/* + * + */ +void +writelabel_data(void) +{ + logctl.l_label.ill_vol = 0; + __pmLogWriteLabel(logctl.l_mfp, &logctl.l_label); +} + + +/* --- Start of reclist functions --- */ + +/* + * make a reclist_t record + */ +static reclist_t * +mk_reclist_t(void) +{ + reclist_t *rec; + + if ((rec = (reclist_t *)malloc(sizeof(reclist_t))) == NULL) { + fprintf(stderr, "%s: Error: cannot malloc space for record list.\n", + pmProgname); + abandon(); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + totalmalloc += sizeof(reclist_t); + fprintf(stderr, "mk_reclist_t: allocated %d\n", (int)sizeof(reclist_t)); + } +#endif + rec->pdu = NULL; + rec->desc.pmid = PM_ID_NULL; + rec->desc.type = PM_TYPE_NOSUPPORT; + rec->desc.indom = PM_IN_NULL; + rec->desc.sem = 0; + rec->desc.units = nullunits; /* struct assignment */ + rec->written = NOT_WRITTEN; + rec->ptr = NULL; + rec->next = NULL; + return(rec); +} + +/* + * find indom in indomreclist - if it isn't in the list then add it in + * with no pdu buffer + */ +static reclist_t * +findnadd_indomreclist(int indom) +{ + reclist_t *curr; + + if (rindom == NULL) { + rindom = mk_reclist_t(); + rindom->desc.pmid = PM_ID_NULL; + rindom->desc.type = PM_TYPE_NOSUPPORT; + rindom->desc.indom = indom; + rindom->desc.sem = 0; + rindom->desc.units = nullunits; /* struct assignment */ + return(rindom); + } + else { + curr = rindom; + + /* find matching record or last record */ + while (curr->next != NULL && curr->desc.indom != indom) + curr = curr->next; + + if (curr->desc.indom == indom) { + /* we have found a matching record - return the pointer */ + return(curr); + } + else { + /* we have not found a matching record - append new record */ + curr->next = mk_reclist_t(); + curr = curr->next; + curr->desc.pmid = PM_ID_NULL; + curr->desc.type = PM_TYPE_NOSUPPORT; + curr->desc.indom = indom; + curr->desc.sem = 0; + curr->desc.units = nullunits; /* struct assignment */ + return(curr); + } + } + +} + +/* + * append a new record to the log record list + */ +void +append_logreclist(int i) +{ + inarch_t *iap; + reclist_t *curr; + + iap = &inarch[i]; + + if (rlog == NULL) { + rlog = mk_reclist_t(); + rlog->pdu = iap->pb[LOG]; + } + else { + curr = rlog; + + /* find matching record or last record */ + while (curr->next != NULL && + curr->pdu[4] != iap->pb[LOG][4]) curr = curr->next; + + if (curr->pdu[4] == iap->pb[LOG][4]) { + /* LOG: discard old record; insert new record */ + __pmUnpinPDUBuf(curr->pdu); + curr->pdu = iap->pb[LOG]; + } + else { + curr->next = mk_reclist_t(); + curr = curr->next; + curr->pdu = iap->pb[LOG]; + } + } /*else*/ + + iap->pb[LOG] = NULL; +} + +/* + * append a new record to the desc meta record list if not seen + * before, else check the desc meta record is semantically the + * same as the last desc meta record for this pmid from this source + */ +void +update_descreclist(int i) +{ + inarch_t *iap; + reclist_t *curr; + pmUnits pmu; + pmUnits *pmup; + + iap = &inarch[i]; + + if (rdesc == NULL) { + /* first time */ + curr = rdesc = mk_reclist_t(); + } + else { + curr = rdesc; + /* find matching record or last record */ + while (curr->next != NULL && curr->desc.pmid != ntoh_pmID(iap->pb[META][2])) + curr = curr->next; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "update_descreclist: pmid: last/match %s", metricname(curr->desc.pmid)); + fprintf(stderr, " new %s", metricname(ntoh_pmID(iap->pb[META][2]))); + fputc('\n', stderr); + } +#endif + } + + if (curr->desc.pmid == ntoh_pmID(iap->pb[META][2])) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, " type: old %s", pmTypeStr(curr->desc.type)); + fprintf(stderr, " new %s", pmTypeStr(ntohl(iap->pb[META][3]))); + fprintf(stderr, " indom: old %s", pmInDomStr(curr->desc.indom)); + fprintf(stderr, " new %s", pmInDomStr(ntoh_pmInDom(iap->pb[META][4]))); + fprintf(stderr, " sem: old %d", curr->desc.sem); + fprintf(stderr, " new %d", (int)ntohl(iap->pb[META][5])); + fprintf(stderr, " units: old %s", pmUnitsStr(&curr->desc.units)); + pmup = (pmUnits *)&iap->pb[META][6]; + pmu = ntoh_pmUnits(*pmup); + fprintf(stderr, " new %s", pmUnitsStr(&pmu)); + fputc('\n', stderr); + } +#endif + if (curr->desc.type != ntohl(iap->pb[META][3])) { + fprintf(stderr, + "%s: Error: metric %s: type changed from", + pmProgname, metricname(curr->desc.pmid)); + fprintf(stderr, " %s", pmTypeStr(curr->desc.type)); + fprintf(stderr, " to %s!\n", pmTypeStr(ntohl(iap->pb[META][3]))); + abandon(); + } + if (curr->desc.indom != ntoh_pmInDom(iap->pb[META][4])) { + fprintf(stderr, + "%s: Error: metric %s: indom changed from", + pmProgname, metricname(curr->desc.pmid)); + fprintf(stderr, " %s", pmInDomStr(curr->desc.indom)); + fprintf(stderr, " to %s!\n", pmInDomStr(ntoh_pmInDom(iap->pb[META][4]))); + abandon(); + } + if (curr->desc.sem != ntohl(iap->pb[META][5])) { + fprintf(stderr, + "%s: Error: metric %s: semantics changed from", + pmProgname, metricname(curr->desc.pmid)); + fprintf(stderr, " %d", curr->desc.sem); + fprintf(stderr, " to %d!\n", (int)ntohl(iap->pb[META][5])); + abandon(); + } + pmup = (pmUnits *)&iap->pb[META][6]; + pmu = ntoh_pmUnits(*pmup); + if (curr->desc.units.dimSpace != pmu.dimSpace || + curr->desc.units.dimTime != pmu.dimTime || + curr->desc.units.dimCount != pmu.dimCount || + curr->desc.units.scaleSpace != pmu.scaleSpace || + curr->desc.units.scaleTime != pmu.scaleTime || + curr->desc.units.scaleCount != pmu.scaleCount) { + fprintf(stderr, + "%s: Error: metric %s: units changed from", + pmProgname, metricname(curr->desc.pmid)); + fprintf(stderr, " %s", pmUnitsStr(&curr->desc.units)); + fprintf(stderr, " to %s!\n", pmUnitsStr(&pmu)); + abandon(); + } + /* not adding, so META: discard new record */ + free(iap->pb[META]); + iap->pb[META] = NULL; + } + else { + /* append new record */ + curr->next = mk_reclist_t(); + curr = curr->next; + curr->pdu = iap->pb[META]; + curr->desc.pmid = ntoh_pmID(iap->pb[META][2]); + curr->desc.type = ntohl(iap->pb[META][3]); + curr->desc.indom = ntoh_pmInDom(iap->pb[META][4]); + curr->desc.sem = ntohl(iap->pb[META][5]); + pmup =(pmUnits *)&iap->pb[META][6]; + curr->desc.units = ntoh_pmUnits(*pmup); + curr->ptr = findnadd_indomreclist(curr->desc.indom); + iap->pb[META] = NULL; + } +} + +/* + * append a new record to the indom meta record list + */ +void +append_indomreclist(int i) +{ + inarch_t *iap; + reclist_t *curr; + reclist_t *rec; + + iap = &inarch[i]; + + if (rindom == NULL) { + rindom = mk_reclist_t(); + rindom->pdu = iap->pb[META]; + rindom->stamp.tv_sec = ntohl(rindom->pdu[2]); + rindom->stamp.tv_usec = ntohl(rindom->pdu[3]); + rindom->desc.pmid = PM_ID_NULL; + rindom->desc.type = PM_TYPE_NOSUPPORT; + rindom->desc.indom = ntoh_pmInDom(iap->pb[META][4]); + rindom->desc.sem = 0; + rindom->desc.units = nullunits; /* struct assignment */ + } + else { + curr = rindom; + + /* find matching record or last record */ + while (curr->next != NULL && curr->desc.indom != ntoh_pmInDom(iap->pb[META][4])) { + curr = curr->next; + } + + if (curr->desc.indom == ntoh_pmInDom(iap->pb[META][4])) { + if (curr->pdu == NULL) { + /* insert new record */ + curr->pdu = iap->pb[META]; + curr->stamp.tv_sec = ntohl(curr->pdu[2]); + curr->stamp.tv_usec = ntohl(curr->pdu[3]); + } + else { + /* do NOT discard old record; insert new record */ + rec = mk_reclist_t(); + rec->pdu = iap->pb[META]; + rec->stamp.tv_sec = ntohl(rec->pdu[2]); + rec->stamp.tv_usec = ntohl(rec->pdu[3]); + rec->desc.pmid = PM_ID_NULL; + rec->desc.type = PM_TYPE_NOSUPPORT; + rec->desc.indom = ntoh_pmInDom(iap->pb[META][4]); + rec->desc.sem = 0; + rec->desc.units = nullunits; /* struct assignment */ + rec->next = curr->next; + curr->next = rec; + } + } + else { + /* append new record */ + curr->next = mk_reclist_t(); + curr = curr->next; + curr->pdu = iap->pb[META]; + curr->stamp.tv_sec = ntohl(curr->pdu[2]); + curr->stamp.tv_usec = ntohl(curr->pdu[3]); + curr->desc.pmid = PM_ID_NULL; + curr->desc.type = PM_TYPE_NOSUPPORT; + curr->desc.indom = ntoh_pmInDom(iap->pb[META][4]); + curr->desc.sem = 0; + curr->desc.units = nullunits; /* struct assignment */ + } + } /*else*/ + + iap->pb[META] = NULL; +} + +/* + * write out one desc/indom record + */ +void +write_rec(reclist_t *rec) +{ + int sts; + + if (rec->written == MARK_FOR_WRITE) { + if (rec->pdu == NULL) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr," record is marked for write, but pdu is NULL\n"); + abandon(); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOGMETA) { + __pmLogHdr *h; + int len; + int type; + h = (__pmLogHdr *)rec->pdu; + len = ntohl(h->len); + type = ntohl(h->type); + fprintf(stderr, "write_rec: record len=%d, type=%d @ offset=%d\n", + len, type, (int)(ftell(logctl.l_mdfp) - sizeof(__pmLogHdr))); + if (type == TYPE_DESC) { + pmDesc *dp; + pmDesc desc; + int *namelen; + char *name; /* just first name for diag */ + dp = (pmDesc *)((void *)rec->pdu + sizeof(__pmLogHdr)); + desc.type = ntohl(dp->type); + desc.sem = ntohl(dp->sem); + desc.indom = ntoh_pmInDom(dp->indom); + desc.units = ntoh_pmUnits(dp->units); + desc.pmid = ntoh_pmID(dp->pmid); + namelen = (int *)((void *)rec->pdu + sizeof(__pmLogHdr) + sizeof(pmDesc) + sizeof(int)); + len = ntohl(*namelen); + name = (char *)((void *)rec->pdu + sizeof(__pmLogHdr) + sizeof(pmDesc) + sizeof(int) + sizeof(int)); + fprintf(stderr, "PMID: %s name: %*.*s\n", pmIDStr(desc.pmid), len, len, name); + __pmPrintDesc(stderr, &desc); + } + else if (type == TYPE_INDOM) { + __pmTimeval *tvp; + __pmTimeval when; + int k = 2; + pmInDom indom; + int numinst; + int *instlist; + int inst; + + tvp = (__pmTimeval *)&rec->pdu[k]; + when.tv_sec = ntohl(tvp->tv_sec); + when.tv_usec = ntohl(tvp->tv_usec); + k += sizeof(__pmTimeval)/sizeof(rec->pdu[0]); + indom = ntoh_pmInDom((unsigned int)rec->pdu[k++]); + fprintf(stderr, "INDOM: %s when: ", pmInDomStr(indom)); + __pmPrintTimeval(stderr, &when); + numinst = ntohl(rec->pdu[k++]); + fprintf(stderr, " numinst: %d", numinst); + if (numinst > 0) { + int i; + instlist = (int *)&rec->pdu[k]; + for (i = 0; i < numinst; i++) { + inst = ntohl(instlist[i]); + fprintf(stderr, " [%d] %d", i, inst); + } + } + fputc('\n', stderr); + } + else { + fprintf(stderr, "Botch: bad type\n"); + } + } +#endif + + /* write out the pdu ; exit if write failed */ + if ((sts = _pmLogPut(logctl.l_mdfp, rec->pdu)) < 0) { + fprintf(stderr, "%s: Error: _pmLogPut: meta data : %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + } + /* META: free PDU buffer */ + free(rec->pdu); + rec->pdu = NULL; + rec->written = WRITTEN; + } + else { + fprintf(stderr, + "%s : Warning: attempting to write out meta record (%d,%d)\n", + pmProgname, rec->desc.pmid, rec->desc.indom); + fprintf(stderr, " when it is not marked for writing (%d)\n", + rec->written); + } +} + +void +write_metareclist(pmResult *result, int *needti) +{ + int i; + reclist_t *curr_desc; /* current desc record */ + reclist_t *curr_indom; /* current indom record */ + reclist_t *othr_indom; /* other indom record */ + pmID pmid; + pmInDom indom; + struct timeval *this; /* ptr to timestamp in result */ + + this = &result->timestamp; + + /* if pmid in result matches a pmid in desc then write desc + */ + for (i=0; i<result->numpmid; i++) { + pmid = result->vset[i]->pmid; + indom = PM_IN_NULL; + curr_indom = NULL; + + curr_desc = rdesc; + while (curr_desc != NULL && curr_desc->desc.pmid != pmid) + curr_desc = curr_desc->next; + + if (curr_desc == NULL) { + /* descriptor has not been found - this is bad + */ + fprintf(stderr, "%s: Error: meta data (TYPE_DESC) for pmid %s has not been found.\n", pmProgname, pmIDStr(pmid)); + abandon(); + } + else { + /* descriptor has been found + */ + if (curr_desc->written == WRITTEN) { + /* descriptor has been written before (no need to write again) + * but still need to check indom + */ + indom = curr_desc->desc.indom; + curr_indom = curr_desc->ptr; + } + else if (curr_desc->pdu == NULL) { + /* descriptor is in list, has not been written, but no pdu + * - this is bad + */ + fprintf(stderr, "%s: Error: missing pdu for pmid %s\n", + pmProgname, pmIDStr(pmid)); + abandon(); + } + else { + /* descriptor is in list, has not been written, and has pdu + * write! + */ + curr_desc->written = MARK_FOR_WRITE; + write_rec(curr_desc); + indom = curr_desc->desc.indom; + curr_indom = curr_desc->ptr; + } + } + + /* descriptor has been found and written, + * now go and find & write the indom + */ + if (indom != PM_INDOM_NULL) { + /* there may be more than one indom in the list, so we need + * to traverse the entire list + * - we can safely ignore all indoms after the current timestamp + * - we want the latest indom at, or before the current timestamp + */ + othr_indom = NULL; + while (curr_indom != NULL && curr_indom->desc.indom == indom) { + if (curr_indom->stamp.tv_sec < this->tv_sec || + (curr_indom->stamp.tv_sec == this->tv_sec && + curr_indom->stamp.tv_usec <= this->tv_usec)) + { + /* indom is in list, indom has pdu + * and timestamp in pdu suits us + */ + if (othr_indom == NULL) { + othr_indom = curr_indom; + } + else if (othr_indom->stamp.tv_sec < curr_indom->stamp.tv_sec || + (othr_indom->stamp.tv_sec == curr_indom->stamp.tv_sec && + othr_indom->stamp.tv_usec <= curr_indom->stamp.tv_usec)) + { + /* we already have a perfectly good indom, + * but curr_indom has a better timestamp + */ + othr_indom = curr_indom; + } + } + curr_indom = curr_indom->next; + } /*while()*/ + + if (othr_indom != NULL && othr_indom->pdu != NULL && othr_indom->written != WRITTEN) { + othr_indom->written = MARK_FOR_WRITE; + othr_indom->pdu[2] = htonl(this->tv_sec); + othr_indom->pdu[3] = htonl(this->tv_usec); + + /* make sure to set needti, when writing out the indom + */ + *needti = 1; + write_rec(othr_indom); + } + } + } /*for(i)*/ +} + +/* --- End of reclist functions --- */ + +/* + * create a mark record + */ +__pmPDU * +_createmark(void) +{ + mark_t *markp; + + /* + * add space for trailer in case __pmLogPutResult2() is called with + * this PDU buffer + */ + markp = (mark_t *)malloc(sizeof(mark_t)+sizeof(int)); + if (markp == NULL) { + fprintf(stderr, "%s: Error: mark_t malloc: %s\n", + pmProgname, osstrerror()); + abandon(); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + totalmalloc += sizeof(mark_t); + fprintf(stderr, "_createmark : allocated %d\n", (int)sizeof(mark_t)); + } +#endif + + markp->len = (int)sizeof(mark_t); + markp->type = markp->from = 0; + markp->timestamp = current; + markp->timestamp.tv_usec += 1000; /* + 1msec */ + if (markp->timestamp.tv_usec > 1000000) { + markp->timestamp.tv_usec -= 1000000; + markp->timestamp.tv_sec++; + } + markp->numpmid = 0; + return((__pmPDU *)markp); +} + +void +checklogtime(__pmTimeval *this, int i) +{ + if ((curlog.tv_sec == 0 && curlog.tv_usec == 0) || + (curlog.tv_sec > this->tv_sec || + (curlog.tv_sec == this->tv_sec && curlog.tv_usec > this->tv_usec))) { + ilog = i; + curlog.tv_sec = this->tv_sec; + curlog.tv_usec = this->tv_usec; + } +} + + +/* + * pick next meta record - if all meta is at EOF return -1 + * (normally this function returns 0) + */ +static int +nextmeta(void) +{ + int i; + int j; + int want; + int numeof = 0; + int sts; + pmID pmid; /* pmid for TYPE_DESC */ + pmInDom indom; /* indom for TYPE_INDOM */ + __pmLogCtl *lcp; + __pmContext *ctxp; + inarch_t *iap; /* pointer to input archive control */ + + for (i=0; i<inarchnum; i++) { + iap = &inarch[i]; + + /* if at the end of meta file then skip this archive + */ + if (iap->eof[META]) { + ++numeof; + continue; + } + + /* we should never already have a meta record + */ + if (iap->pb[META] != NULL) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr, " iap->pb[META] is not NULL\n"); + abandon(); + } + if ((ctxp = __pmHandleToPtr(iap->ctx)) == NULL) { + fprintf(stderr, "%s: botch: __pmHandleToPtr(%d) returns NULL!\n", pmProgname, iap->ctx); + abandon(); + } + lcp = ctxp->c_archctl->ac_log; + +againmeta: + /* get next meta record */ + + if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &iap->pb[META])) < 0) { + iap->eof[META] = 1; + ++numeof; + if (sts != PM_ERR_EOL) { + fprintf(stderr, "%s: Error: _pmLogGet[meta %s]: %s\n", + pmProgname, iap->name, pmErrStr(sts)); + _report(lcp->l_mdfp); + } + PM_UNLOCK(ctxp->c_lock); + continue; + } + + /* pmDesc entries, if not seen before & wanted, + * then append to desc list + */ + if (ntohl(iap->pb[META][1]) == TYPE_DESC) { + pmid = ntoh_pmID(iap->pb[META][2]); + + /* if ml is defined, then look for pmid in the list + * if pmid is not in the list then discard it immediately + */ + want = 0; + if (ml == NULL) + want = 1; + else { + for (j=0; j<ml_numpmid; j++) { + if (pmid == ml[j].idesc->pmid) + want = 1; + } + } + + if (want) { + if (__pmHashSearch((int)pmid, &mdesc_hash) == NULL) + __pmHashAdd((int)pmid, NULL, &mdesc_hash); + /* + * update the desc list (add first time, check on subsequent + * sightings of desc for this pmid from this source + * update_descreclist() sets pb[META] to NULL + */ + update_descreclist(i); + } + else { + /* not wanted */ + free(iap->pb[META]); + iap->pb[META] = NULL; + goto againmeta; + } + } + else if (ntohl(iap->pb[META][1]) == TYPE_INDOM) { + /* if ml is defined, then look for instance domain in the list + * if indom is not in the list then discard it immediately + */ + indom = ntoh_pmInDom(iap->pb[META][4]); + want = 0; + if (ml == NULL) + want = 1; + else { + for (j=0; j<ml_numpmid; j++) { + if (indom == ml[j].idesc->indom) + want = 1; + } + } + + if (want) { + if (__pmHashSearch((int)indom, &mindom_hash) == NULL) { + /* meta record has never been seen ... add it to the list */ + __pmHashAdd((int)indom, NULL, &mindom_hash); + } + /* add to indom list */ + /* append_indomreclist() sets pb[META] to NULL + * append_indomreclist() may unpin the pdu buffer + */ + append_indomreclist(i); + } + else { + /* META: don't want this meta */ + free(iap->pb[META]); + iap->pb[META] = NULL; + goto againmeta; + } + } + else { + fprintf(stderr, "%s: Error: unrecognised meta data type: %d\n", + pmProgname, (int)ntohl(iap->pb[META][1])); + abandon(); + } + + PM_UNLOCK(ctxp->c_lock); + } + + if (numeof == inarchnum) return(-1); + return(0); +} + + +/* + * read in next log record for every archive + */ +static int +nextlog(void) +{ + int i; + int eoflog = 0; /* number of log files at eof */ + int sts; + __pmTimeval curtime; + __pmLogCtl *lcp; + __pmContext *ctxp; + inarch_t *iap; + + + for (i=0; i<inarchnum; i++) { + iap = &inarch[i]; + + /* if at the end of log file then skip this archive + */ + if (iap->eof[LOG]) { + ++eoflog; + continue; + } + + /* if we already have a log record then skip this archive + */ + if (iap->_Nresult != NULL) { + continue; + } + + /* if mark has been written out, then log is at EOF + */ + if (iap->mark) { + iap->eof[LOG] = 1; + ++eoflog; + continue; + } + + if ((ctxp = __pmHandleToPtr(iap->ctx)) == NULL) { + fprintf(stderr, "%s: botch: __pmHandleToPtr(%d) returns NULL!\n", pmProgname, iap->ctx); + abandon(); + } + lcp = ctxp->c_archctl->ac_log; + +againlog: + if ((sts=__pmLogRead(lcp, PM_MODE_FORW, NULL, &iap->_result, PMLOGREAD_NEXT)) < 0) { + if (sts != PM_ERR_EOL) { + fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n", + pmProgname, iap->name, pmErrStr(sts)); + _report(lcp->l_mfp); + } + /* if the first data record has not been written out, then + * do not generate a mark record, and you may as well ignore + * this archive + */ + if (first_datarec) { + iap->mark = 1; + iap->eof[LOG] = 1; + ++eoflog; + } + else { + iap->mark = 1; + iap->pb[LOG] = _createmark(); + } + PM_UNLOCK(ctxp->c_lock); + continue; + } + assert(iap->_result != NULL); + + + /* set current log time - this is only done so that we can + * determine whether to keep or discard the log + */ + curtime.tv_sec = iap->_result->timestamp.tv_sec; + curtime.tv_usec = iap->_result->timestamp.tv_usec; + + /* if log time is greater than (or equal to) the current window + * start time, then we may want it + * (irrespective of the current window end time) + */ + if (tvcmp(curtime, winstart) < 0) { + /* log is not in time window - discard result and get next record + */ + pmFreeResult(iap->_result); + iap->_result = NULL; + goto againlog; + } + else { + /* log is within time window - check whether we want this record + */ + if (iap->_result->numpmid == 0) { + /* mark record, process this one as is + */ + iap->_Nresult = iap->_result; + } + else if (ml == NULL) { + /* ml is NOT defined, we want everything + */ + iap->_Nresult = iap->_result; + } + else { + /* ml is defined, need to search metric list for wanted pmid's + * (searchmlist may return a NULL pointer - this is fine) + */ + iap->_Nresult = searchmlist(iap->_result); + } + + if (iap->_Nresult == NULL) { + /* dont want any of the metrics in _result, try again + */ + pmFreeResult(iap->_result); + iap->_result = NULL; + goto againlog; + } + } + + PM_UNLOCK(ctxp->c_lock); + } /*for(i)*/ + + /* if we are here, then each archive control struct should either + * be at eof, or it should have a _result, or it should have a mark PDU + * (if we have a _result, we may want all/some/none of the pmid's in it) + */ + + if (eoflog == inarchnum) return(-1); + return 0; +} + +/* + * parse command line arguments + */ +int +parseargs(int argc, char *argv[]) +{ + int c; + int sts; + char *endnum; + struct stat sbuf; + + while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'c': /* config file */ + configfile = opts.optarg; + if (stat(configfile, &sbuf) < 0) { + pmprintf("%s: %s - invalid file\n", pmProgname, configfile); + opts.errors++; + } + break; + + case 'D': /* debug flag */ + sts = __pmParseDebug(opts.optarg); + if (sts < 0) { + pmprintf("%s: unrecognized debug flag specification (%s)\n", + pmProgname, opts.optarg); + opts.errors++; + } + else + pmDebug |= sts; + break; + + case 'd': /* desperate to save output archive, even after error */ + desperate = 1; + break; + + case 'f': /* use timezone from first archive */ + farg = 1; + break; + + case 's': /* number of samples to write out */ + sarg = (int)strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0' || sarg < 0) { + pmprintf("%s: -s requires numeric argument\n", pmProgname); + opts.errors++; + } + break; + + case 'S': /* start time for extracting */ + Sarg = opts.optarg; + break; + + case 'T': /* end time for extracting */ + Targ = opts.optarg; + break; + + case 'v': /* number of samples per volume */ + varg = (int)strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0' || varg < 0) { + pmprintf("%s: -v requires numeric argument\n", pmProgname); + opts.errors++; + } + break; + + case 'w': /* ignore day/month/year */ + warg++; + break; + + case 'Z': /* use timezone from command line */ + if (zarg) { + pmprintf("%s: at most one of -Z and/or -z allowed\n", + pmProgname); + opts.errors++; + } + tz = opts.optarg; + break; + + case 'z': /* use timezone from archive */ + if (tz != NULL) { + pmprintf("%s: at most one of -Z and/or -z allowed\n", + pmProgname); + opts.errors++; + } + zarg++; + break; + + case '?': + default: + opts.errors++; + break; + } + } + + if (warg) { + if (Sarg == NULL || Targ == NULL) { + fprintf(stderr, "%s: Warning: -w flag requires that both -S and -T are specified.\nIgnoring -w flag.\n", pmProgname); + warg = 0; + } + } + + + if (opts.errors == 0 && opts.optind > argc - 2) { + pmprintf("%s: Error: insufficient arguments\n", pmProgname); + opts.errors++; + } + + return -opts.errors; +} + +int +parseconfig(void) +{ + int errflag = 0; + + if ((yyin = fopen(configfile, "r")) == NULL) { + fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + + if (yyparse() != 0) + exit(1); + + fclose(yyin); + yyin = NULL; + + return(-errflag); +} + +/* + * we are within time window ... return 0 + * we are outside of time window & mk new window ... return 1 + * we are outside of time window & exit ... return -1 + */ +static int +checkwinend(__pmTimeval now) +{ + int i; + int sts; + __pmTimeval tmptime; + inarch_t *iap; + __pmPDU *markpdu; /* mark b/n time windows */ + + if (winend.tv_sec < 0 || tvcmp(now, winend) <= 0) + return(0); + + /* we have reached the end of a window + * - if warg is not set, then we have finished (break) + * - otherwise, calculate start and end of next window, + * set pre_startwin, discard logs before winstart, + * and write out mark + */ + if (!warg) + return(-1); + + winstart.tv_sec += NUM_SEC_PER_DAY; + winend.tv_sec += NUM_SEC_PER_DAY; + pre_startwin = 1; + + /* if start of next window is later than max termination + * then bail out here + */ + if (tvcmp(winstart, logend) > 0) + return(-1); + + ilog = -1; + for (i=0; i<inarchnum; i++) { + iap = &inarch[i]; + if (iap->_Nresult != NULL) { + tmptime.tv_sec = iap->_Nresult->timestamp.tv_sec; + tmptime.tv_usec = iap->_Nresult->timestamp.tv_usec; + if (tvcmp(tmptime, winstart) < 0) { + /* free _result and _Nresult + */ + if (iap->_result != iap->_Nresult) { + free(iap->_Nresult); + } + if (iap->_result != NULL) { + pmFreeResult(iap->_result); + iap->_result = NULL; + } + iap->_Nresult = NULL; + iap->pb[LOG] = NULL; + } + } + if (iap->pb[LOG] != NULL) { + tmptime.tv_sec = ntohl(iap->pb[LOG][3]); + tmptime.tv_usec = ntohl(iap->pb[LOG][4]); + if (tvcmp(tmptime, winstart) < 0) { + /* free PDU buffer ... it is probably a mark + * and has not been pinned + */ + free(iap->pb[LOG]); + iap->pb[LOG] = NULL; + } + } + } /*for(i)*/ + + /* must create "mark" record and write it out */ + /* (need only one mark record) */ + markpdu = _createmark(); + if ((sts = __pmLogPutResult2(&logctl, markpdu)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutResult2: log data: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + } + written++; + free(markpdu); + return(1); +} + + +/* + * + */ +void +writerlist(rlist_t **rlready, __pmTimeval mintime) +{ + int sts; + int needti = 0; /* need to flush/update */ + __pmTimeval titime = {0,0};/* time of last temporal index write */ + __pmTimeval restime; /* time of result */ + rlist_t *elm; /* element of rlready to be written out */ + __pmPDU *pb; /* pdu buffer */ + unsigned long peek_offset; + + while (*rlready != NULL) { + restime.tv_sec = (*rlready)->res->timestamp.tv_sec; + restime.tv_usec = (*rlready)->res->timestamp.tv_usec; + + if (tvcmp(restime, mintime) > 0) { +#if 0 +fprintf(stderr, "writelist: restime %d.%06d mintime %d.%06d ", restime.tv_sec, restime.tv_usec, mintime.tv_sec, mintime.tv_usec); +fprintf(stderr, " break!\n"); +#endif + break; + } + + /* get the first element from the list + */ + elm = *rlready; + *rlready = elm->next; + + /* if this is the first record (for output archive) then do some + * admin stuff + */ + if (first_datarec) { + first_datarec = 0; + logctl.l_label.ill_start.tv_sec = elm->res->timestamp.tv_sec; + logctl.l_label.ill_start.tv_usec = elm->res->timestamp.tv_usec; + logctl.l_state = PM_LOG_STATE_INIT; + writelabel_data(); + } + + /* if we are in a pre_startwin state, and we are writing + * something out, then we are not in a pre_startwin state any more + * (it also means that there may be some discrete metrics to be + * written out) + */ + if (pre_startwin) + pre_startwin = 0; + + + /* convert log record to a pdu + */ + sts = __pmEncodeResult(PDU_OVERRIDE2, elm->res, &pb); + if (sts < 0) { + fprintf(stderr, "%s: Error: __pmEncodeResult: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + } + + /* switch volumes if required */ + if (varg > 0) { + if (written > 0 && (written % varg) == 0) { + newvolume(outarchname, (__pmTimeval *)&pb[3]); + } + } + /* + * Even without a -v option, we may need to switch volumes + * if the data file exceeds 2^31-1 bytes + */ + peek_offset = ftell(logctl.l_mfp); + peek_offset += ((__pmPDUHdr *)pb)->len - sizeof(__pmPDUHdr) + 2*sizeof(int); + if (peek_offset > 0x7fffffff) { + newvolume(outarchname, (__pmTimeval *)&pb[3]); + } + + /* write out the descriptor and instance domain pdu's first + */ + write_metareclist(elm->res, &needti); + + /* write out log record */ + old_log_offset = ftell(logctl.l_mfp); + assert(old_log_offset >= 0); + if ((sts = __pmLogPutResult2(&logctl, pb)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutResult2: log data: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + } + written++; + + + /* check whether we need to write TI (temporal index) */ + if (old_log_offset == 0 || + old_log_offset == sizeof(__pmLogLabel)+2*sizeof(int) || + ftell(logctl.l_mfp) > flushsize) + needti = 1; + + /* make sure that we do not write out the temporal index more + * than once for the same timestamp + */ + if (needti && tvcmp(titime, restime) >= 0) + needti = 0; + + /* flush/update */ + if (needti) { + titime = restime; + + fflush(logctl.l_mfp); + fflush(logctl.l_mdfp); + + if (old_log_offset == 0) + old_log_offset = sizeof(__pmLogLabel)+2*sizeof(int); + + new_log_offset = ftell(logctl.l_mfp); + assert(new_log_offset >= 0); + new_meta_offset = ftell(logctl.l_mdfp); + assert(new_meta_offset >= 0); + + fseek(logctl.l_mfp, (long)old_log_offset, SEEK_SET); + fseek(logctl.l_mdfp, (long)old_meta_offset, SEEK_SET); + + __pmLogPutIndex(&logctl, &restime); + + fseek(logctl.l_mfp, (long)new_log_offset, SEEK_SET); + fseek(logctl.l_mdfp, (long)new_meta_offset, SEEK_SET); + + old_log_offset = ftell(logctl.l_mfp); + assert(old_log_offset >= 0); + old_meta_offset = ftell(logctl.l_mdfp); + assert(old_meta_offset >= 0); + + flushsize = ftell(logctl.l_mfp) + 100000; + } + + /* free PDU buffer */ + __pmUnpinPDUBuf(pb); + pb = NULL; + + elm->res = NULL; + elm->next = NULL; + free(elm); + + } /*while(*rlready)*/ +} + + +/* + * mark record has been created and assigned to iap->pb[LOG] + * write it out + */ +void +writemark(inarch_t *iap) +{ + int sts; + mark_t *p = (mark_t *)iap->pb[LOG]; + + if (!iap->mark) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr, " writemark called, but mark not set\n"); + abandon(); + } + + if (p == NULL) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr, " writemark called, but no pdu\n"); + abandon(); + } + + p->timestamp.tv_sec = htonl(p->timestamp.tv_sec); + p->timestamp.tv_usec = htonl(p->timestamp.tv_usec); + + if ((sts = __pmLogPutResult2(&logctl, iap->pb[LOG])) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutResult2: log data: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + } + written++; + free(iap->pb[LOG]); + iap->pb[LOG] = NULL; +} + +/*--- END FUNCTIONS ---------------------------------------------------------*/ + +int +main(int argc, char **argv) +{ + int i; + int j; + int sts; + int stslog; /* sts from nextlog() */ + int stsmeta; /* sts from nextmeta() */ + + char *msg; + + __pmTimeval now = {0,0}; /* the current time */ + __pmTimeval mintime = {0,0}; + __pmTimeval tmptime = {0,0}; + + __pmTimeval tstamp; /* temporary timestamp */ + inarch_t *iap; /* ptr to archive control */ + rlist_t *rlready; /* list of results ready for writing */ + struct timeval unused; + + + rlog = NULL; /* list of log records to write */ + rdesc = NULL; /* list of meta desc records to write */ + rindom = NULL; /* list of meta indom records to write */ + rlready = NULL; + + + /* process cmd line args */ + if (parseargs(argc, argv) < 0) { + pmUsageMessage(&opts); + exit(1); + } + + + /* input archive names are argv[opts.optind] ... argv[argc-2]) */ + /* output archive name is argv[argc-1]) */ + + /* output archive */ + outarchname = argv[argc-1]; + + /* input archive(s) */ + inarchnum = argc - 1 - opts.optind; + inarch = (inarch_t *) malloc(inarchnum * sizeof(inarch_t)); + if (inarch == NULL) { + fprintf(stderr, "%s: Error: mallco inarch: %s\n", + pmProgname, osstrerror()); + exit(1); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + totalmalloc += (inarchnum * sizeof(inarch_t)); + fprintf(stderr, "main : allocated %d\n", + (int)(inarchnum * sizeof(inarch_t))); + } +#endif + + + for (i=0; i<inarchnum; i++, opts.optind++) { + iap = &inarch[i]; + + iap->name = argv[opts.optind]; + + iap->pb[LOG] = iap->pb[META] = NULL; + iap->eof[LOG] = iap->eof[META] = 0; + iap->mark = 0; + iap->_result = NULL; + iap->_Nresult = NULL; + + if ((iap->ctx = pmNewContext(PM_CONTEXT_ARCHIVE, iap->name)) < 0) { + fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n", + pmProgname, iap->name, pmErrStr(iap->ctx)); + exit(1); + } + + if ((sts = pmUseContext(iap->ctx)) < 0) { + fprintf(stderr, "%s: Error: cannot use context (%s): %s\n", + pmProgname, iap->name, pmErrStr(sts)); + exit(1); + } + + if ((sts = pmGetArchiveLabel(&iap->label)) < 0) { + fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n", pmProgname, iap->name, pmErrStr(sts)); + exit(1); + } + + if ((sts = pmGetArchiveEnd(&unused)) < 0) { + fprintf(stderr, "%s: Error: cannot get end of archive (%s): %s\n", + pmProgname, iap->name, pmErrStr(sts)); + exit(1); + } + + if (i == 0) { + /* start time */ + logstart_tval.tv_sec = iap->label.ll_start.tv_sec; + logstart_tval.tv_usec = iap->label.ll_start.tv_usec; + + /* end time */ + logend_tval.tv_sec = unused.tv_sec; + logend_tval.tv_usec = unused.tv_usec; + } + else { + /* get the earlier start time */ + if (logstart_tval.tv_sec > iap->label.ll_start.tv_sec || + (logstart_tval.tv_sec == iap->label.ll_start.tv_sec && + logstart_tval.tv_usec > iap->label.ll_start.tv_usec)) { + logstart_tval.tv_sec = iap->label.ll_start.tv_sec; + logstart_tval.tv_usec = iap->label.ll_start.tv_usec; + } + + /* get the later end time */ + if (logend_tval.tv_sec < unused.tv_sec || + (logend_tval.tv_sec == unused.tv_sec && + logend_tval.tv_usec < unused.tv_usec)) { + logend_tval.tv_sec = unused.tv_sec; + logend_tval.tv_usec = unused.tv_usec; + } + } + } /*for(i)*/ + + logctl.l_label.ill_start.tv_sec = logstart_tval.tv_sec; + logctl.l_label.ill_start.tv_usec = logstart_tval.tv_usec; + + /* process config file + * - this includes a list of metrics and their instances + */ + if (configfile && parseconfig() < 0) + exit(1); + + if (zarg) { + /* use TZ from metrics source (input-archive) */ + if ((sts = pmNewZone(inarch[0].label.ll_tz)) < 0) { + fprintf(stderr, "%s: Cannot set context timezone: %s\n", + pmProgname, pmErrStr(sts)); + exit_status = 1; + goto cleanup; + } + printf("Note: timezone set to local timezone of host \"%s\" from archive\n\n", inarch[0].label.ll_hostname); + } + else if (tz != NULL) { + /* use TZ as specified by user */ + if ((sts = pmNewZone(tz)) < 0) { + fprintf(stderr, "%s: Cannot set timezone to \"%s\": %s\n", + pmProgname, tz, pmErrStr(sts)); + exit_status = 1; + goto cleanup; + } + printf("Note: timezone set to \"TZ=%s\"\n\n", tz); + } + else { + char *tz; + tz = __pmTimezone(); + /* use TZ from local host */ + if ((sts = pmNewZone(tz)) < 0) { + fprintf(stderr, "%s: Cannot set local host's timezone: %s\n", + pmProgname, pmErrStr(sts)); + exit_status = 1; + goto cleanup; + } + } + + + /* create output log - must be done before writing label */ + if ((sts = __pmLogCreate("", outarchname, outarchvers, &logctl)) < 0) { + fprintf(stderr, "%s: Error: __pmLogCreate: %s\n", + pmProgname, pmErrStr(sts)); + exit(1); + } + + /* This must be done after log is created: + * - checks that archive version, host, and timezone are ok + * - set archive version, host, and timezone of output archive + */ + newlabel(); + + /* write label record */ + writelabel_metati(0); + + + /* set winstart and winend timevals */ + sts = pmParseTimeWindow(Sarg, Targ, Aarg, Oarg, + &logstart_tval, &logend_tval, + &winstart_tval, &winend_tval, &unused, &msg); + if (sts < 0) { + fprintf(stderr, "%s: Invalid time window specified: %s\n", + pmProgname, msg); + abandon(); + } + winstart.tv_sec = winstart_tval.tv_sec; + winstart.tv_usec = winstart_tval.tv_usec; + winend.tv_sec = winend_tval.tv_sec; + winend.tv_usec = winend_tval.tv_usec; + logend.tv_sec = logend_tval.tv_sec; + logend.tv_usec = logend_tval.tv_usec; + + if (warg) { + if (winstart.tv_sec + NUM_SEC_PER_DAY < winend.tv_sec) { + fprintf(stderr, "%s: Warning: -S and -T must specify a time window within\nthe same day, for -w to be used. Ignoring -w flag.\n", pmProgname); + warg = 0; + } + } + + ilog = -1; + written = 0; + curlog.tv_sec = 0; + curlog.tv_usec = 0; + current.tv_sec = 0; + current.tv_usec = 0; + first_datarec = 1; + pre_startwin = 1; + + /* get all meta data first + * nextmeta() should return 0 (will return -1 when all meta is eof) + */ + do { + stsmeta = nextmeta(); + } while (stsmeta >= 0); + + + /* get log record - choose one with earliest timestamp + * write out meta data (required by this log record) + * write out log + * do ti update if necessary + */ + while (sarg == -1 || written < sarg) { + ilog = -1; + curlog.tv_sec = 0; + curlog.tv_usec = 0; + old_meta_offset = ftell(logctl.l_mdfp); + assert(old_meta_offset >= 0); + + /* nextlog() resets ilog, and curlog (to the smallest timestamp) + */ + stslog = nextlog(); + + if (stslog < 0) + break; + + /* find the _Nresult (or mark pdu) with the earliest timestamp; + * set ilog + * (this is a bit more complex when tflag is specified) + */ + mintime.tv_sec = mintime.tv_usec = 0; + for (i=0; i<inarchnum; i++) { + if (inarch[i]._Nresult != NULL) { + tstamp.tv_sec = inarch[i]._Nresult->timestamp.tv_sec; + tstamp.tv_usec = inarch[i]._Nresult->timestamp.tv_usec; + checklogtime(&tstamp, i); + + if (ilog == i) { + tmptime = curlog; + if (mintime.tv_sec <= 0 || tvcmp(mintime, tmptime) > 0) + mintime = tmptime; + } + } + else if (inarch[i].pb[LOG] != NULL) { + tstamp.tv_sec = inarch[i].pb[LOG][3]; /* no swab needed */ + tstamp.tv_usec = inarch[i].pb[LOG][4]; /* no swab needed */ + checklogtime(&tstamp, i); + + if (ilog == i) { + tmptime = curlog; + if (mintime.tv_sec <= 0 || tvcmp(mintime, tmptime) > 0) + mintime = tmptime; + } + } + } + + /* now == the earliest timestamp of the archive(s) + * and/or mark records + * mintime == now or timestamp of the earliest mark + * (whichever is smaller) + */ + now = curlog; + + /* note - mark (after last archive) will be created, but this + * break, will prevent it from being written out + */ + if (tvcmp(now, logend) > 0) + break; + + sts = checkwinend(now); + if (sts < 0) + break; + if (sts > 0) + continue; + + current = curlog; + + /* prepare to write out log record + */ + if (ilog < 0 || ilog >= inarchnum) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr, " log file index = %d\n", ilog); + abandon(); + } + + + iap = &inarch[ilog]; + if (iap->mark) + writemark(iap); + else { + /* result is to be written out, but there is no _Nresult + */ + if (iap->_Nresult == NULL) { + fprintf(stderr, "%s: Fatal Error!\n", pmProgname); + fprintf(stderr, " pick == LOG and _Nresult = NULL\n"); + abandon(); + } + insertresult(&rlready, iap->_Nresult); +#if 0 +{ + rlist_t *rp; + int i; + + fprintf(stderr, "rlready"); + for (i = 0, rp = rlready; rp != NULL; i++, rp = rp->next) { + fprintf(stderr, " [%d] t=%d.%06d numpmid=%d", i, (int)rp->res->timestamp.tv_sec, (int)rp->res->timestamp.tv_usec, rp->res->numpmid); + } + fprintf(stderr, " now=%d.%06d\n", now.tv_sec, now.tv_usec); +} +#endif + + writerlist(&rlready, curlog); + + /* writerlist frees elm (elements of rlready) but does not + * free _result & _Nresult + */ + + /* free _result & _Nresult + * _Nresult may contain space that was allocated + * in __pmStuffValue this space has PM_VAL_SPTR format, + * and has to be freed first + * (in order to avoid memory leaks) + */ + if (iap->_result != iap->_Nresult && iap->_Nresult != NULL) { + pmValueSet *vsetp; + for (i=0; i<iap->_Nresult->numpmid; i++) { + vsetp = iap->_Nresult->vset[i]; + if (vsetp->valfmt == PM_VAL_SPTR) { + for (j=0; j<vsetp->numval; j++) { + free(vsetp->vlist[j].value.pval); + } + } + } + free(iap->_Nresult); + } + if (iap->_result != NULL) { + pmFreeResult(iap->_result); + iap->_result = NULL; + } + iap->_Nresult = NULL; + } + } /*while()*/ + + if (first_datarec) { + fprintf(stderr, "%s: Warning: no qualifying records found.\n", + pmProgname); +cleanup: + abandon(); + } + else { + /* write the last time stamp */ + fflush(logctl.l_mfp); + fflush(logctl.l_mdfp); + + if (old_log_offset == 0) + old_log_offset = sizeof(__pmLogLabel)+2*sizeof(int); + + new_log_offset = ftell(logctl.l_mfp); + assert(new_log_offset >= 0); + new_meta_offset = ftell(logctl.l_mdfp); + assert(new_meta_offset >= 0); + +#if 0 + fprintf(stderr, "*** last tstamp: \n\tmintime=%d.%06d \n\ttmptime=%d.%06d \n\tlogend=%d.%06d \n\twinend=%d.%06d \n\tcurrent=%d.%06d\n", + mintime.tv_sec, mintime.tv_usec, tmptime.tv_sec, tmptime.tv_usec, logend.tv_sec, logend.tv_usec, winend.tv_sec, winend.tv_usec, current.tv_sec, current.tv_usec); +#endif + + fseek(logctl.l_mfp, old_log_offset, SEEK_SET); + __pmLogPutIndex(&logctl, ¤t); + + + /* need to fix up label with new start-time */ + writelabel_metati(1); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "main : total allocated %ld\n", totalmalloc); + } +#endif + + exit(exit_status); +} |