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/pmlogrewrite | |
download | pcp-47e6e7c84f008a53061e661f31ae96629bc694ef.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmlogrewrite')
-rw-r--r-- | src/pmlogrewrite/GNUlocaldefs.coverage | 2 | ||||
-rw-r--r-- | src/pmlogrewrite/GNUmakefile | 48 | ||||
-rw-r--r-- | src/pmlogrewrite/gram.y | 986 | ||||
-rw-r--r-- | src/pmlogrewrite/indom.c | 381 | ||||
-rw-r--r-- | src/pmlogrewrite/lex.l | 223 | ||||
-rw-r--r-- | src/pmlogrewrite/logger.h | 178 | ||||
-rw-r--r-- | src/pmlogrewrite/logio.c | 175 | ||||
-rw-r--r-- | src/pmlogrewrite/metric.c | 230 | ||||
-rw-r--r-- | src/pmlogrewrite/pmlogrewrite.c | 1318 | ||||
-rw-r--r-- | src/pmlogrewrite/result.c | 730 | ||||
-rw-r--r-- | src/pmlogrewrite/util.c | 302 |
11 files changed, 4573 insertions, 0 deletions
diff --git a/src/pmlogrewrite/GNUlocaldefs.coverage b/src/pmlogrewrite/GNUlocaldefs.coverage new file mode 100644 index 0000000..00f5eb4 --- /dev/null +++ b/src/pmlogrewrite/GNUlocaldefs.coverage @@ -0,0 +1,2 @@ +CFLAGS += -fprofile-arcs -ftest-coverage +LDIRT += *.gcda *.gcno *.gcov diff --git a/src/pmlogrewrite/GNUmakefile b/src/pmlogrewrite/GNUmakefile new file mode 100644 index 0000000..9a0c79a --- /dev/null +++ b/src/pmlogrewrite/GNUmakefile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2011 Ken McDonell. All Rights Reserved. +# Copyright (c) 2012 Red Hat, 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 +-include ./GNUlocaldefs + +CFILES = pmlogrewrite.c util.c logio.c metric.c indom.c result.c +HFILES = logger.h +LFILES = lex.l +YFILES = gram.y + +CMDTARGET = pmlogrewrite$(EXECSUFFIX) +LLDLIBS = $(PCPLIB) $(LIB_FOR_MATH) +LDIRT += $(YFILES:%.y=%.tab.?) + +default: $(CMDTARGET) + +include $(BUILDRULES) + +pmlogextract: $(OBJECTS) + +install: $(CMDTARGET) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogrewrite + $(INSTALL) -m 755 $(CMDTARGET) $(PCP_BINADM_DIR)/$(CMDTARGET) + +.NOTPARALLEL: +gram.tab.h gram.tab.c: gram.y + +lex.o gram.tab.o: gram.tab.h logger.h +indom.o metric.o result.o util.o pmlogrewrite.o: logger.h + +default_pcp: default + +install_pcp: install diff --git a/src/pmlogrewrite/gram.y b/src/pmlogrewrite/gram.y new file mode 100644 index 0000000..bdbb795 --- /dev/null +++ b/src/pmlogrewrite/gram.y @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2011 Ken McDonell. All Rights Reserved. + * 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. + */ + +%{ +/* + * pmlogrewrite parser + */ +#include "pmapi.h" +#include "impl.h" +#include "logger.h" +#include <errno.h> +#include <assert.h> + +static indomspec_t *current_indomspec; +static int current_star_indom; +static int do_walk_indom; +static int star_domain; + +static metricspec_t *current_metricspec; +static int current_star_metric; +static int star_cluster; +static int do_walk_metric; +static int output = OUTPUT_ALL; +static int one_inst; +static char *one_name; + +indomspec_t * +walk_indom(int mode) +{ + static indomspec_t *ip; + + if (do_walk_indom) { + if (mode == W_START) + ip = indom_root; + else + ip = ip->i_next; + while (ip != NULL && pmInDom_domain(ip->old_indom) != star_domain) + ip = ip->i_next; + } + else { + if (mode == W_START) + ip = current_indomspec; + else + ip = NULL; + } + + return ip; +} + +metricspec_t * +walk_metric(int mode, int flag, char *which) +{ + static metricspec_t *mp; + + if (do_walk_metric) { + if (mode == W_START) + mp = metric_root; + else + mp = mp->m_next; + while (mp != NULL) { + if (pmid_domain(mp->old_desc.pmid) == star_domain && + (star_cluster == PM_ID_NULL || star_cluster == pmid_cluster(mp->old_desc.pmid))) + break; + mp = mp->m_next; + } + } + else { + if (mode == W_START) + mp = current_metricspec; + else + mp = NULL; + } + + if (mp != NULL) { + if (mp->flags & flag) { + snprintf(mess, sizeof(mess), "Duplicate %s clause for metric %s", which, mp->old_name); + yyerror(mess); + } + if (flag != METRIC_DELETE) { + if (mp->flags & METRIC_DELETE) { + snprintf(mess, sizeof(mess), "Conflicting %s clause for deleted metric %s", which, mp->old_name); + yyerror(mess); + } + } + else { + if (mp->flags & (~METRIC_DELETE)) { + snprintf(mess, sizeof(mess), "Conflicting delete and other clauses for metric %s", mp->old_name); + yyerror(mess); + } + } + } + + return mp; +} + +%} + +%union { + char *str; + int ival; + double dval; + pmInDom indom; + pmID pmid; +} + +%token TOK_LBRACE + TOK_RBRACE + TOK_PLUS + TOK_MINUS + TOK_COLON + TOK_COMMA + TOK_ASSIGN + TOK_GLOBAL + TOK_INDOM + TOK_DUPLICATE + TOK_METRIC + TOK_HOSTNAME + TOK_TZ + TOK_TIME + TOK_NAME + TOK_INST + TOK_INAME + TOK_DELETE + TOK_PMID + TOK_NULL_INT + TOK_TYPE + TOK_SEM + TOK_UNITS + TOK_OUTPUT + TOK_RESCALE + +%token<str> TOK_GNAME TOK_NUMBER TOK_STRING TOK_HNAME TOK_FLOAT +%token<str> TOK_INDOM_STAR TOK_PMID_INT TOK_PMID_STAR +%token<ival> TOK_TYPE_NAME TOK_SEM_NAME TOK_SPACE_NAME TOK_TIME_NAME +%token<ival> TOK_COUNT_NAME TOK_OUTPUT_TYPE + +%type<str> hname +%type<indom> indom_int null_or_indom +%type<pmid> pmid_int pmid_or_name +%type<ival> signnumber number rescaleopt duplicateopt +%type<dval> float + +%% + +config : speclist + ; + +speclist : spec + | spec speclist + ; + +spec : globalspec + | indomspec + | metricspec + ; + +globalspec : TOK_GLOBAL TOK_LBRACE globaloptlist TOK_RBRACE + | TOK_GLOBAL TOK_LBRACE TOK_RBRACE + ; + +globaloptlist : globalopt + | globalopt globaloptlist + ; + +globalopt : TOK_HOSTNAME TOK_ASSIGN hname + { + if (global.flags & GLOBAL_CHANGE_HOSTNAME) { + snprintf(mess, sizeof(mess), "Duplicate global hostname clause"); + yyerror(mess); + } + if (strcmp(inarch.label.ll_hostname, $3) == 0) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Global hostname (%s): No change", inarch.label.ll_hostname); + yywarn(mess); + } + } + else { + strncpy(global.hostname, $3, sizeof(global.hostname)); + global.flags |= GLOBAL_CHANGE_HOSTNAME; + } + free($3); + } + | TOK_TZ TOK_ASSIGN TOK_STRING + { + if (global.flags & GLOBAL_CHANGE_TZ) { + snprintf(mess, sizeof(mess), "Duplicate global tz clause"); + yyerror(mess); + } + if (strcmp(inarch.label.ll_tz, $3) == 0) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Global timezone (%s): No change", inarch.label.ll_tz); + yywarn(mess); + } + } + else { + strncpy(global.tz, $3, sizeof(global.tz)); + global.flags |= GLOBAL_CHANGE_TZ; + } + free($3); + } + | TOK_TIME TOK_ASSIGN signtime + { + if (global.flags & GLOBAL_CHANGE_TIME) { + snprintf(mess, sizeof(mess), "Duplicate global time clause"); + yyerror(mess); + } + if (global.time.tv_sec == 0 && global.time.tv_usec == 0) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Global time: No change"); + yywarn(mess); + } + } + else + global.flags |= GLOBAL_CHANGE_TIME; + } + | TOK_HOSTNAME TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting hostname in hostname clause"); + yyerror(mess); + } + | TOK_HOSTNAME + { + snprintf(mess, sizeof(mess), "Expecting -> in hostname clause"); + yyerror(mess); + } + | TOK_TZ TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting timezone string in tz clause"); + yyerror(mess); + } + | TOK_TZ + { + snprintf(mess, sizeof(mess), "Expecting -> in tz clause"); + yyerror(mess); + } + | TOK_TIME TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting delta of the form [+-][HH:[MM:]]SS[.d...] in time clause"); + yyerror(mess); + } + | TOK_TIME + { + snprintf(mess, sizeof(mess), "Expecting -> in time clause"); + yyerror(mess); + } + ; + + /* + * ambiguity in lexical scanner ... handle here + * abc.def - is TOK_HNAME or TOK_GNAME + * 123 - is TOK_HNAME or TOK_NUMBER + * 123.456 - is TOK_HNAME or TOK_FLOAT + */ +hname : TOK_HNAME + | TOK_GNAME + | TOK_NUMBER + | TOK_FLOAT + ; + +signnumber : TOK_PLUS TOK_NUMBER + { + $$ = atoi($2); + free($2); + } + | TOK_MINUS TOK_NUMBER + { + $$ = -atoi($2); + free($2); + } + | TOK_NUMBER + { + $$ = atoi($1); + free($1); + } + ; + +number : TOK_NUMBER + { + $$ = atoi($1); + free($1); + } + ; + +float : TOK_FLOAT + { + $$ = atof($1); + free($1); + } + ; + +signtime : TOK_PLUS time + | TOK_MINUS time { global.time.tv_sec = -global.time.tv_sec; } + | time + ; + +time : number TOK_COLON number TOK_COLON float /* HH:MM:SS.d.. format */ + { + if ($3 > 59) { + snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $3); + yywarn(mess); + } + if ($5 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $5); + yywarn(mess); + } + global.time.tv_sec = $1 * 3600 + $3 * 60 + (int)$5; + global.time.tv_usec = (int)(1000000*(($5 - (int)$5))+0.5); + } + | number TOK_COLON number TOK_COLON number /* HH:MM:SS format */ + { + if ($3 > 59) { + snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $3); + yywarn(mess); + } + if ($5 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $5); + yywarn(mess); + } + global.time.tv_sec = $1 * 3600 + $3 * 60 + $5; + } + | number TOK_COLON float /* MM:SS.d.. format */ + { + if ($1 > 59) { + snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $1); + yywarn(mess); + } + if ($3 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $3); + yywarn(mess); + } + global.time.tv_sec = $1 * 60 + (int)$3; + global.time.tv_usec = (int)(1000000*(($3 - (int)$3))+0.5); + } + | number TOK_COLON number /* MM:SS format */ + { + if ($1 > 59) { + snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $1); + yywarn(mess); + } + if ($3 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $3); + yywarn(mess); + } + global.time.tv_sec = $1 * 60 + $3; + } + | float /* SS.d.. format */ + { + if ($1 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $1); + yywarn(mess); + } + global.time.tv_sec = (int)$1; + global.time.tv_usec = (int)(1000000*(($1 - (int)$1))+0.5); + } + | number /* SS format */ + { + if ($1 > 59) { + snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $1); + yywarn(mess); + } + global.time.tv_sec = $1; + global.time.tv_usec = 0; + } + ; + +indomspec : TOK_INDOM indom_int + { + if (current_star_indom) { + __pmContext *ctxp; + __pmHashCtl *hcp; + __pmHashNode *node; + + ctxp = __pmHandleToPtr(pmWhichContext()); + assert(ctxp != NULL); + hcp = &ctxp->c_archctl->ac_log->l_hashindom; + star_domain = pmInDom_domain($2); + for (node = __pmHashWalk(hcp, PM_HASH_WALK_START); + node != NULL; + node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) { + if (pmInDom_domain((pmInDom)(node->key)) == star_domain) + current_indomspec = start_indom((pmInDom)(node->key)); + } + do_walk_indom = 1; + } + else { + current_indomspec = start_indom($2); + do_walk_indom = 0; + } + } + TOK_LBRACE optindomopt TOK_RBRACE + | TOK_INDOM + { + snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or <domain>.* in indom rule"); + yyerror(mess); + } + ; + +indom_int : TOK_FLOAT + { + int domain; + int serial; + int sts; + sts = sscanf($1, "%d.%d", &domain, &serial); + if (sts < 2) { + snprintf(mess, sizeof(mess), "Missing serial field for indom"); + yyerror(mess); + } + if (domain < 1 || domain >= DYNAMIC_PMID) { + snprintf(mess, sizeof(mess), "Illegal domain field (%d) for indom", domain); + yyerror(mess); + } + if (serial < 0 || serial >= 4194304) { + snprintf(mess, sizeof(mess), "Illegal serial field (%d) for indom", serial); + yyerror(mess); + } + current_star_indom = 0; + free($1); + $$ = pmInDom_build(domain, serial); + } + | TOK_INDOM_STAR + { + int domain; + sscanf($1, "%d.", &domain); + if (domain < 1 || domain >= DYNAMIC_PMID) { + snprintf(mess, sizeof(mess), "Illegal domain field (%d) for indom", domain); + yyerror(mess); + } + current_star_indom = 1; + free($1); + $$ = pmInDom_build(domain, 0); + } + ; + +optindomopt : indomoptlist + | + ; + +indomoptlist : indomopt + | indomopt indomoptlist + ; + +indomopt : TOK_INDOM TOK_ASSIGN duplicateopt indom_int + { + indomspec_t *ip; + for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) { + pmInDom indom; + if (indom_root->new_indom != indom_root->old_indom) { + snprintf(mess, sizeof(mess), "Duplicate indom clause for indom %s", pmInDomStr(indom_root->old_indom)); + yyerror(mess); + } + if (current_star_indom) + indom = pmInDom_build(pmInDom_domain($4), pmInDom_serial(ip->old_indom)); + else + indom = $4; + if (indom != ip->old_indom) + ip->new_indom = indom; + else { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Instance domain %s: indom: No change", pmInDomStr(ip->old_indom)); + yywarn(mess); + } + } + ip->indom_flags |= $3; + } + } + | TOK_INAME TOK_STRING TOK_ASSIGN TOK_STRING + { + indomspec_t *ip; + for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) { + if (change_inst_by_name(ip->old_indom, $2, $4) < 0) + yyerror(mess); + } + free($2); + /* Note: $4 referenced from new_iname[] */ + } + | TOK_INAME TOK_STRING TOK_ASSIGN TOK_DELETE + { + indomspec_t *ip; + for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) { + if (change_inst_by_name(ip->old_indom, $2, NULL) < 0) + yyerror(mess); + } + free($2); + } + | TOK_INST number TOK_ASSIGN number + { + indomspec_t *ip; + for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) { + if (change_inst_by_inst(ip->old_indom, $2, $4) < 0) + yyerror(mess); + } + } + | TOK_INST number TOK_ASSIGN TOK_DELETE + { + indomspec_t *ip; + for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) { + if (change_inst_by_inst(ip->old_indom, $2, PM_IN_NULL) < 0) + yyerror(mess); + } + } + | TOK_INDOM TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or <domain>.* in indom clause"); + yyerror(mess); + } + | TOK_INDOM + { + snprintf(mess, sizeof(mess), "Expecting -> in indom clause"); + yyerror(mess); + } + | TOK_INAME TOK_STRING TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting new external instance name string or DELETE in iname clause"); + yyerror(mess); + } + | TOK_INAME TOK_STRING + { + snprintf(mess, sizeof(mess), "Expecting -> in iname clause"); + yyerror(mess); + } + | TOK_INAME + { + snprintf(mess, sizeof(mess), "Expecting old external instance name string in iname clause"); + yyerror(mess); + } + | TOK_INST number TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting new internal instance identifier or DELETE in inst clause"); + yyerror(mess); + } + | TOK_INST number + { + snprintf(mess, sizeof(mess), "Expecting -> in inst clause"); + yyerror(mess); + } + | TOK_INST + { + snprintf(mess, sizeof(mess), "Expecting old internal instance identifier in inst clause"); + yyerror(mess); + } + ; + +duplicateopt : TOK_DUPLICATE { $$ = INDOM_DUPLICATE; } + | { $$ = 0; } + ; + +metricspec : TOK_METRIC pmid_or_name + { + if (current_star_metric) { + __pmContext *ctxp; + __pmHashCtl *hcp; + __pmHashNode *node; + + ctxp = __pmHandleToPtr(pmWhichContext()); + assert(ctxp != NULL); + hcp = &ctxp->c_archctl->ac_log->l_hashpmid; + star_domain = pmid_domain($2); + if (current_star_metric == 1) + star_cluster = pmid_cluster($2); + else + star_cluster = PM_ID_NULL; + for (node = __pmHashWalk(hcp, PM_HASH_WALK_START); + node != NULL; + node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) { + if (pmid_domain((pmID)(node->key)) == star_domain && + (star_cluster == PM_ID_NULL || + star_cluster == pmid_cluster((pmID)(node->key)))) + current_metricspec = start_metric((pmID)(node->key)); + } + do_walk_metric = 1; + } + else { + if ($2 == PM_ID_NULL) + /* metric not in archive */ + current_metricspec = NULL; + else + current_metricspec = start_metric($2); + do_walk_metric = 0; + } + } + TOK_LBRACE optmetricoptlist TOK_RBRACE + | TOK_METRIC + { + snprintf(mess, sizeof(mess), "Expecting metric name or <domain>.<cluster>.<item> or <domain>.<cluster>.* or <domain>.*.* in metric rule"); + yyerror(mess); + } + ; + +pmid_or_name : pmid_int + | TOK_GNAME + { + int sts; + pmID pmid; + sts = pmLookupName(1, &$1, &pmid); + if (sts < 0) { + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s: %s", $1, pmErrStr(sts)); + yywarn(mess); + } + pmid = PM_ID_NULL; + } + current_star_metric = 0; + free($1); + $$ = pmid; + } + ; + +pmid_int : TOK_PMID_INT + { + int domain; + int cluster; + int item; + int sts; + sts = sscanf($1, "%d.%d.%d", &domain, &cluster, &item); + assert(sts == 3); + if (domain < 1 || domain >= DYNAMIC_PMID) { + snprintf(mess, sizeof(mess), "Illegal domain field (%d) for pmid", domain); + yyerror(mess); + } + if (cluster < 0 || cluster >= 4096) { + snprintf(mess, sizeof(mess), "Illegal cluster field (%d) for pmid", cluster); + yyerror(mess); + } + if (item < 0 || item >= 1024) { + snprintf(mess, sizeof(mess), "Illegal item field (%d) for pmid", item); + yyerror(mess); + } + current_star_metric = 0; + free($1); + $$ = pmid_build(domain, cluster, item); + } + | TOK_PMID_STAR + { + int domain; + int cluster; + int sts; + sts = sscanf($1, "%d.%d.", &domain, &cluster); + if (domain < 1 || domain >= DYNAMIC_PMID) { + snprintf(mess, sizeof(mess), "Illegal domain field (%d) for pmid", domain); + yyerror(mess); + } + if (sts == 2) { + if (cluster >= 4096) { + snprintf(mess, sizeof(mess), "Illegal cluster field (%d) for pmid", cluster); + yyerror(mess); + } + current_star_metric = 1; + } + else { + cluster = 0; + current_star_metric = 2; + } + free($1); + $$ = pmid_build(domain, cluster, 0); + } + ; + +optmetricoptlist : metricoptlist + | /* nothing */ + ; + +metricoptlist : metricopt + | metricopt metricoptlist + ; + +metricopt : TOK_PMID TOK_ASSIGN pmid_int + { + metricspec_t *mp; + pmID pmid; + for (mp = walk_metric(W_START, METRIC_CHANGE_PMID, "pmid"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_PMID, "pmid")) { + if (current_star_metric == 1) + pmid = pmid_build(pmid_domain($3), pmid_cluster($3), pmid_item(mp->old_desc.pmid)); + else if (current_star_metric == 2) + pmid = pmid_build(pmid_domain($3), pmid_cluster(mp->old_desc.pmid), pmid_item(mp->old_desc.pmid)); + else + pmid = $3; + if (pmid == mp->old_desc.pmid) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): pmid: No change", mp->old_name, pmIDStr(mp->old_desc.pmid)); + yywarn(mess); + } + } + else { + mp->new_desc.pmid = pmid; + mp->flags |= METRIC_CHANGE_PMID; + } + } + } + | TOK_NAME TOK_ASSIGN TOK_GNAME + { + metricspec_t *mp; + for (mp = walk_metric(W_START, METRIC_CHANGE_NAME, "name"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_NAME, "name")) { + if (strcmp($3, mp->old_name) == 0) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): name: No change", mp->old_name, pmIDStr(mp->old_desc.pmid)); + yywarn(mess); + } + } + else { + int sts; + pmID pmid; + sts = pmLookupName(1, &$3, &pmid); + if (sts >= 0) { + snprintf(mess, sizeof(mess), "Metric name %s already assigned for PMID %s", $3, pmIDStr(pmid)); + yyerror(mess); + } + mp->new_name = $3; + mp->flags |= METRIC_CHANGE_NAME; + } + } + } + | TOK_TYPE TOK_ASSIGN TOK_TYPE_NAME + { + metricspec_t *mp; + for (mp = walk_metric(W_START, METRIC_CHANGE_TYPE, "type"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_TYPE, "type")) { + if ($3 == mp->old_desc.type) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): type: PM_TYPE_%s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmTypeStr(mp->old_desc.type)); + yywarn(mess); + } + } + else { + if (mp->old_desc.type == PM_TYPE_32 || + mp->old_desc.type == PM_TYPE_U32 || + mp->old_desc.type == PM_TYPE_64 || + mp->old_desc.type == PM_TYPE_U64 || + mp->old_desc.type == PM_TYPE_FLOAT || + mp->old_desc.type == PM_TYPE_DOUBLE) { + mp->new_desc.type = $3; + mp->flags |= METRIC_CHANGE_TYPE; + } + else { + snprintf(mess, sizeof(mess), "Old type (PM_TYPE_%s) must be numeric", pmTypeStr(mp->old_desc.type)); + yyerror(mess); + } + } + } + } + | TOK_INDOM TOK_ASSIGN null_or_indom pick + { + metricspec_t *mp; + pmInDom indom; + for (mp = walk_metric(W_START, METRIC_CHANGE_INDOM, "indom"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_INDOM, "indom")) { + if (current_star_indom) + indom = pmInDom_build(pmInDom_domain($3), pmInDom_serial(mp->old_desc.indom)); + else + indom = $3; + if (indom == mp->old_desc.indom) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): indom: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmInDomStr(mp->old_desc.indom)); + yywarn(mess); + } + } + else { + if ((output == OUTPUT_MIN || + output == OUTPUT_MAX || + output == OUTPUT_SUM || + output == OUTPUT_AVG) && + mp->old_desc.type != PM_TYPE_32 && + mp->old_desc.type != PM_TYPE_U32 && + mp->old_desc.type != PM_TYPE_64 && + mp->old_desc.type != PM_TYPE_U64 && + mp->old_desc.type != PM_TYPE_FLOAT && + mp->old_desc.type != PM_TYPE_DOUBLE) { + snprintf(mess, sizeof(mess), "OUTPUT option MIN, MAX, AVG or SUM requires type to be numeric, not PM_TYPE_%s", pmTypeStr(mp->old_desc.type)); + yyerror(mess); + } + mp->new_desc.indom = indom; + mp->flags |= METRIC_CHANGE_INDOM; + mp->output = output; + if (output == OUTPUT_ONE) { + mp->one_name = one_name; + mp->one_inst = one_inst; + if (mp->old_desc.indom == PM_INDOM_NULL) + /* + * singular input, pick first (only) + * value, not one_inst matching ... + * one_inst used for output instance + * id + */ + mp->output = OUTPUT_FIRST; + } + if (output == OUTPUT_ALL) { + /* + * No OUTPUT clause, set up the defaults + * based on indom types: + * non-NULL -> NULL + * OUTPUT_FIRST, inst PM_IN_NULL + * NULL -> non-NULL + * OUTPUT_FIRST, inst 0 + * non-NULL -> non-NULL + * all instances selected + * (nothing to do for defaults) + * NULL -> NULL + * caught above in no change case + */ + if (mp->old_desc.indom != PM_INDOM_NULL && + mp->new_desc.indom == PM_INDOM_NULL) { + mp->output = OUTPUT_FIRST; + mp->one_inst = PM_IN_NULL; + } + else if (mp->old_desc.indom == PM_INDOM_NULL && + mp->new_desc.indom != PM_INDOM_NULL) { + mp->output = OUTPUT_FIRST; + mp->one_inst = 0; + } + } + } + } + output = OUTPUT_ALL; /* for next time */ + } + | TOK_SEM TOK_ASSIGN TOK_SEM_NAME + { + metricspec_t *mp; + for (mp = walk_metric(W_START, METRIC_CHANGE_SEM, "sem"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_SEM, "sem")) { + if ($3 == mp->old_desc.sem) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): sem: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), SemStr(mp->old_desc.sem)); + yywarn(mess); + } + } + else { + mp->new_desc.sem = $3; + mp->flags |= METRIC_CHANGE_SEM; + } + } + } + | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA TOK_TIME_NAME TOK_COMMA TOK_COUNT_NAME rescaleopt + { + metricspec_t *mp; + for (mp = walk_metric(W_START, METRIC_CHANGE_UNITS, "units"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_UNITS, "units")) { + if ($3 == mp->old_desc.units.dimSpace && + $5 == mp->old_desc.units.dimTime && + $7 == mp->old_desc.units.dimCount && + $9 == mp->old_desc.units.scaleSpace && + $11 == mp->old_desc.units.scaleTime && + $13 == mp->old_desc.units.scaleCount) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): units: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmUnitsStr(&mp->old_desc.units)); + yywarn(mess); + } + } + else { + mp->new_desc.units.dimSpace = $3; + mp->new_desc.units.dimTime = $5; + mp->new_desc.units.dimCount = $7; + mp->new_desc.units.scaleSpace = $9; + mp->new_desc.units.scaleTime = $11; + mp->new_desc.units.scaleCount = $13; + mp->flags |= METRIC_CHANGE_UNITS; + if ($14 == 1) { + if ($3 == mp->old_desc.units.dimSpace && + $5 == mp->old_desc.units.dimTime && + $7 == mp->old_desc.units.dimCount) + /* OK, no dim changes */ + mp->flags |= METRIC_RESCALE; + else { + if (wflag) { + snprintf(mess, sizeof(mess), "Metric: %s (%s): Dimension changed, cannot rescale", mp->old_name, pmIDStr(mp->old_desc.pmid)); + yywarn(mess); + } + } + } + else if (sflag) { + if ($3 == mp->old_desc.units.dimSpace && + $5 == mp->old_desc.units.dimTime && + $7 == mp->old_desc.units.dimCount) + mp->flags |= METRIC_RESCALE; + } + } + } + } + | TOK_DELETE + { + metricspec_t *mp; + for (mp = walk_metric(W_START, METRIC_DELETE, "delete"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_DELETE, "delete")) { + mp->flags |= METRIC_DELETE; + } + } + | TOK_PMID TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting <domain>.<cluster>.<item> or <domain>.<cluster>.* or <domain>.*.* in pmid clause"); + yyerror(mess); + } + | TOK_NAME TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting metric name in iname clause"); + yyerror(mess); + } + | TOK_TYPE TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting XXX (from PM_TYPE_XXX) in type clause"); + yyerror(mess); + } + | TOK_INDOM TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or NULL in indom clause"); + yyerror(mess); + } + | TOK_SEM TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting XXX (from PM_SEM_XXX) in sem clause"); + yyerror(mess); + } + | TOK_UNITS TOK_ASSIGN + { + snprintf(mess, sizeof(mess), "Expecting 3 numeric values for dim* fields of units"); + yyerror(mess); + } + | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA + { + snprintf(mess, sizeof(mess), "Expecting 0 or XXX (from PM_SPACE_XXX) for scaleSpace field of units"); + yyerror(mess); + } + | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA + { + snprintf(mess, sizeof(mess), "Expecting 0 or XXX (from PM_TIME_XXX) for scaleTime field of units"); + yyerror(mess); + } + | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA TOK_TIME_NAME TOK_COMMA + { + snprintf(mess, sizeof(mess), "Expecting 0 or ONE for scaleCount field of units"); + yyerror(mess); + } + ; + +null_or_indom : indom_int + | TOK_NULL_INT + { + $$ = PM_INDOM_NULL; + } + ; + +pick : TOK_OUTPUT TOK_INST number + { + output = OUTPUT_ONE; + one_inst = $3; + one_name = NULL; + } + | TOK_OUTPUT TOK_INAME TOK_STRING + { + output = OUTPUT_ONE; + one_inst = PM_IN_NULL; + one_name = $3; + } + | TOK_OUTPUT TOK_OUTPUT_TYPE + { + output = $2; + } + | TOK_OUTPUT + { + snprintf(mess, sizeof(mess), "Expecting FIRST or LAST or INST or INAME or MIN or MAX or AVG for OUTPUT instance option"); + yyerror(mess); + } + | /* nothing */ + ; + +rescaleopt : TOK_RESCALE { $$ = 1; } + | /* nothing */ + { $$ = 0; } + ; + +%% diff --git a/src/pmlogrewrite/indom.c b/src/pmlogrewrite/indom.c new file mode 100644 index 0000000..25592cb --- /dev/null +++ b/src/pmlogrewrite/indom.c @@ -0,0 +1,381 @@ +/* + * Indom metadata support for pmlogrewrite + * + * Copyright (c) 2011 Ken McDonell. 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 "logger.h" +#include <assert.h> + +/* + * Find or create a new indomspec_t + */ +indomspec_t * +start_indom(pmInDom indom) +{ + indomspec_t *ip; + int i; + + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (indom == ip->old_indom) + break; + } + if (ip == NULL) { + int numinst; + int *instlist; + char **namelist; + + numinst = pmGetInDomArchive(indom, &instlist, &namelist); + if (numinst < 0) { + if (wflag) { + snprintf(mess, sizeof(mess), "Instance domain %s: %s", pmInDomStr(indom), pmErrStr(numinst)); + yywarn(mess); + } + return NULL; + } + + ip = (indomspec_t *)malloc(sizeof(indomspec_t)); + if (ip == NULL) { + fprintf(stderr, "indomspec malloc(%d) failed: %s\n", (int)sizeof(indomspec_t), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + ip->i_next = indom_root; + indom_root = ip; + ip->indom_flags = 0; + ip->inst_flags = (int *)malloc(numinst*sizeof(int)); + if (ip->inst_flags == NULL) { + fprintf(stderr, "indomspec flags malloc(%d) failed: %s\n", (int)(numinst*sizeof(int)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + for (i = 0; i < numinst; i++) + ip->inst_flags[i] = 0; + ip->old_indom = indom; + ip->new_indom = indom; + ip->numinst = numinst; + ip->old_inst = instlist; + ip->new_inst = (int *)malloc(numinst*sizeof(int)); + if (ip->new_inst == NULL) { + fprintf(stderr, "new_inst malloc(%d) failed: %s\n", (int)(numinst*sizeof(int)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + ip->old_iname = namelist; + ip->new_iname = (char **)malloc(numinst*sizeof(char *)); + if (ip->new_iname == NULL) { + fprintf(stderr, "new_iname malloc(%d) failed: %s\n", (int)(numinst*sizeof(char *)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + } + + return ip; +} + +int +change_inst_by_name(pmInDom indom, char *old, char *new) +{ + int i; + indomspec_t *ip; + + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (indom == ip->old_indom) + break; + } + assert(ip != NULL); + + for (i = 0; i < ip->numinst; i++) { + if (inst_name_eq(ip->old_iname[i], old) > 0) { + if ((new == NULL && ip->inst_flags[i]) || + (ip->inst_flags[i] & (INST_CHANGE_INAME|INST_DELETE))) { + snprintf(mess, sizeof(mess), "Duplicate or conflicting clauses for instance [%d] \"%s\" of indom %s", + ip->old_inst[i], ip->old_iname[i], pmInDomStr(indom)); + return -1; + } + break; + } + } + if (i == ip->numinst) { + if (wflag) { + snprintf(mess, sizeof(mess), "Unknown instance \"%s\" in iname clause for indom %s", old, pmInDomStr(indom)); + yywarn(mess); + } + return 0; + } + + if (new == NULL) { + ip->inst_flags[i] |= INST_DELETE; + ip->new_iname[i] = NULL; + return 0; + } + + if (strcmp(ip->old_iname[i], new) == 0) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Instance domain %s: Instance: \"%s\": No change", pmInDomStr(indom), ip->old_iname[i]); + yywarn(mess); + } + } + else { + ip->inst_flags[i] |= INST_CHANGE_INAME; + ip->new_iname[i] = new; + } + + return 0; +} + +int +change_inst_by_inst(pmInDom indom, int old, int new) +{ + int i; + indomspec_t *ip; + + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (indom == ip->old_indom) + break; + } + assert(ip != NULL); + + for (i = 0; i < ip->numinst; i++) { + if (ip->old_inst[i] == old) { + if ((new == PM_IN_NULL && ip->inst_flags[i]) || + (ip->inst_flags[i] & (INST_CHANGE_INST|INST_DELETE))) { + snprintf(mess, sizeof(mess), "Duplicate or conflicting clauses for instance [%d] \"%s\" of indom %s", + ip->old_inst[i], ip->old_iname[i], pmInDomStr(indom)); + return -1; + } + break; + } + } + if (i == ip->numinst) { + if (wflag) { + snprintf(mess, sizeof(mess), "Unknown instance %d in inst clause for indom %s", old, pmInDomStr(indom)); + yywarn(mess); + } + return 0; + } + + if (new == PM_IN_NULL) { + ip->inst_flags[i] |= INST_DELETE; + ip->new_inst[i] = PM_IN_NULL; + return 0; + } + + if (ip->old_inst[i] == new) { + /* no change ... */ + if (wflag) { + snprintf(mess, sizeof(mess), "Instance domain %s: Instance: %d: No change", pmInDomStr(indom), ip->old_inst[i]); + yywarn(mess); + } + } + else { + ip->new_inst[i] = new; + ip->inst_flags[i] |= INST_CHANGE_INST; + } + + return 0; +} + +typedef struct { + __pmLogHdr hdr; + __pmTimeval stamp; + pmInDom indom; + int numinst; + char other[1]; +} indom_t; + +/* + * reverse the logic of __pmLogPutInDom() + */ +static void +_pmUnpackInDom(__pmPDU *pdubuf, pmInDom *indom, __pmTimeval *tp, int *numinst, int **instlist, char ***inamelist) +{ + indom_t *idp; + int i; + int *ip; + char *strbuf; + + idp = (indom_t *)pdubuf; + + tp->tv_sec = ntohl(idp->stamp.tv_sec); + tp->tv_usec = ntohl(idp->stamp.tv_usec); + *indom = ntoh_pmInDom(idp->indom); + *numinst = ntohl(idp->numinst); + *instlist = (int *)malloc(*numinst * sizeof(int)); + if (*instlist == NULL) { + fprintf(stderr, "_pmUnpackInDom instlist malloc(%d) failed: %s\n", (int)(*numinst * sizeof(int)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + ip = (int *)idp->other; + for (i = 0; i < *numinst; i++) + (*instlist)[i] = ntohl(*ip++); + *inamelist = (char **)malloc(*numinst * sizeof(char *)); + if (*inamelist == NULL) { + fprintf(stderr, "_pmUnpackInDom inamelist malloc(%d) failed: %s\n", (int)(*numinst * sizeof(char *)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + /* + * ip[i] is stridx[i], which is offset into strbuf[] + */ + strbuf = (char *)&ip[*numinst]; + for (i = 0; i < *numinst; i++) { + (*inamelist)[i] = &strbuf[ntohl(ip[i])]; + } +} + +/* + * Note: + * We unpack the indom metadata record _again_ (was already done when + * the input archive was opened), but the data structure behind + * __pmLogCtl has differences for 32-bit and 64-bit pointers and + * modifying it as part of the rewrite could make badness break + * out later. It is safer to do it again, populate local copies + * of instlist[] and inamelist[], dink with 'em and then toss them + * away. + */ +void +do_indom(void) +{ + long out_offset; + pmInDom indom; + __pmTimeval stamp; + int numinst; + int *instlist; + char **inamelist; + indomspec_t *ip; + int sts; + int i; + int j; + int need_alloc = 0; + + out_offset = ftell(outarch.logctl.l_mdfp); + _pmUnpackInDom(inarch.metarec, &indom, &stamp, &numinst, &instlist, &inamelist); + + /* + * global time stamp adjustment (if any has already been done in the + * PDU buffer, so this is reflected in the unpacked value of stamp. + */ + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (ip->old_indom != indom) + continue; + if (ip->indom_flags & INDOM_DUPLICATE) { + /* + * save the old indom without changes, then operate on the + * duplicate + */ + if ((sts = __pmLogPutInDom(&outarch.logctl, indom, &stamp, numinst, instlist, inamelist)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutInDom: %s: %s\n", + pmProgname, pmInDomStr(indom), pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Metadata: write pre-duplicate InDom %s @ offset=%ld\n", pmInDomStr(indom), out_offset); + } +#endif + out_offset = ftell(outarch.logctl.l_mdfp); + } + if (ip->new_indom != ip->old_indom) + indom = ip->new_indom; + for (i = 0; i < ip->numinst; i++) { + for (j = 0; j < numinst; j++) { + if (ip->old_inst[i] == instlist[j]) + break; + } + if (j == numinst) + continue; + if (ip->inst_flags[i] & INST_DELETE) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Delete: instance %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], pmInDomStr(ip->old_indom)); +#endif + j++; + while (j < numinst) { + instlist[j-1] = instlist[j]; + inamelist[j-1] = inamelist[j]; + j++; + } + need_alloc = 1; + numinst--; + } + else { + if (ip->inst_flags[i] & INST_CHANGE_INST) + instlist[j] = ip->new_inst[i]; + if (ip->inst_flags[i] & INST_CHANGE_INAME) { + inamelist[j] = ip->new_iname[i]; + need_alloc = 1; + } +#if PCP_DEBUG + if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) && (pmDebug & DBG_TRACE_APPL1)) { + if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) == (INST_CHANGE_INST | INST_CHANGE_INAME)) + fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->new_iname[i], ip->new_inst[i], pmInDomStr(ip->old_indom)); + else if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) == INST_CHANGE_INST) + fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->old_iname[i], ip->new_inst[i], pmInDomStr(ip->old_indom)); + else + fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->new_iname[i], ip->old_inst[i], pmInDomStr(ip->old_indom)); + } +#endif + } + } + } + + if (need_alloc) { + /* + * __pmLogPutInDom assumes the elements of inamelist[] point into + * of a contiguous allocation starting at inamelist[0] ... if we've + * changed an instance name or moved instance names about, then we + * need to reallocate the strings for inamelist[] + */ + int need = 0; + char *new; + char *p; + + for (j = 0; j < numinst; j++) + need += strlen(inamelist[j]) + 1; + new = (char *)malloc(need); + if (new == NULL) { + fprintf(stderr, "inamelist[] malloc(%d) failed: %s\n", need, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + p = new; + for (j = 0; j < numinst; j++) { + strcpy(p, inamelist[j]); + inamelist[j] = p; + p += strlen(p) + 1; + } + } + + if ((sts = __pmLogPutInDom(&outarch.logctl, indom, &stamp, numinst, instlist, inamelist)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutInDom: %s: %s\n", + pmProgname, pmInDomStr(indom), pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Metadata: write InDom %s @ offset=%ld\n", pmInDomStr(indom), out_offset); + } +#endif + + free(instlist); + if (need_alloc) + free(inamelist[0]); + free(inamelist); +} diff --git a/src/pmlogrewrite/lex.l b/src/pmlogrewrite/lex.l new file mode 100644 index 0000000..4f7e6d7 --- /dev/null +++ b/src/pmlogrewrite/lex.l @@ -0,0 +1,223 @@ +/* + * Copyright (c) 1997-2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2011 Ken McDonell. 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +/* + * pmlogrewrite configfile lexical scanner + */ +#include "pmapi.h" +#include "impl.h" +#include "logger.h" +#include <errno.h> + +char mess[256]; + +#define LEX_NONE 0 +#define LEX_GLOBAL 1 +#define LEX_INDOM 2 +#define LEX_METRIC 3 + +int mystate = LEX_NONE; + +static int comma_count; + +#include "gram.tab.h" + +static char * +dupstr(char *s, int strip_quotes) +{ + char *p; + if (strip_quotes) + p = strdup(&s[1]); + else + p = strdup(s); + if (p == NULL) { + fprintf(stderr, "Failed strdup(\"%s\") in lexer: %s\n", s, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + if (strip_quotes) { + char *pend = p; + while (*pend != '\0') + pend++; + *--pend = '\0'; + } + return p; +} + +%} + +%option noinput +%option nounput + +%{ +#ifdef FLEX_SCANNER +#ifndef YY_NO_UNPUT +#define YY_NO_UNPUT +#endif +#endif +%} + +%s none glob host indom metric type sem units space time count output +%option case-insensitive + +%% + +<INITIAL>"global" { mystate= LEX_GLOBAL; return TOK_GLOBAL; } +<INITIAL>"metric" { mystate = LEX_METRIC; return TOK_METRIC; } +<INITIAL>"indom" { mystate = LEX_INDOM; return TOK_INDOM; } + +<glob>"hostname" { BEGIN(host); return TOK_HOSTNAME; } +<glob>"time" { return TOK_TIME; } +<glob>"tz" { return TOK_TZ; } + /* Hostname */ +<host>[A-Za-z0-9][A-Za-z0-9.-]* { yylval.str = dupstr(yytext, 0); BEGIN(glob); return TOK_HNAME; } + +<indom,metric>"delete" { return TOK_DELETE; } +<indom>"indom" { return TOK_INDOM; } +<indom>"duplicate" { return TOK_DUPLICATE; } +<indom>"iname" { return TOK_INAME; } +<indom>"inst" { return TOK_INST; } + +<metric>"name" { return TOK_NAME; } +<metric>"pmid" { return TOK_PMID; } +<metric>"type" { BEGIN(type); return TOK_TYPE; } +<metric>"indom" { return TOK_INDOM; } +<metric>"NULL" { return TOK_NULL_INT; } +<metric>"output" { BEGIN(output); return TOK_OUTPUT; } +<metric>"sem" { BEGIN(sem); return TOK_SEM; } +<metric>"units" { BEGIN(units); comma_count = 0; return TOK_UNITS; } + +<type>"32" { yylval.ival = PM_TYPE_32; BEGIN(metric); return TOK_TYPE_NAME; } +<type>"U32" { yylval.ival = PM_TYPE_U32; BEGIN(metric); return TOK_TYPE_NAME; } +<type>"64" { yylval.ival = PM_TYPE_64; BEGIN(metric); return TOK_TYPE_NAME; } +<type>"U64" { yylval.ival = PM_TYPE_U64; BEGIN(metric); return TOK_TYPE_NAME; } +<type>"FLOAT" { yylval.ival = PM_TYPE_FLOAT; BEGIN(metric); return TOK_TYPE_NAME; } +<type>"DOUBLE" { yylval.ival = PM_TYPE_DOUBLE; BEGIN(metric); return TOK_TYPE_NAME; } + +<output>"first" { yylval.ival = OUTPUT_FIRST; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"last" { yylval.ival = OUTPUT_LAST; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"min" { yylval.ival = OUTPUT_MIN; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"max" { yylval.ival = OUTPUT_MAX; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"sum" { yylval.ival = OUTPUT_SUM; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"avg" { yylval.ival = OUTPUT_AVG; BEGIN(metric); return TOK_OUTPUT_TYPE; } +<output>"inst" { BEGIN(metric); return TOK_INST; } +<output>"iname" { BEGIN(metric); return TOK_INAME; } + +<sem>"COUNTER" { yylval.ival = PM_SEM_COUNTER; BEGIN(metric); return TOK_SEM_NAME; } +<sem>"INSTANT" { yylval.ival = PM_SEM_INSTANT; BEGIN(metric); return TOK_SEM_NAME; } +<sem>"DISCRETE" { yylval.ival = PM_SEM_DISCRETE; BEGIN(metric); return TOK_SEM_NAME; } + +<units>"," { + ++comma_count; + switch (comma_count) { + case 1: + case 2: + break; + case 3: + BEGIN(space); + break; + case 4: + BEGIN(time); + break; + case 5: + BEGIN(count); + break; + } + return TOK_COMMA; + } +<metric>"rescale" { return TOK_RESCALE; } + +<space>"BYTE" { yylval.ival = PM_SPACE_BYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"KBYTE" { yylval.ival = PM_SPACE_KBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"MBYTE" { yylval.ival = PM_SPACE_MBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"GBYTE" { yylval.ival = PM_SPACE_GBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"TBYTE" { yylval.ival = PM_SPACE_TBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"PBYTE" { yylval.ival = PM_SPACE_PBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"EBYTE" { yylval.ival = PM_SPACE_EBYTE; BEGIN(units); return TOK_SPACE_NAME; } +<space>"0" { yylval.ival = 0; BEGIN(units); return TOK_SPACE_NAME; } + +<time>"NSEC" { yylval.ival = PM_TIME_NSEC; BEGIN(units); return TOK_TIME_NAME; } +<time>"USEC" { yylval.ival = PM_TIME_USEC; BEGIN(units); return TOK_TIME_NAME; } +<time>"MSEC" { yylval.ival = PM_TIME_MSEC; BEGIN(units); return TOK_TIME_NAME; } +<time>"SEC" { yylval.ival = PM_TIME_SEC; BEGIN(units); return TOK_TIME_NAME; } +<time>"MIN" { yylval.ival = PM_TIME_MIN; BEGIN(units); return TOK_TIME_NAME; } +<time>"HOUR" { yylval.ival = PM_TIME_HOUR; BEGIN(units); return TOK_TIME_NAME; } +<time>"0" { yylval.ival = 0; BEGIN(units); return TOK_TIME_NAME; } + +<count>"ONE" { yylval.ival = PM_COUNT_ONE; BEGIN(metric); return TOK_COUNT_NAME; } +<count>[0-9]+ { yylval.ival = atoi(yytext); BEGIN(metric); return TOK_COUNT_NAME; } + +\"[^\"\n][^\"\n]*\" { yylval.str = dupstr(yytext, 1); return TOK_STRING; } + +[0-9]+ { yylval.str = dupstr(yytext, 0); return TOK_NUMBER; } + +[0-9]+\.[0-9]* { yylval.str = dupstr(yytext, 0); return TOK_FLOAT; } + +[0-9]+\.\* { yylval.str = dupstr(yytext, 0); return TOK_INDOM_STAR; } + +[0-9]+\.[0-9]+\.[0-9]+ { yylval.str = dupstr(yytext, 0); return TOK_PMID_INT; } + +[0-9]+\.[0-9]+\.\* { yylval.str = dupstr(yytext, 0); return TOK_PMID_STAR; } +[0-9]+\.\*\.\* { yylval.str = dupstr(yytext, 0); return TOK_PMID_STAR; } + + /* Generic name, e.g. for identifier or metric or hostname */ +[A-Za-z][A-Za-z0-9_.]* { yylval.str = dupstr(yytext, 0); return TOK_GNAME; } + +\#.* { } + +[ \t\r]+ { } + +"->" { return TOK_ASSIGN; } +"{" { + if (mystate == LEX_GLOBAL) BEGIN(glob); + if (mystate == LEX_INDOM) BEGIN(indom); + if (mystate == LEX_METRIC) BEGIN(metric); +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) + fprintf(stderr, "lex: [%d] { begin state=%d\n", lineno, mystate); +#endif + return TOK_LBRACE; + } +"}" { +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) + fprintf(stderr, "lex: [%d] } end state=%d\n", lineno, mystate); +#endif + mystate = LEX_NONE; + BEGIN(INITIAL); return TOK_RBRACE; + } +"+" { return TOK_PLUS; } +"-" { return TOK_MINUS; } +":" { return TOK_COLON; } +"," { return TOK_COMMA; } + +\n { lineno++; } +. { + snprintf(mess, sizeof(mess), "Unexpected character '%c'", + yytext[0]); + yyerror(mess); + } +%% + +int +yywrap(void) +{ + return(1); +} diff --git a/src/pmlogrewrite/logger.h b/src/pmlogrewrite/logger.h new file mode 100644 index 0000000..36c9712 --- /dev/null +++ b/src/pmlogrewrite/logger.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 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. + * + * common data structures for pmlogextract + */ + +#ifndef _LOGGER_H +#define _LOGGER_H + +extern int sflag; /* -s from command line */ +extern int vflag; /* -v from command line */ +extern int wflag; /* -w from command line */ + +/* + * Global rewrite specifications + */ +typedef struct { + int flags; /* GLOBAL_* flags */ + struct timeval time; /* timestamp shift */ + char hostname[PM_LOG_MAXHOSTLEN]; + char tz[PM_TZ_MAXLEN]; +} global_t; + +/* values for global_t flags */ +#define GLOBAL_CHANGE_TIME 1 +#define GLOBAL_CHANGE_HOSTNAME 2 +#define GLOBAL_CHANGE_TZ 4 + +extern global_t global; + +/* + * Rewrite specifications for an instance domain + */ +typedef struct indomspec { + struct indomspec *i_next; + int indom_flags; /* INDOM_* flags */ + int *inst_flags; /* INST_* flags */ + pmInDom old_indom; + pmInDom new_indom; /* old_indom if no change */ + int numinst; + int *old_inst; /* filled from pmGetInDomArchive() */ + char **old_iname; /* filled from pmGetInDomArchive() */ + int *new_inst; + char **new_iname; +} indomspec_t; + +/* values for indomspec_t indom_flags */ +#define INDOM_ADD 1 +#define INDOM_DELETE 2 +#define INDOM_DUPLICATE 4 +/* values for indomspec_t inst_flags[] */ +#define INST_CHANGE_INST 16 +#define INST_CHANGE_INAME 32 +#define INST_DELETE 64 + +extern indomspec_t *indom_root; + +/* + * Rewrite specifications for a metric + */ +typedef struct metricspec { + struct metricspec *m_next; + int flags; /* METRIC_* flags */ + int output; /* OUTPUT_* values */ + int one_inst; /* for OUTPUT_ONE INST */ + char *one_name; /* for OUTPUT_ONE NAME */ + char *old_name; + char *new_name; + pmDesc old_desc; + pmDesc new_desc; + indomspec_t *ip; /* for instance id changes */ +} metricspec_t; + +/* values for metricspec_t flags[] */ +#define METRIC_CHANGE_PMID 1 +#define METRIC_CHANGE_NAME 2 +#define METRIC_CHANGE_TYPE 4 +#define METRIC_CHANGE_INDOM 8 +#define METRIC_CHANGE_SEM 16 +#define METRIC_CHANGE_UNITS 32 +#define METRIC_DELETE 64 +#define METRIC_RESCALE 128 + +/* values for output when indom (numval >= 1) => PM_INDOM_NULL (numval = 1) */ +#define OUTPUT_ALL 0 +#define OUTPUT_FIRST 1 +#define OUTPUT_LAST 2 +#define OUTPUT_ONE 3 +#define OUTPUT_MIN 4 +#define OUTPUT_MAX 5 +#define OUTPUT_SUM 6 +#define OUTPUT_AVG 7 + +extern metricspec_t *metric_root; + +/* + * Input archive control + */ +typedef struct { + int ctx; + __pmContext *ctxp; + char *name; + pmLogLabel label; + __pmPDU *metarec; + __pmPDU *logrec; + pmResult *rp; + int mark; /* need EOL marker */ +} inarch_t; + +extern inarch_t inarch; /* input archive */ + +/* + * Output archive control + */ +typedef struct { + char *name; /* base name of output archive */ + __pmLogCtl logctl; /* libpcp control */ +} outarch_t; + +extern outarch_t outarch; /* output archive */ + +/* size of a string length field */ +#define LENSIZE 4 + +/* generic error message buffer */ +extern char mess[256]; + +/* yylex() gets intput from here ... */ +extern char *configfile; +extern FILE *fconfig; +extern int lineno; +extern FILE *yyin; + +extern void yyerror(char *); +extern void yywarn(char *); +extern void yysemantic(char *); +extern int yylex(void); +extern int yyparse(void); + +#define W_START 1 +#define W_NEXT 2 +#define W_NONE 3 + +extern int _pmLogGet(__pmLogCtl *, int, __pmPDU **); +extern int _pmLogPut(FILE *, __pmPDU *); +extern int _pmLogRename(const char *, const char *); +extern int _pmLogRemove(const char *); +extern pmUnits ntoh_pmUnits(pmUnits); +#define ntoh_pmInDom(indom) ntohl(indom) +#define ntoh_pmID(pmid) ntohl(pmid) + +extern metricspec_t *start_metric(pmID); +extern indomspec_t *start_indom(pmInDom); +extern int change_inst_by_inst(pmInDom, int, int); +extern int change_inst_by_name(pmInDom, char *, char *); +extern int inst_name_eq(const char *, const char *); + +extern char *SemStr(int); +extern void newvolume(int); + +extern void do_desc(void); +extern void do_indom(void); +extern void do_result(void); + +extern void abandon(void); + +#endif /* _LOGGER_H */ diff --git a/src/pmlogrewrite/logio.c b/src/pmlogrewrite/logio.c new file mode 100644 index 0000000..010e0e3 --- /dev/null +++ b/src/pmlogrewrite/logio.c @@ -0,0 +1,175 @@ +/* + * utils for pmlogextract + * + * 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 <assert.h> +#include "pmapi.h" +#include "impl.h" + +/* + * raw read of next log record - largely stolen from __pmLogRead in libpcp + */ +int +_pmLogGet(__pmLogCtl *lcp, int vol, __pmPDU **pb) +{ + int head; + int tail; + int sts; + long offset; + char *p; + __pmPDU *lpb; + FILE *f; + + if (vol == PM_LOG_VOL_META) + f = lcp->l_mdfp; + else + f = lcp->l_mfp; + + offset = ftell(f); + assert(offset >= 0); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) { + fprintf(stderr, "_pmLogGet: fd=%d vol=%d posn=%ld ", + fileno(f), vol, offset); + } +#endif + +again: + sts = (int)fread(&head, 1, sizeof(head), f); + if (sts != sizeof(head)) { + if (sts == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "AFTER end\n"); +#endif + fseek(f, offset, SEEK_SET); + if (vol != PM_LOG_VOL_META) { + if (lcp->l_curvol < lcp->l_maxvol) { + if (__pmLogChangeVol(lcp, lcp->l_curvol+1) == 0) { + f = lcp->l_mfp; + goto again; + } + } + } + return PM_ERR_EOL; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "Error: hdr fread=%d %s\n", sts, osstrerror()); +#endif + if (sts > 0) + return PM_ERR_LOGREC; + else + return -oserror(); + } + + if ((lpb = (__pmPDU *)malloc(ntohl(head))) == NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "Error: __pmFindPDUBuf(%d) %s\n", + (int)ntohl(head), osstrerror()); +#endif + fseek(f, offset, SEEK_SET); + return -oserror(); + } + + lpb[0] = head; + if ((sts = (int)fread(&lpb[1], 1, ntohl(head) - sizeof(head), f)) != ntohl(head) - sizeof(head)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "Error: data fread=%d %s\n", sts, osstrerror()); +#endif + free(lpb); + if (sts == 0) { + fseek(f, offset, SEEK_SET); + return PM_ERR_EOL; + } + else if (sts > 0) + return PM_ERR_LOGREC; + else + return -oserror(); + } + + + p = (char *)lpb; + memcpy(&tail, &p[ntohl(head) - sizeof(head)], sizeof(head)); + if (head != tail) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "Error: head-tail mismatch (%d-%d)\n", + (int)ntohl(head), (int)ntohl(tail)); +#endif + free(lpb); + return PM_ERR_LOGREC; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) { + if (vol != PM_LOG_VOL_META || ntohl(lpb[1]) == TYPE_INDOM) { + fprintf(stderr, "@"); + if (sts >= 0) { + struct timeval stamp; + __pmTimeval *tvp = (__pmTimeval *)&lpb[vol == PM_LOG_VOL_META ? 2 : 1]; + stamp.tv_sec = ntohl(tvp->tv_sec); + stamp.tv_usec = ntohl(tvp->tv_usec); + __pmPrintStamp(stderr, &stamp); + } + else + fprintf(stderr, "unknown time"); + } + fprintf(stderr, " len=%d (incl head+tail)\n", (int)ntohl(head)); + } +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PDU) { + int i, j; + struct timeval stamp; + __pmTimeval *tvp = (__pmTimeval *)&lpb[vol == PM_LOG_VOL_META ? 2 : 1]; + fprintf(stderr, "_pmLogGet"); + if (vol != PM_LOG_VOL_META || ntohl(lpb[1]) == TYPE_INDOM) { + fprintf(stderr, " timestamp="); + stamp.tv_sec = ntohl(tvp->tv_sec); + stamp.tv_usec = ntohl(tvp->tv_usec); + __pmPrintStamp(stderr, &stamp); + } + fprintf(stderr, " " PRINTF_P_PFX "%p ... " PRINTF_P_PFX "%p", lpb, &lpb[ntohl(head)/sizeof(__pmPDU) - 1]); + fputc('\n', stderr); + fprintf(stderr, "%03d: ", 0); + for (j = 0, i = 0; j < ntohl(head)/sizeof(__pmPDU); j++) { + if (i == 8) { + fprintf(stderr, "\n%03d: ", j); + i = 0; + } + fprintf(stderr, "%08x ", lpb[j]); + i++; + } + fputc('\n', stderr); + } +#endif + + *pb = lpb; + return 0; +} + +pmUnits +ntoh_pmUnits(pmUnits units) +{ + unsigned int x; + + x = ntohl(*(unsigned int *)&units); + units = *(pmUnits *)&x; + return units; +} diff --git a/src/pmlogrewrite/metric.c b/src/pmlogrewrite/metric.c new file mode 100644 index 0000000..31da308 --- /dev/null +++ b/src/pmlogrewrite/metric.c @@ -0,0 +1,230 @@ +/* + * Metric metadata support for pmlogrewrite + * + * Copyright (c) 2011 Ken McDonell. 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 "logger.h" +#include <assert.h> + +/* + * Find or create a new metricspec_t + */ +metricspec_t * +start_metric(pmID pmid) +{ + metricspec_t *mp; + int sts; + +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) + fprintf(stderr, "start_metric(%s)", pmIDStr(pmid)); +#endif + + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (pmid == mp->old_desc.pmid) { +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) + fprintf(stderr, " -> %s\n", mp->old_name); +#endif + break; + } + } + if (mp == NULL) { + char *name; + pmDesc desc; + + sts = pmNameID(pmid, &name); + if (sts < 0) { + if (wflag) { + snprintf(mess, sizeof(mess), "Metric %s pmNameID: %s", pmIDStr(pmid), pmErrStr(sts)); + yywarn(mess); + } + return NULL; + } + sts = pmLookupDesc(pmid, &desc); + if (sts < 0) { + if (wflag) { + snprintf(mess, sizeof(mess), "Metric %s: pmLookupDesc: %s", pmIDStr(pmid), pmErrStr(sts)); + yywarn(mess); + } + free(name); + return NULL; + } + + mp = (metricspec_t *)malloc(sizeof(metricspec_t)); + if (mp == NULL) { + fprintf(stderr, "metricspec malloc(%d) failed: %s\n", (int)sizeof(metricspec_t), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + mp->m_next = metric_root; + metric_root = mp; + mp->output = OUTPUT_ALL; + mp->one_inst = 0; + mp->one_name = NULL; + mp->old_name = name; + mp->old_desc = desc; + mp->new_desc = mp->old_desc; + mp->flags = 0; + mp->ip = NULL; +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) + fprintf(stderr, " -> %s [new entry]\n", mp->old_name); +#endif + } + + return mp; +} + +typedef struct { + __pmLogHdr hdr; + pmDesc desc; + int numnames; + char strbuf[1]; +} desc_t; + +/* + * reverse the logic of __pmLogPutDesc() + */ +static void +_pmUnpackDesc(__pmPDU *pdubuf, pmDesc *desc, int *numnames, char ***names) +{ + desc_t *pp; + int i; + char *p; + int slen; + + pp = (desc_t *)pdubuf; + desc->type = ntohl(pp->desc.type); + desc->sem = ntohl(pp->desc.sem); + desc->indom = ntoh_pmInDom(pp->desc.indom); + desc->units = ntoh_pmUnits(pp->desc.units); + desc->pmid = ntoh_pmID(pp->desc.pmid); + *numnames = ntohl(pp->numnames); + *names = (char **)malloc(*numnames * sizeof(*names[1])); + if (*names == NULL) { + fprintf(stderr, "_pmUnpackDesc malloc(%d) failed: %s\n", (int)(*numnames * sizeof(*names[1])), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + + p = pp->strbuf; + for (i = 0; i < *numnames; i++) { + memcpy(&slen, p, LENSIZE); + slen = ntohl(slen); + p += LENSIZE; + (*names)[i] = malloc(slen+1); + if ((*names)[i] == NULL) { + fprintf(stderr, "_pmUnpackDesc malloc(%d) failed: %s\n", slen+1, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + strncpy((*names)[i], p, slen); + (*names)[i][slen] = '\0'; + p += slen; + } + + return; +} + +/* + * rewrite pmDesc from metadata, returns + * -1 delete this pmDesc + * 0 no change + * 1 changed + */ +void +do_desc(void) +{ + metricspec_t *mp; + pmDesc desc; + int i; + int sts; + int numnames; + char **names; + long out_offset; + + out_offset = ftell(outarch.logctl.l_mdfp); + _pmUnpackDesc(inarch.metarec, &desc, &numnames, &names); + + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (desc.pmid != mp->old_desc.pmid || mp->flags == 0) + continue; + if (mp->flags & METRIC_DELETE) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Delete: pmDesc for %s\n", pmIDStr(desc.pmid)); +#endif + goto done; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Rewrite: pmDesc for %s\n", pmIDStr(desc.pmid)); +#endif + if (mp->flags & METRIC_CHANGE_PMID) + desc.pmid = mp->new_desc.pmid; + if (mp->flags & METRIC_CHANGE_NAME) { + for (i = 0; i < numnames; i++) { + if (strcmp(names[i], mp->old_name) == 0) { + free(names[i]); + names[i] = strdup(mp->new_name); + if (names[i] == NULL) { + fprintf(stderr, "do_desc strdup(%s) failed: %s\n", mp->new_name, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + break; + } + } + if (i == numnames) { + fprintf(stderr, "%s: Botch: old name %s not found in list of %d names for pmid %s ...", + pmProgname, mp->old_name, numnames, pmIDStr(mp->old_desc.pmid)); + for (i = 0; i < numnames; i++) { + if (i > 0) fputc(',', stderr); + fprintf(stderr, " %s", names[i]); + } + fputc('\n', stderr); + abandon(); + /*NOTREACHED*/ + } + } + if (mp->flags & METRIC_CHANGE_TYPE) + desc.type = mp->new_desc.type; + if (mp->flags & METRIC_CHANGE_INDOM) + desc.indom = mp->new_desc.indom; + if (mp->flags & METRIC_CHANGE_SEM) + desc.sem = mp->new_desc.sem; + if (mp->flags & METRIC_CHANGE_UNITS) + desc.units = mp->new_desc.units; /* struct assignment */ + break; + } + if ((sts = __pmLogPutDesc(&outarch.logctl, &desc, numnames, names)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutDesc: %s (%s): %s\n", + pmProgname, names[0], pmIDStr(desc.pmid), pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Metadata: write PMID %s @ offset=%ld\n", pmIDStr(desc.pmid), out_offset); +#endif + +done: + for (i = 0; i < numnames; i++) + free(names[i]); + free(names); + return; +} diff --git a/src/pmlogrewrite/pmlogrewrite.c b/src/pmlogrewrite/pmlogrewrite.c new file mode 100644 index 0000000..625366d --- /dev/null +++ b/src/pmlogrewrite/pmlogrewrite.c @@ -0,0 +1,1318 @@ +/* + * pmlogrewrite - config-driven stream editor for PCP archives + * + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2011 Ken McDonell. All Rights Reserved. + * 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" +#include <assert.h> + +global_t global; +indomspec_t *indom_root; +metricspec_t *metric_root; +int lineno; + +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + { "config", 1, 'c', "PATH", "file or directory to load rules from" }, + { "check", 0, 'C', 0, "parse config file(s) and quit (verbose warnings also)" }, + { "desperate", 0, 'd', 0, "desperate, save output archive even after error" }, + { "", 0, 'i', 0, "rewrite in place, input-archive will be over-written" }, + { "quick", 0, 'q', 0, "quick mode, no output if no change" }, + { "scale", 0, 's', 0, "do scale conversion" }, + { "verbose", 0, 'v', 0, "increased diagnostic verbosity" }, + { "warnings", 0, 'w', 0, "emit warnings [default is silence]" }, + PMAPI_OPTIONS_TEXT(""), + PMAPI_OPTIONS_TEXT("output-archive is required unless -i is specified"), + PMAPI_OPTIONS_END +}; + +static pmOptions opts = { + .short_options = "c:CdD:iqsvw?", + .long_options = longopts, + .short_usage = "[options] input-archive [output-archive]", +}; + +/* + * Global variables + */ +static int first_datarec = 1; /* first record flag */ +static char bak_base[MAXPATHLEN+1]; /* basename for backup with -i */ + +off_t new_log_offset; /* new log offset */ +off_t new_meta_offset; /* new meta offset */ + + +/* archive control stuff */ +inarch_t inarch; /* input archive control */ +outarch_t outarch; /* output archive control */ + +/* command line args */ +int nconf; /* number of config files */ +char **conf; /* list of config files */ +char *configfile; /* current config file */ +int Cflag; /* -C parse config and quit */ +int dflag; /* -d desperate */ +int iflag; /* -i in-place */ +int qflag; /* -q quick or quiet */ +int sflag; /* -s scale values */ +int vflag; /* -v verbosity */ +int wflag; /* -w emit warnings */ + +/* + * 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)here); + if (fstat(fileno(fp), &sbuf) < 0) + fprintf(stderr, ": stat: %s\n", osstrerror()); + else + fprintf(stderr, " %ld bytes.\n", (long)sbuf.st_size); + if (dflag) + fprintf(stderr, "The last record, and the remainder of this file will not be processed.\n"); + abandon(); + /*NOTREACHED*/ +} + +/* + * switch output volumes + */ +void +newvolume(int vol) +{ + FILE *newfp; + + if ((newfp = __pmLogNewFile(outarch.name, vol)) != NULL) { + fclose(outarch.logctl.l_mfp); + outarch.logctl.l_mfp = newfp; + outarch.logctl.l_label.ill_vol = outarch.logctl.l_curvol = vol; + __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label); + fflush(outarch.logctl.l_mfp); + } + else { + fprintf(stderr, "%s: __pmLogNewFile(%s,%d) Error: %s\n", + pmProgname, outarch.name, vol, pmErrStr(-oserror())); + abandon(); + /*NOTREACHED*/ + } +} + +/* construct new archive label */ +static void +newlabel(void) +{ + __pmLogLabel *lp = &outarch.logctl.l_label; + + /* copy magic number, pid, host and timezone */ + lp->ill_magic = inarch.label.ll_magic; + lp->ill_pid = inarch.label.ll_pid; + if (global.flags & GLOBAL_CHANGE_HOSTNAME) + strncpy(lp->ill_hostname, global.hostname, PM_LOG_MAXHOSTLEN); + else + strncpy(lp->ill_hostname, inarch.label.ll_hostname, PM_LOG_MAXHOSTLEN); + lp->ill_hostname[PM_LOG_MAXHOSTLEN-1] = '\0'; + if (global.flags & GLOBAL_CHANGE_TZ) + strncpy(lp->ill_tz, global.tz, PM_TZ_MAXLEN); + else + strncpy(lp->ill_tz, inarch.label.ll_tz, PM_TZ_MAXLEN); + lp->ill_tz[PM_TZ_MAXLEN-1] = '\0'; +} + +/* + * write label records at the start of each physical file + */ +void +writelabel(int do_rewind) +{ + off_t old_offset; + + if (do_rewind) { + old_offset = ftell(outarch.logctl.l_tifp); + assert(old_offset >= 0); + rewind(outarch.logctl.l_tifp); + } + outarch.logctl.l_label.ill_vol = PM_LOG_VOL_TI; + __pmLogWriteLabel(outarch.logctl.l_tifp, &outarch.logctl.l_label); + if (do_rewind) + fseek(outarch.logctl.l_tifp, (long)old_offset, SEEK_SET); + + if (do_rewind) { + old_offset = ftell(outarch.logctl.l_mdfp); + assert(old_offset >= 0); + rewind(outarch.logctl.l_mdfp); + } + outarch.logctl.l_label.ill_vol = PM_LOG_VOL_META; + __pmLogWriteLabel(outarch.logctl.l_mdfp, &outarch.logctl.l_label); + if (do_rewind) + fseek(outarch.logctl.l_mdfp, (long)old_offset, SEEK_SET); + + if (do_rewind) { + old_offset = ftell(outarch.logctl.l_mfp); + assert(old_offset >= 0); + rewind(outarch.logctl.l_mfp); + } + outarch.logctl.l_label.ill_vol = 0; + __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label); + if (do_rewind) + fseek(outarch.logctl.l_mfp, (long)old_offset, SEEK_SET); +} + +/* + * read next metadata record + */ +static int +nextmeta() +{ + int sts; + __pmLogCtl *lcp; + + lcp = inarch.ctxp->c_archctl->ac_log; + if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &inarch.metarec)) < 0) { + if (sts != PM_ERR_EOL) { + fprintf(stderr, "%s: Error: _pmLogGet[meta %s]: %s\n", + pmProgname, inarch.name, pmErrStr(sts)); + _report(lcp->l_mdfp); + } + return -1; + } + + return ntohl(inarch.metarec[1]); +} + + +/* + * read next log record + * + * return status is + * 0 ok + * 1 ok, but volume switched + * PM_ERR_EOL end of file + * -1 fatal error + */ +static int +nextlog(void) +{ + int sts; + __pmLogCtl *lcp; + int old_vol; + + + lcp = inarch.ctxp->c_archctl->ac_log; + old_vol = inarch.ctxp->c_archctl->ac_log->l_curvol; + + if ((sts = __pmLogRead(lcp, PM_MODE_FORW, NULL, &inarch.rp, PMLOGREAD_NEXT)) < 0) { + if (sts != PM_ERR_EOL) { + fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n", + pmProgname, inarch.name, pmErrStr(sts)); + _report(lcp->l_mfp); + } + return -1; + } + + return old_vol == inarch.ctxp->c_archctl->ac_log->l_curvol ? 0 : 1; +} + +#ifdef IS_MINGW +#define S_ISLINK(mode) 0 /* no symlink support */ +#else +#ifndef S_ISLINK +#define S_ISLINK(mode) ((mode & S_IFMT) == S_IFLNK) +#endif +#endif + +/* + * parse command line arguments + */ +int +parseargs(int argc, char *argv[]) +{ + int c; + int sts; + int sep = __pmPathSeparator(); + struct stat sbuf; + + while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'c': /* config file */ + if (stat(opts.optarg, &sbuf) < 0) { + pmprintf("%s: stat(%s) failed: %s\n", + pmProgname, opts.optarg, osstrerror()); + opts.errors++; + break; + } + if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) { + nconf++; + if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) != NULL) + conf[nconf-1] = opts.optarg; + } + else if (S_ISDIR(sbuf.st_mode)) { + DIR *dirp; + struct dirent *dp; + char path[MAXPATHLEN+1]; + + if ((dirp = opendir(opts.optarg)) == NULL) { + pmprintf("%s: opendir(%s) failed: %s\n", pmProgname, opts.optarg, osstrerror()); + opts.errors++; + } + else while ((dp = readdir(dirp)) != NULL) { + /* skip ., .. and "hidden" files */ + if (dp->d_name[0] == '.') continue; + snprintf(path, sizeof(path), "%s%c%s", opts.optarg, sep, dp->d_name); + if (stat(path, &sbuf) < 0) { + pmprintf("%s: %s: %s\n", pmProgname, path, osstrerror()); + opts.errors++; + } + else if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) { + nconf++; + if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) == NULL) + break; + if ((conf[nconf-1] = strdup(path)) == NULL) { + fprintf(stderr, "conf[%d] strdup(%s) failed: %s\n", nconf-1, path, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + } + } + if (dirp != NULL) + closedir(dirp); + } + else { + pmprintf("%s: Error: -c config %s is not a file or directory\n", pmProgname, opts.optarg); + opts.errors++; + } + if (nconf > 0 && conf == NULL) { + fprintf(stderr, "%s: Error: conf[%d] realloc(%d) failed: %s\n", pmProgname, nconf, (int)(nconf*sizeof(conf[0])), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + break; + + case 'C': /* parse configs and quit */ + Cflag = 1; + vflag = 1; + wflag = 1; + break; + + case 'd': /* desperate */ + dflag = 1; + 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 'i': /* in-place, over-write input archive */ + iflag = 1; + break; + + case 'q': /* quick or quiet */ + qflag = 1; + break; + + case 's': /* do scale conversions */ + sflag = 1; + break; + + case 'v': /* verbosity */ + vflag++; + break; + + case 'w': /* print warnings */ + wflag = 1; + break; + + case '?': + default: + opts.errors++; + break; + } + } + + if (opts.errors == 0) { + if ((iflag == 0 && opts.optind != argc-2) || + (iflag == 1 && opts.optind != argc-1)) + opts.errors++; + } + + return -opts.errors; +} + +static void +parseconfig(char *file) +{ + configfile = file; + if ((yyin = fopen(configfile, "r")) == NULL) { + fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + if (vflag > 1) + fprintf(stderr, "Start configfile: %s\n", file); + lineno = 1; + + if (yyparse() != 0) + exit(1); + + fclose(yyin); + yyin = NULL; + + return; +} + +char * +SemStr(int sem) +{ + static char buf[20]; + + if (sem == PM_SEM_COUNTER) snprintf(buf, sizeof(buf), "counter"); + else if (sem == PM_SEM_INSTANT) snprintf(buf, sizeof(buf), "instant"); + else if (sem == PM_SEM_DISCRETE) snprintf(buf, sizeof(buf), "discrete"); + else snprintf(buf, sizeof(buf), "bad sem? %d", sem); + + return buf; +} + +static void +reportconfig(void) +{ + indomspec_t *ip; + metricspec_t *mp; + int i; + int change = 0; + + printf("PCP Archive Log Rewrite Specifications Summary\n"); + change |= (global.flags != 0); + if (global.flags & GLOBAL_CHANGE_HOSTNAME) + printf("Hostname:\t%s -> %s\n", inarch.label.ll_hostname, global.hostname); + if (global.flags & GLOBAL_CHANGE_TZ) + printf("Timezone:\t%s -> %s\n", inarch.label.ll_tz, global.tz); + if (global.flags & GLOBAL_CHANGE_TIME) { + static struct tm *tmp; + char *sign = ""; + time_t time; + if (global.time.tv_sec < 0) { + time = (time_t)(-global.time.tv_sec); + sign = "-"; + } + else + time = (time_t)global.time.tv_sec; + tmp = gmtime(&time); + tmp->tm_hour += 24 * tmp->tm_yday; + if (tmp->tm_hour < 10) + printf("Delta:\t\t-> %s%02d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec); + else + printf("Delta:\t\t-> %s%d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec); + } + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + int hdr_done = 0; + if (ip->new_indom != ip->old_indom) { + printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom)); + hdr_done = 1; + printf("pmInDom:\t-> %s\n", pmInDomStr(ip->new_indom)); + change |= 1; + } + for (i = 0; i < ip->numinst; i++) { + change |= (ip->inst_flags[i] != 0); + if (ip->inst_flags[i]) { + if (hdr_done == 0) { + printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom)); + hdr_done = 1; + } + printf("Instance:\t\[%d] \"%s\" -> ", ip->old_inst[i], ip->old_iname[i]); + if (ip->inst_flags[i] & INST_DELETE) + printf("DELETE\n"); + else { + if (ip->inst_flags[i] & INST_CHANGE_INST) + printf("[%d] ", ip->new_inst[i]); + else + printf("[%d] ", ip->old_inst[i]); + if (ip->inst_flags[i] & INST_CHANGE_INAME) + printf("\"%s\"\n", ip->new_iname[i]); + else + printf("\"%s\"\n", ip->old_iname[i]); + } + } + } + } + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (mp->flags != 0 || mp->ip != NULL) { + change |= 1; + printf("\nMetric: %s (%s)\n", mp->old_name, pmIDStr(mp->old_desc.pmid)); + } + if (mp->flags & METRIC_CHANGE_PMID) { + printf("pmID:\t\t%s ->", pmIDStr(mp->old_desc.pmid)); + printf(" %s\n", pmIDStr(mp->new_desc.pmid)); + } + if (mp->flags & METRIC_CHANGE_NAME) + printf("Name:\t\t%s -> %s\n", mp->old_name, mp->new_name); + if (mp->flags & METRIC_CHANGE_TYPE) { + printf("Type:\t\t%s ->", pmTypeStr(mp->old_desc.type)); + printf(" %s\n", pmTypeStr(mp->new_desc.type)); + } + if (mp->flags & METRIC_CHANGE_INDOM) { + printf("InDom:\t\t%s ->", pmInDomStr(mp->old_desc.indom)); + printf(" %s\n", pmInDomStr(mp->new_desc.indom)); + if (mp->output != OUTPUT_ALL) { + printf("Output:\t\t"); + switch (mp->output) { + case OUTPUT_ONE: + if (mp->old_desc.indom != PM_INDOM_NULL) { + printf("value for instance"); + if (mp->one_inst != PM_IN_NULL) + printf(" %d", mp->one_inst); + if (mp->one_name != NULL) + printf(" \"%s\"", mp->one_name); + putchar('\n'); + } + else + printf("the only value (output instance %d)\n", mp->one_inst); + break; + case OUTPUT_FIRST: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("first value\n"); + else { + if (mp->one_inst != PM_IN_NULL) + printf("first and only value (output instance %d)\n", mp->one_inst); + else + printf("first and only value (output instance \"%s\")\n", mp->one_name); + } + break; + case OUTPUT_LAST: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("last value\n"); + else + printf("last and only value (output instance %d)\n", mp->one_inst); + break; + case OUTPUT_MIN: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("smallest value\n"); + else + printf("smallest and only value (output instance %d)\n", mp->one_inst); + break; + case OUTPUT_MAX: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("largest value\n"); + else + printf("largest and only value (output instance %d)\n", mp->one_inst); + break; + case OUTPUT_SUM: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("sum value (output instance %d)\n", mp->one_inst); + else + printf("sum and only value (output instance %d)\n", mp->one_inst); + break; + case OUTPUT_AVG: + if (mp->old_desc.indom != PM_INDOM_NULL) + printf("average value (output instance %d)\n", mp->one_inst); + else + printf("average and only value (output instance %d)\n", mp->one_inst); + break; + } + } + } + if (mp->ip != NULL) + printf("Inst Changes:\t<- InDom %s\n", pmInDomStr(mp->ip->old_indom)); + if (mp->flags & METRIC_CHANGE_SEM) { + printf("Semantics:\t%s ->", SemStr(mp->old_desc.sem)); + printf(" %s\n", SemStr(mp->new_desc.sem)); + } + if (mp->flags & METRIC_CHANGE_UNITS) { + printf("Units:\t\t%s ->", pmUnitsStr(&mp->old_desc.units)); + printf(" %s", pmUnitsStr(&mp->new_desc.units)); + if (mp->flags & METRIC_RESCALE) + printf(" (rescale)"); + putchar('\n'); + } + if (mp->flags & METRIC_DELETE) + printf("DELETE\n"); + } + if (change == 0) + printf("No changes\n"); +} + +static int +anychange(void) +{ + indomspec_t *ip; + metricspec_t *mp; + int i; + + if (global.flags != 0) + return 1; + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (ip->new_indom != ip->old_indom) + return 1; + for (i = 0; i < ip->numinst; i++) { + if (ip->inst_flags[i]) + return 1; + } + } + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (mp->flags != 0 || mp->ip != NULL) + return 1; + } + + return 0; +} + +static int +fixstamp(struct timeval *tvp) +{ + if (global.flags & GLOBAL_CHANGE_TIME) { + if (global.time.tv_sec > 0) { + tvp->tv_sec += global.time.tv_sec; + tvp->tv_usec += global.time.tv_usec; + if (tvp->tv_usec > 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + return 1; + } + else if (global.time.tv_sec < 0) { + /* parser makes tv_sec < 0 and tv_usec >= 0 */ + tvp->tv_sec += global.time.tv_sec; + tvp->tv_usec -= global.time.tv_usec; + if (tvp->tv_usec < 0) { + tvp->tv_sec--; + tvp->tv_usec += 1000000; + } + return 1; + } + } + return 0; +} + +/* + * Link metricspec_t entries to corresponding indom_t entry if there + * are changes to instance identifiers or instance names (includes + * instance deletion) + */ +static void +link_entries(void) +{ + indomspec_t *ip; + metricspec_t *mp; + __pmHashCtl *hcp; + __pmHashNode *node; + int i; + int change; + + hcp = &inarch.ctxp->c_archctl->ac_log->l_hashpmid; + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + change = 0; + for (i = 0; i < ip->numinst; i++) + change |= (ip->inst_flags[i] != 0); + if (change == 0 && ip->new_indom == ip->old_indom) + continue; + + for (node = __pmHashWalk(hcp, PM_HASH_WALK_START); + node != NULL; + node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) { + mp = start_metric((pmID)(node->key)); + if (mp->old_desc.indom == ip->old_indom) { + if (change) + mp->ip = ip; + if (ip->new_indom != ip->old_indom) { + if (mp->flags & METRIC_CHANGE_INDOM) { + /* indom already changed via metric clause */ + if (mp->new_desc.indom != ip->new_indom) { + char strbuf[80]; + snprintf(strbuf, sizeof(strbuf), "%s", pmInDomStr(mp->new_desc.indom)); + snprintf(mess, sizeof(mess), "Conflicting indom change for metric %s (%s from metric clause, %s from indom clause)", mp->old_name, strbuf, pmInDomStr(ip->new_indom)); + yysemantic(mess); + } + } + else { + mp->flags |= METRIC_CHANGE_INDOM; + mp->new_desc.indom = ip->new_indom; + } + } + } + } + } +} + +static void +check_indoms() +{ + /* + * For each metric, make sure the output instance domain will be in + * the output archive. + * Called after link_entries(), so if an input metric is associated + * with an instance domain that has any instance rewriting, we're OK. + * The case to be checked here is a rewritten metric with an indom + * clause and no associated indomspec_t (so no instance domain changes, + * but the new indom may not match any indom in the archive. + */ + metricspec_t *mp; + indomspec_t *ip; + __pmHashCtl *hcp; + __pmHashNode *node; + + + hcp = &inarch.ctxp->c_archctl->ac_log->l_hashindom; + + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (mp->ip != NULL) + /* associated indom has instance changes, we're OK */ + continue; + if ((mp->flags & METRIC_CHANGE_INDOM) && mp->new_desc.indom != PM_INDOM_NULL) { + for (node = __pmHashWalk(hcp, PM_HASH_WALK_START); + node != NULL; + node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) { + /* + * if this indom has an indomspec_t, check that, else + * this indom will go to the archive without change + */ + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + if (ip->old_indom == mp->old_desc.indom) + break; + } + if (ip == NULL) { + if ((pmInDom)(node->key) == mp->new_desc.indom) + /* we're OK */ + break; + } + else { + if (ip->new_indom != ip->old_indom && + ip->new_indom == mp->new_desc.indom) + /* we're OK */ + break; + } + } + if (node == NULL) { + snprintf(mess, sizeof(mess), "New indom (%s) for metric %s is not in the output archive", pmInDomStr(mp->new_desc.indom), mp->old_name); + yysemantic(mess); + } + } + } + + /* + * For each modified instance domain, make sure instances are + * still unique and instance names are unique to the first + * space. + */ + for (ip = indom_root; ip != NULL; ip = ip->i_next) { + int i; + for (i = 0; i < ip->numinst; i++) { + int insti; + char *namei; + int j; + if (ip->inst_flags[i] & INST_CHANGE_INST) + insti = ip->new_inst[i]; + else + insti = ip->old_inst[i]; + if (ip->inst_flags[i] & INST_CHANGE_INAME) + namei = ip->new_iname[i]; + else + namei = ip->old_iname[i]; + for (j = 0; j < ip->numinst; j++) { + int instj; + char *namej; + if (i == j) + continue; + if (ip->inst_flags[j] & INST_CHANGE_INST) + instj = ip->new_inst[j]; + else + instj = ip->old_inst[j]; + if (ip->inst_flags[j] & INST_CHANGE_INAME) + namej = ip->new_iname[j]; + else + namej = ip->old_iname[j]; + if (insti == instj) { + snprintf(mess, sizeof(mess), "Duplicate instance id %d (\"%s\" and \"%s\") for indom %s", insti, namei, namej, pmInDomStr(ip->old_indom)); + yysemantic(mess); + } + if (inst_name_eq(namei, namej) > 0) { + snprintf(mess, sizeof(mess), "Duplicate instance name \"%s\" (%d) and \"%s\" (%d) for indom %s", namei, insti, namej, instj, pmInDomStr(ip->old_indom)); + yysemantic(mess); + } + } + } + } +} + +static void +check_output() +{ + /* + * For each metric, if there is an INDOM clause, perform some + * additional semantic checks and perhaps a name -> instance id + * mapping. + * + * Note instance renumbering happens _after_ value selction from + * the INDOM -> ,,,, OUTPUT clause, so all references to + * instance names and instance ids are relative to the + * "old" set. + */ + metricspec_t *mp; + indomspec_t *ip; + + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if ((mp->flags & METRIC_CHANGE_INDOM)) { + if (mp->output == OUTPUT_ONE || mp->output == OUTPUT_FIRST) { + /* + * cases here are + * INAME "name" + * => one_name == "name" and one_inst == PM_IN_NULL + * INST id + * => one_name == NULL and one_inst = id + */ + if (mp->old_desc.indom != PM_INDOM_NULL && mp->output == OUTPUT_ONE) { + /* + * old metric is not singular, so one_name and one_inst + * are used to pick the value + * also map one_name -> one_inst + */ + int i; + ip = start_indom(mp->old_desc.indom); + for (i = 0; i < ip->numinst; i++) { + if (mp->one_name != NULL) { + if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) { + mp->one_name = NULL; + mp->one_inst = ip->old_inst[i]; + break; + } + } + else if (ip->old_inst[i] == mp->one_inst) + break; + } + if (i == ip->numinst) { + if (wflag) { + if (mp->one_name != NULL) + snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in old indom %s", mp->one_name, pmInDomStr(mp->old_desc.indom)); + else + snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in old indom %s", mp->one_inst, pmInDomStr(mp->old_desc.indom)); + yywarn(mess); + } + } + } + if (mp->new_desc.indom != PM_INDOM_NULL) { + /* + * new metric is not singular, so one_inst should be + * found in the new instance domain ... ignore one_name + * other than to map one_name -> one_inst if one_inst + * is not already known + */ + int i; + ip = start_indom(mp->new_desc.indom); + for (i = 0; i < ip->numinst; i++) { + if (mp->one_name != NULL) { + if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) { + mp->one_name = NULL; + mp->one_inst = ip->old_inst[i]; + break; + } + } + else if (ip->old_inst[i] == mp->one_inst) + break; + } + if (i == ip->numinst) { + if (wflag) { + if (mp->one_name != NULL) + snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in new indom %s", mp->one_name, pmInDomStr(mp->new_desc.indom)); + else + snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in new indom %s", mp->one_inst, pmInDomStr(mp->new_desc.indom)); + yywarn(mess); + } + } + /* + * use default rule (id 0) if INAME not found and + * and instance id is needed for output value + */ + if (mp->old_desc.indom == PM_INDOM_NULL && mp->one_inst == PM_IN_NULL) + mp->one_inst = 0; + } + } + } + } +} + +int +main(int argc, char **argv) +{ + int sts; + int stslog; /* sts from nextlog() */ + int stsmeta = 0; /* sts from nextmeta() */ + int i; + int ti_idx; /* next slot for input temporal index */ + int dir_fd = -1; /* poinless initialization to humour gcc */ + int needti = 0; + int doneti = 0; + __pmTimeval tstamp = { 0 }; /* for last log record */ + off_t old_log_offset = 0; /* log offset before last log record */ + off_t old_meta_offset; + + /* process cmd line args */ + if (parseargs(argc, argv) < 0) { + pmUsageMessage(&opts); + exit(1); + } + + /* input archive */ + if (iflag == 0) + inarch.name = argv[argc-2]; + else + inarch.name = argv[argc-1]; + inarch.logrec = inarch.metarec = NULL; + inarch.mark = 0; + inarch.rp = NULL; + + if ((inarch.ctx = pmNewContext(PM_CONTEXT_ARCHIVE, inarch.name)) < 0) { + fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n", + pmProgname, inarch.name, pmErrStr(inarch.ctx)); + exit(1); + } + inarch.ctxp = __pmHandleToPtr(inarch.ctx); + assert(inarch.ctxp != NULL); + + if ((sts = pmGetArchiveLabel(&inarch.label)) < 0) { + fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n", + pmProgname, inarch.name, pmErrStr(sts)); + exit(1); + } + + if ((inarch.label.ll_magic & 0xff) != PM_LOG_VERS02) { + fprintf(stderr,"%s: Error: illegal version number %d in archive (%s)\n", + pmProgname, inarch.label.ll_magic & 0xff, inarch.name); + exit(1); + } + + /* output archive */ + if (iflag && Cflag == 0) { + /* + * -i (in place) method outline + * + * + create one temporary base filename in the same directory is + * the input archive, keep a copy of this name this accessed + * via outarch.name + * + create a second (and different) temporary base file name + * in the same directory, keep this name in bak_base[] + * + close the temporary file descriptors and unlink the basename + * files + * + create the output as per normal in outarch.name + * + fsync() all the output files and the container directory + * + rename the _input_ archive files using the _second_ temporary + * basename + * + rename the output archive files to the basename of the input + * archive ... if this step fails for any reason, restore the + * original input files + * + unlink all the (old) input archive files + */ + char path[MAXPATHLEN+1]; + char dname[MAXPATHLEN+1]; + mode_t cur_umask; + int tmp_f1; /* fd for first temp basename */ + int tmp_f2; /* fd for second temp basename */ + +#if HAVE_MKSTEMP + strncpy(path, argv[argc-1], sizeof(path)); + path[sizeof(path)-1] = '\0'; + strncpy(dname, dirname(path), sizeof(dname)); + dname[sizeof(dname)-1] = '\0'; + if ((dir_fd = open(dname, O_RDONLY)) < 0) { + fprintf(stderr, "%s: Error: cannot open directory \"%s\" for reading: %s\n", pmProgname, dname, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + sprintf(path, "%s%cXXXXXX", dname, __pmPathSeparator()); + cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO); + tmp_f1 = mkstemp(path); + umask(cur_umask); + outarch.name = strdup(path); + if (outarch.name == NULL) { + fprintf(stderr, "%s: Error: temp file strdup(%s) failed: %s\n", pmProgname, path, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + sprintf(bak_base, "%s%cXXXXXX", dname, __pmPathSeparator()); + cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO); + tmp_f2 = mkstemp(bak_base); + umask(cur_umask); +#else + char fname[MAXPATHLEN+1]; + char *s; + + strncpy(path, argv[argc-1], sizeof(path)); + path[sizeof(path)-1] = '\0'; + strncpy(fname, basename(path), sizeof(fname)); + fname[sizeof(fname)-1] = '\0'; + strncpy(dname, dirname(path), sizeof(dname)); + dname[sizeof(dname)-1] = '\0'; + + if ((s = tempnam(dname, fname)) == NULL) { + fprintf(stderr, "%s: Error: first tempnam() failed: %s\n", pmProgname, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + else { + outarch.name = strdup(s); + if (outarch.name == NULL) { + fprintf(stderr, "%s: Error: temp file strdup(%s) failed: %s\n", pmProgname, s, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO); + tmp_f1 = open(outarch.name, O_WRONLY|O_CREAT|O_EXCL, 0600); + umask(cur_umask); + } + if ((s = tempnam(dname, fname)) == NULL) { + fprintf(stderr, "%s: Error: second tempnam() failed: %s\n", pmProgname, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + else { + strcpy(bak_base, s); + cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO); + tmp_f2 = open(bak_base, O_WRONLY|O_CREAT|O_EXCL, 0600); + umask(cur_umask); + } +#endif + if (tmp_f1 < 0) { + fprintf(stderr, "%s: Error: create first temp (%s) failed: %s\n", pmProgname, outarch.name, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + if (tmp_f2 < 0) { + fprintf(stderr, "%s: Error: create second temp (%s) failed: %s\n", pmProgname, bak_base, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + close(tmp_f1); + close(tmp_f2); + unlink(outarch.name); + unlink(bak_base); + } + else + outarch.name = argv[argc-1]; + + /* + * process config file(s) + */ + for (i = 0; i < nconf; i++) { + parseconfig(conf[i]); + } + + /* + * cross-specification dependencies and semantic checks once all + * config files have been processed + */ + link_entries(); + check_indoms(); + check_output(); + + if (vflag) + reportconfig(); + + if (Cflag) + exit(0); + + if (qflag && anychange() == 0) + exit(0); + + /* create output log - must be done before writing label */ + if ((sts = __pmLogCreate("", outarch.name, PM_LOG_VERS02, &outarch.logctl)) < 0) { + fprintf(stderr, "%s: Error: __pmLogCreate(%s): %s\n", + pmProgname, outarch.name, pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } + + /* initialize and write label records */ + newlabel(); + outarch.logctl.l_state = PM_LOG_STATE_INIT; + writelabel(0); + + first_datarec = 1; + ti_idx = 0; + + /* + * loop + * - get next log record + * - write out new/changed meta data required by this log record + * - write out log + * - do ti update if necessary + */ + while (1) { + static long in_offset; /* for -Dappl0 */ + + fflush(outarch.logctl.l_mdfp); + old_meta_offset = ftell(outarch.logctl.l_mdfp); + assert(old_meta_offset >= 0); + + in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mfp); + stslog = nextlog(); + if (stslog < 0) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Log: read EOF @ offset=%ld\n", in_offset); +#endif + break; + } + if (stslog == 1) { + /* volume change */ + if (inarch.ctxp->c_archctl->ac_log->l_curvol >= outarch.logctl.l_curvol+1) + /* track input volume numbering */ + newvolume(inarch.ctxp->c_archctl->ac_log->l_curvol); + else + /* + * output archive volume number is ahead, probably because + * rewriting has forced an earlier volume change + */ + newvolume(outarch.logctl.l_curvol+1); + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + struct timeval stamp; + fprintf(stderr, "Log: read "); + stamp.tv_sec = inarch.rp->timestamp.tv_sec; + stamp.tv_usec = inarch.rp->timestamp.tv_usec; + __pmPrintStamp(stderr, &stamp); + fprintf(stderr, " numpmid=%d @ offset=%ld\n", inarch.rp->numpmid, in_offset); + } +#endif + + if (ti_idx < inarch.ctxp->c_archctl->ac_log->l_numti) { + __pmLogTI *tip = &inarch.ctxp->c_archctl->ac_log->l_ti[ti_idx]; + if (tip->ti_stamp.tv_sec == inarch.rp->timestamp.tv_sec && + tip->ti_stamp.tv_usec == inarch.rp->timestamp.tv_usec) { + /* + * timestamp on input pmResult matches next temporal index + * entry for input archive ... make sure matching temporal + * index entry added to output archive + */ + needti = 1; + ti_idx++; + } + } + + /* + * optionally rewrite timestamp in pmResult for global time + * adjustment ... flows to output pmResult, indom entries in + * metadata, temporal index entries and label records + * */ + fixstamp(&inarch.rp->timestamp); + + /* + * process metadata until find an indom record with timestamp + * after the current log record, or a metric record for a pmid + * that is not in the current log record + */ + for ( ; ; ) { + pmID pmid; /* pmid for TYPE_DESC */ + pmInDom indom; /* indom for TYPE_INDOM */ + + if (stsmeta == 0) { + in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mdfp); + stsmeta = nextmeta(); +#if PCP_DEBUG + if (stsmeta < 0 && pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Metadata: read EOF @ offset=%ld\n", in_offset); +#endif + } + if (stsmeta < 0) { + break; + } + if (stsmeta == TYPE_DESC) { + int i; + pmid = ntoh_pmID(inarch.metarec[2]); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Metadata: read PMID %s @ offset=%ld\n", pmIDStr(pmid), in_offset); +#endif + /* + * if pmid not in next pmResult, we're done ... + */ + for (i = 0; i < inarch.rp->numpmid; i++) { + if (pmid == inarch.rp->vset[i]->pmid) + break; + } + if (i == inarch.rp->numpmid) + break; + /* + * rewrite if needed, delete if needed else output + */ + do_desc(); + } + else if (stsmeta == TYPE_INDOM) { + struct timeval stamp; + __pmTimeval *tvp = (__pmTimeval *)&inarch.metarec[2]; + indom = ntoh_pmInDom((unsigned int)inarch.metarec[4]); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Metadata: read InDom %s @ offset=%ld\n", pmInDomStr(indom), in_offset); +#endif + stamp.tv_sec = ntohl(tvp->tv_sec); + stamp.tv_usec = ntohl(tvp->tv_usec); + if (fixstamp(&stamp)) { + /* global time adjustment specified */ + tvp->tv_sec = htonl(stamp.tv_sec); + tvp->tv_usec = htonl(stamp.tv_usec); + } + /* if time of indom > next pmResult stop processing metadata */ + if (stamp.tv_sec > inarch.rp->timestamp.tv_sec) + break; + if (stamp.tv_sec == inarch.rp->timestamp.tv_sec && + stamp.tv_usec > inarch.rp->timestamp.tv_usec) + break; + needti = 1; + do_indom(); + } + else { + fprintf(stderr, "%s: Error: unrecognised meta data type: %d\n", + pmProgname, stsmeta); + abandon(); + /*NOTREACHED*/ + } + free(inarch.metarec); + stsmeta = 0; + } + + if (first_datarec) { + first_datarec = 0; + /* any global time adjustment done after nextlog() above */ + outarch.logctl.l_label.ill_start.tv_sec = inarch.rp->timestamp.tv_sec; + outarch.logctl.l_label.ill_start.tv_usec = inarch.rp->timestamp.tv_usec; + /* need to fix start-time in label records */ + writelabel(1); + needti = 1; + } + + tstamp.tv_sec = inarch.rp->timestamp.tv_sec; + tstamp.tv_usec = inarch.rp->timestamp.tv_usec; + + if (needti) { + fflush(outarch.logctl.l_mdfp); + fflush(outarch.logctl.l_mfp); + new_meta_offset = ftell(outarch.logctl.l_mdfp); + assert(new_meta_offset >= 0); + fseek(outarch.logctl.l_mdfp, (long)old_meta_offset, SEEK_SET); + __pmLogPutIndex(&outarch.logctl, &tstamp); + fseek(outarch.logctl.l_mdfp, (long)new_meta_offset, SEEK_SET); + needti = 0; + doneti = 1; + } + else + doneti = 0; + + old_log_offset = ftell(outarch.logctl.l_mfp); + assert(old_log_offset >= 0); + + if (inarch.rp->numpmid == 0) + /* mark record, need index entry @ next log record */ + needti = 1; + + do_result(); + } + + if (!doneti) { + /* Final temporal index entry */ + fflush(outarch.logctl.l_mfp); + fseek(outarch.logctl.l_mfp, (long)old_log_offset, SEEK_SET); + __pmLogPutIndex(&outarch.logctl, &tstamp); + } + + if (iflag) { + /* + * fsync() to make sure new archive is safe before we start + * renaming ... + */ + if (fsync(fileno(outarch.logctl.l_mdfp)) < 0) { + fprintf(stderr, "%s: Error: fsync(%d) failed for output metadata file: %s\n", + pmProgname, fileno(outarch.logctl.l_mdfp), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + if (fsync(fileno(outarch.logctl.l_mfp)) < 0) { + fprintf(stderr, "%s: Error: fsync(%d) failed for output data file: %s\n", + pmProgname, fileno(outarch.logctl.l_mfp), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + if (fsync(fileno(outarch.logctl.l_tifp)) < 0) { + fprintf(stderr, "%s: Error: fsync(%d) failed for output index file: %s\n", + pmProgname, fileno(outarch.logctl.l_tifp), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + if (fsync(dir_fd) < 0) { + fprintf(stderr, "%s: Error: fsync(%d) failed for output directory: %s\n", + pmProgname, dir_fd, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + close(dir_fd); + if (_pmLogRename(inarch.name, bak_base) < 0) { + abandon(); + /*NOTREACHED*/ + } + if (_pmLogRename(outarch.name, inarch.name) < 0) { + abandon(); + /*NOTREACHED*/ + } + _pmLogRemove(bak_base); + } + + exit(0); +} + +void +abandon(void) +{ + char path[MAXNAMELEN+1]; + if (dflag == 0) { + if (Cflag == 0 && iflag == 0) + fprintf(stderr, "Archive \"%s\" not created.\n", outarch.name); + + _pmLogRemove(outarch.name); + if (iflag) + _pmLogRename(bak_base, inarch.name); + while (outarch.logctl.l_curvol >= 0) { + snprintf(path, sizeof(path), "%s.%d", outarch.name, outarch.logctl.l_curvol); + unlink(path); + outarch.logctl.l_curvol--; + } + snprintf(path, sizeof(path), "%s.meta", outarch.name); + unlink(path); + snprintf(path, sizeof(path), "%s.index", outarch.name); + unlink(path); + } + else + fprintf(stderr, "Archive \"%s\" creation truncated.\n", outarch.name); + + exit(1); +} diff --git a/src/pmlogrewrite/result.c b/src/pmlogrewrite/result.c new file mode 100644 index 0000000..715b9d5 --- /dev/null +++ b/src/pmlogrewrite/result.c @@ -0,0 +1,730 @@ +/* + * pmResult rewrite methods for pmlogrewrite + * + * Copyright (c) 2011 Ken McDonell. 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. + * + * pmResult rewriting is complicated because ... + * + * - deleting a metric involves moving all of the following rp->vset[] + * entries "up" one position and decrementing rp->numpmid + * - deleting an instance involves moving all of the following + * rp->vset[i]->vlist[] enties "up" one position and then decrementing + * rp->vset[i]->numval + * - rescaling values involves calling __pmStuffValue which modifies + * rp->vset[i]->vlist[j].value, and in the case of all types other than + * U32 or 32 this will involve an allocation for a new pmValueBlock + * ... we need to keep track of the previous pmValueBlock (if any) to + * avoid memory leaks + * - changing type has the same implications as rescaling + * - a single metric within a pmResult may have both rescaling and type + * change + * - the initial pmResult contains pointers into a PDU buffer so the fast + * track case in pmFreeresult() may not release any of the pmValueSet + * or pmValue or pmValueBlock allocations if pmFreeResult is given a + * rewritten pmResult, so we modify the pmResult in place but use save[] + * to remember the original pmValueSet when retyping or rescaling, use + * orig_numval[] to remember how many pmValue instances we had had for + * each pmValueSet, and use orig_numpmid to remember the original numpmid + * value + */ + +#include "pmapi.h" +#include "impl.h" +#include "logger.h" +#include <assert.h> + +/* + * Keep track of pmValueSets that have been moved aside to allow for + * new values from __pmStuffValue() during rewriting. + */ +static pmValueSet **save = NULL; +static int len_save = 0; + +/* + * Save rp->vset[idx] in save[idx], and build a new rp->vset[idx] + * for the number of values expected for this metric + */ +static int +save_vset(pmResult *rp, int idx) +{ + pmValueSet *vsp; + int need; + int j; + + if (save[idx] != NULL) + /* already done */ + return 1; + + vsp = save[idx] = rp->vset[idx]; + + if (vsp->numval > 0) + need = sizeof(pmValueSet) + (vsp->numval-1)*sizeof(pmValue); + else + need = sizeof(pmValueSet); + rp->vset[idx] = (pmValueSet *)malloc(need); + if (rp->vset[idx] == NULL) { + fprintf(stderr, "save_vset: malloc(%d) failed: %s\n", need, strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + rp->vset[idx]->pmid = vsp->pmid; + rp->vset[idx]->numval = vsp->numval; + rp->vset[idx]->valfmt = vsp->valfmt; + for (j = 0; j < vsp->numval; j++) + rp->vset[idx]->vlist[j].inst = vsp->vlist[j].inst; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "save_vset: vset[%d] -> " PRINTF_P_PFX "%p (was " PRINTF_P_PFX "%p) pmid=%s numval=%d\n", + idx, rp->vset[idx], save[idx], pmIDStr(rp->vset[idx]->pmid), rp->vset[idx]->numval); + } +#endif + return 0; +} + +/* + * free the pval for the jth instance of the ith metric + */ +static void +free_pval(pmResult *rp, int i, int j) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "free_pval: free(" PRINTF_P_PFX "%p) pmid=%s inst=%d\n", + rp->vset[i]->vlist[j].value.pval, pmIDStr(rp->vset[i]->pmid), rp->vset[i]->vlist[j].inst); + } +#endif + free(rp->vset[i]->vlist[j].value.pval); +} + +/* + * if a pmValueSet was saved via save_vset(), then free the newly build + * pmValueSet and put the old one back in place + */ +static void +clean_vset(pmResult *rp) +{ + int i; + int j; + + for (i = 0; i < rp->numpmid; i++) { + if (save[i] != NULL) { + if (rp->vset[i]->valfmt == PM_VAL_DPTR) { + /* values hanging off the pval */ + for (j = 0; j < rp->vset[i]->numval; j++) + free_pval(rp, i, j); + } + /* + * we did the vset[i] allocation in save_vset(), so malloc() + */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "clean_vset: free(" PRINTF_P_PFX "%p) pmValueSet pmid=%s\n", + rp->vset[i], pmIDStr(rp->vset[i]->pmid)); + } +#endif + free(rp->vset[i]); + rp->vset[i] = save[i]; + save[i] = NULL; + } + } +} + +/* + * pick/calculate one value from multiple values for the ith vset[] ... + * + * Note: pval->vbuf may not have correct alignment for all data types, + * so memcpy() rather than assign. + */ +static int +pick_val(int i, metricspec_t *mp) +{ + int j; + int pick = -1; + pmAtomValue jval; + pmAtomValue pickval; + + assert(inarch.rp->vset[i]->numval > 0); + + for (j = 0; j < inarch.rp->vset[i]->numval; j++) { + if (mp->output == OUTPUT_ONE) { + if (inarch.rp->vset[i]->vlist[j].inst == mp->one_inst) { + pick = j; + break; + } + continue; + } + if (j == 0) { + pick = 0; + switch (mp->old_desc.type) { + case PM_TYPE_64: + memcpy(&pickval.ll, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(__int64_t)); + break; + case PM_TYPE_U64: + memcpy(&pickval.ull, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(__uint64_t)); + break; + case PM_TYPE_FLOAT: + memcpy(&pickval.f, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(float)); + break; + case PM_TYPE_DOUBLE: + memcpy(&pickval.d, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(double)); + break; + } + if (mp->output == OUTPUT_MIN || mp->output == OUTPUT_MAX || + mp->output == OUTPUT_SUM || mp->output == OUTPUT_AVG) + continue; + } + switch (mp->old_desc.type) { + case PM_TYPE_32: + switch (mp->output) { + case OUTPUT_MIN: + if (inarch.rp->vset[i]->vlist[j].value.lval < inarch.rp->vset[i]->vlist[pick].value.lval) + pick = j; + break; + case OUTPUT_MAX: + if (inarch.rp->vset[i]->vlist[j].value.lval > inarch.rp->vset[i]->vlist[pick].value.lval) + pick = j; + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + inarch.rp->vset[i]->vlist[0].value.lval += inarch.rp->vset[i]->vlist[j].value.lval; + break; + } + break; + case PM_TYPE_U32: + switch (mp->output) { + case OUTPUT_MIN: + if ((__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval < (__uint32_t)inarch.rp->vset[i]->vlist[pick].value.lval) + pick = j; + break; + case OUTPUT_MAX: + if ((__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval > (__uint32_t)inarch.rp->vset[i]->vlist[pick].value.lval) + pick = j; + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval += (__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval; + break; + } + break; + case PM_TYPE_64: + memcpy(&jval.ll, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(__int64_t)); + switch (mp->output) { + case OUTPUT_MIN: + if (jval.ll < pickval.ll) { + pickval.ll = jval.ll; + pick = j; + } + break; + case OUTPUT_MAX: + if (jval.ll > pickval.ll) { + pickval.ll = jval.ll; + pick = j; + } + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + pickval.ll += jval.ll; + break; + } + break; + case PM_TYPE_U64: + memcpy(&jval.ull, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(__int64_t)); + switch (mp->output) { + case OUTPUT_MIN: + if (jval.ull < pickval.ull) { + pickval.ull = jval.ull; + pick = j; + } + break; + case OUTPUT_MAX: + if (jval.ull > pickval.ull) { + pickval.ull = jval.ull; + pick = j; + } + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + pickval.ull += jval.ull; + break; + } + break; + case PM_TYPE_FLOAT: + memcpy(&jval.f, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(float)); + switch (mp->output) { + case OUTPUT_MIN: + if (jval.f < pickval.f) { + pickval.f = jval.f; + pick = j; + } + break; + case OUTPUT_MAX: + if (jval.f > pickval.f) { + pickval.f = jval.f; + pick = j; + } + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + pickval.f += jval.f; + break; + } + break; + case PM_TYPE_DOUBLE: + memcpy(&jval.d, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(double)); + switch (mp->output) { + case OUTPUT_MIN: + if (jval.d < pickval.d) { + pickval.d = jval.d; + pick = j; + } + break; + case OUTPUT_MAX: + if (jval.d > pickval.d) { + pickval.d = jval.d; + pick = j; + } + break; + case OUTPUT_SUM: + case OUTPUT_AVG: + pickval.d += jval.d; + break; + } + break; + } + } + + if (mp->output == OUTPUT_AVG) { + switch (mp->old_desc.type) { + case PM_TYPE_32: + inarch.rp->vset[i]->vlist[0].value.lval = (int)(0.5 + inarch.rp->vset[i]->vlist[0].value.lval / (double)inarch.rp->vset[i]->numval); + break; + case PM_TYPE_U32: + *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval = (__uint32_t)(0.5 + *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval / (double)inarch.rp->vset[i]->numval); + break; + case PM_TYPE_64: + pickval.ll = 0.5 + pickval.ll / (double)inarch.rp->vset[i]->numval; + break; + case PM_TYPE_U64: + pickval.ull = 0.5 + pickval.ull / (double)inarch.rp->vset[i]->numval; + break; + case PM_TYPE_FLOAT: + pickval.f = pickval.f / (float)inarch.rp->vset[i]->numval; + break; + case PM_TYPE_DOUBLE: + pickval.d = pickval.d / (double)inarch.rp->vset[i]->numval; + break; + } + } + if (mp->output == OUTPUT_AVG || mp->output == OUTPUT_SUM) { + switch (mp->old_desc.type) { + case PM_TYPE_64: + memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.ll, sizeof(__int64_t)); + break; + case PM_TYPE_U64: + memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.ull, sizeof(__uint64_t)); + break; + case PM_TYPE_FLOAT: + memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.f, sizeof(float)); + break; + case PM_TYPE_DOUBLE: + memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.d, sizeof(double)); + break; + } + } + + return pick; +} + +/* + * rescale values for the ith vset[] + */ +static void +rescale(int i, metricspec_t *mp) +{ + int sts; + int j; + pmAtomValue ival; + pmAtomValue oval; + int old_valfmt = inarch.rp->vset[i]->valfmt; + int already_saved; + pmValueSet *vsp; + + sts = old_valfmt; + already_saved = save_vset(inarch.rp, i); + if (already_saved) + vsp = inarch.rp->vset[i]; + else + vsp = save[i]; + for (j = 0; j < inarch.rp->vset[i]->numval; j++) { + sts = pmExtractValue(old_valfmt, &vsp->vlist[j], mp->old_desc.type, &ival, mp->old_desc.type); + if (sts < 0) { + /* + * No type conversion here, so error not expected + */ + fprintf(stderr, "%s: Botch: %s (%s): extracting value: %s\n", + pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmErrStr(sts)); + inarch.rp->vset[i]->numval = j; + __pmDumpResult(stderr, inarch.rp); + abandon(); + /*NOTREACHED*/ + } + sts = pmConvScale(mp->old_desc.type, &ival, &mp->old_desc.units, &oval, &mp->new_desc.units); + if (sts < 0) { + /* + * unless the "units" are bad (and the parser is supposed to + * make sure this does not happen) we do not expect errors + * from pmConvScale() + */ + fprintf(stderr, "%s: Botch: %s (%s): scale conversion from %s", + pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmUnitsStr(&mp->old_desc.units)); + fprintf(stderr, " to %s failed: %s\n", pmUnitsStr(&mp->new_desc.units), pmErrStr(sts)); + inarch.rp->vset[i]->numval = j; + __pmDumpResult(stderr, inarch.rp); + abandon(); + /*NOTREACHED*/ + } + if (already_saved && old_valfmt == PM_VAL_DPTR) { + /* + * current value uses pval that is from a previous call to + * __pmStuffValue() during rewriting, not a pointer into a + * PDU buffer + */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "rescale free(" PRINTF_P_PFX "%p) pval pmid=%s inst=%d\n", + inarch.rp->vset[i]->vlist[j].value.pval, + pmIDStr(inarch.rp->vset[i]->pmid), + inarch.rp->vset[i]->vlist[j].inst); + } +#endif + free(inarch.rp->vset[i]->vlist[j].value.pval); + } + sts = __pmStuffValue(&oval, &inarch.rp->vset[i]->vlist[j], mp->old_desc.type); + if (sts < 0) { + /* + * unless "type" is bad (which the parser is supposed to + * prevent) or malloc() failed, we do not expect errors from + * __pmStuffValue() + */ + fprintf(stderr, "%s: Botch: %s (%s): stuffing value %s (type=%s) into rewritten pmResult: %s\n", + pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmAtomStr(&oval, mp->old_desc.type), pmTypeStr(mp->old_desc.type), pmErrStr(sts)); + inarch.rp->vset[i]->numval = j; + __pmDumpResult(stderr, inarch.rp); + abandon(); + /*NOTREACHED*/ + } + } + inarch.rp->vset[i]->valfmt = sts; +} + +/* + * change type of values for the ith vset[] ... real chance that this will + * fail, as some failure modes depend on the sign or size of the data + * values found in the pmResult + */ +static void +retype(int i, metricspec_t *mp) +{ + int sts; + int j; + pmAtomValue val; + int old_valfmt = inarch.rp->vset[i]->valfmt; + int already_saved; + pmValueSet *vsp; + + sts = old_valfmt; + already_saved = save_vset(inarch.rp, i); + if (already_saved) + vsp = inarch.rp->vset[i]; + else + vsp = save[i]; + for (j = 0; j < inarch.rp->vset[i]->numval; j++) { + sts = pmExtractValue(old_valfmt, &vsp->vlist[j], mp->old_desc.type, &val, mp->new_desc.type); + if (sts < 0) { + fprintf(stderr, "%s: Error: %s (%s): extracting value from type %s", + pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmTypeStr(mp->old_desc.type)); + fprintf(stderr, " to %s: %s\n", pmTypeStr(mp->new_desc.type), pmErrStr(sts)); + inarch.rp->vset[i]->numval = j; + __pmDumpResult(stderr, inarch.rp); + abandon(); + /*NOTREACHED*/ + } + if (already_saved && old_valfmt == PM_VAL_DPTR) { + /* + * current value uses pval that is from a previous call to + * __pmStuffValue() during rewriting, not a pointer into a + * PDU buffer + */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "retype free(" PRINTF_P_PFX "%p) pval pmid=%s inst=%d\n", + inarch.rp->vset[i]->vlist[j].value.pval, + pmIDStr(inarch.rp->vset[i]->pmid), + inarch.rp->vset[i]->vlist[j].inst); + } +#endif + free(inarch.rp->vset[i]->vlist[j].value.pval); + } + sts = __pmStuffValue(&val, &inarch.rp->vset[i]->vlist[j], mp->new_desc.type); + if (sts < 0) { + /* + * unless "type" is bad (which the parser is supposed to + * prevent) or malloc() failed, we do not expect errors from + * __pmStuffValue() + */ + fprintf(stderr, "%s: Botch: %s (%s): stuffing value %s (type=%s) into rewritten pmResult: %s\n", + pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmAtomStr(&val, mp->new_desc.type), pmTypeStr(mp->new_desc.type), pmErrStr(sts)); + inarch.rp->vset[i]->numval = j; + __pmDumpResult(stderr, inarch.rp); + abandon(); + /*NOTREACHED*/ + } + } + inarch.rp->vset[i]->valfmt = sts; +} + +void +do_result(void) +{ + metricspec_t *mp; + int i; + int j; + int sts; + int orig_numpmid; + int *orig_numval = NULL; + + orig_numpmid = inarch.rp->numpmid; + + if (inarch.rp->numpmid > len_save) { + /* expand save[] */ + save = (pmValueSet **)realloc(save, inarch.rp->numpmid * sizeof(save[0])); + if (save == NULL) { + fprintf(stderr, "save_vset: save realloc(...,%d) failed: %s\n", (int)(inarch.rp->numpmid * sizeof(save[0])), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + for (i = len_save; i < inarch.rp->numpmid; i++) + save[i] = NULL; + len_save = inarch.rp->numpmid; + } + orig_numval = (int *)malloc(orig_numpmid * sizeof(int)); + if (orig_numval == NULL) { + fprintf(stderr, "orig_numval malloc(%d) failed: %s\n", (int)(orig_numpmid * sizeof(int)), strerror(errno)); + abandon(); + /*NOTREACHED*/ + } + for (i = 0; i < orig_numpmid; i++) + orig_numval[i] = inarch.rp->vset[i]->numval; + + for (i = 0; i < inarch.rp->numpmid; i++) { + for (mp = metric_root; mp != NULL; mp = mp->m_next) { + if (inarch.rp->vset[i]->pmid != mp->old_desc.pmid) + continue; + if (mp->flags == 0 && mp->ip == NULL) + break; + if (mp->flags & METRIC_DELETE) { + /* move vset[i] to end of list, shuffle lower ones up */ + pmValueSet *vsp = inarch.rp->vset[i]; + pmValueSet *save_vsp = save[i]; + int save_numval; + save_numval = orig_numval[i]; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Delete: vset[%d] for %s\n", i, pmIDStr(inarch.rp->vset[i]->pmid)); +#endif + for (j = i+1; j < inarch.rp->numpmid; j++) { + inarch.rp->vset[j-1] = inarch.rp->vset[j]; + save[j-1] = save[j]; + orig_numval[j-1] = orig_numval[j]; + } + inarch.rp->vset[j-1] = vsp; + save[j-1] = save_vsp; + orig_numval[j-1] = save_numval; + /* one less metric to write out, process vset[i] again */ + inarch.rp->numpmid--; + i--; + break; + } +#if PCP_DEBUG + /* + * mflags that will not force any pmResult rewrite ... + * METRIC_CHANGE_NAME + * METRIC_CHANGE_SEM + */ + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Rewrite: vset[%d] for %s\n", i, pmIDStr(inarch.rp->vset[i]->pmid)); + +#endif + if (mp->flags & METRIC_CHANGE_PMID) + inarch.rp->vset[i]->pmid = mp->new_desc.pmid; + if ((mp->flags & METRIC_CHANGE_INDOM) && inarch.rp->vset[i]->numval > 0) { + if (mp->output != OUTPUT_ALL) { + /* + * Output only one value ... + * Some instance selection to be done for the following + * indom cases: + * non-NULL -> NULL + * pick one input value, singular output + * NULL -> non-NULL + * only one input value, magic-up instance id for + * output value from mp->one_inst + * non-NULL -> non-NULL + * pick one input value base in mp->one_inst, + * copy to output + */ + int pick = 0; + switch (mp->output) { + case OUTPUT_FIRST: + break; + case OUTPUT_LAST: + pick = inarch.rp->vset[i]->numval-1; + break; + case OUTPUT_ONE: + case OUTPUT_MIN: + case OUTPUT_MAX: + case OUTPUT_SUM: + case OUTPUT_AVG: + pick = pick_val(i, mp); + break; + } + if (pick >= 0) { + if (pick > 0) { + /* swap vlist[0] and vlist[pick] */ + pmValue save; + save = inarch.rp->vset[i]->vlist[0]; + inarch.rp->vset[i]->vlist[0] = inarch.rp->vset[i]->vlist[pick]; + inarch.rp->vset[i]->vlist[0].inst = inarch.rp->vset[i]->vlist[pick].inst; + inarch.rp->vset[i]->vlist[pick] = save; + } + if (mp->new_desc.indom == PM_INDOM_NULL) + inarch.rp->vset[i]->vlist[0].inst = PM_IN_NULL; + else if (mp->old_desc.indom == PM_INDOM_NULL) + inarch.rp->vset[i]->vlist[0].inst = mp->one_inst; + inarch.rp->vset[i]->numval = 1; + } + else + inarch.rp->vset[i]->numval = 0; + } + } + /* + * order below is deliberate ... + * - cull/renumber instances if needed + * - rescale if needed + * - fix type if needed + */ + if (mp->ip != NULL) { + /* rewrite/delete instance ids from the indom map */ + int k; + for (k = 0; k < mp->ip->numinst; k++) { + if (mp->ip->inst_flags[k] & INST_CHANGE_INST) { + for (j = 0; j < inarch.rp->vset[i]->numval; j++) { + if (inarch.rp->vset[i]->vlist[j].inst == mp->ip->old_inst[k]) { + inarch.rp->vset[i]->vlist[j].inst = mp->ip->new_inst[k]; + } + } + } + if (mp->ip->inst_flags[k] & INST_DELETE) { + for (j = 0; j < inarch.rp->vset[i]->numval; j++) { + if (inarch.rp->vset[i]->vlist[j].inst == mp->ip->old_inst[k]) { + j++; + while (j < inarch.rp->vset[i]->numval) { + inarch.rp->vset[i]->vlist[j-1] = inarch.rp->vset[i]->vlist[j]; + j++; + } + if (save[i] != NULL && + inarch.rp->vset[i]->valfmt == PM_VAL_DPTR) { + /* + * messy case ... last instance pval is + * from calling __pmStuffValue() in + * rewriting not a pointer into the PDU + * buffer, so free here because + * clean_vset() won't find it + */ + free_pval(inarch.rp, i, j-1); + } + inarch.rp->vset[i]->numval--; + } + } + } + } + } + if (mp->flags & METRIC_RESCALE) { + /* + * parser already checked that dimension is unchanged, + * scale is different and -s on command line or RESCALE + * in UNITS clause of metricspec => rescale values + */ + rescale(i, mp); + } + if (mp->flags & METRIC_CHANGE_TYPE) + retype(i, mp); + break; + } + } + + /* + * only output numpmid == 0 case if input was a mark record + */ + if (orig_numpmid == 0 || inarch.rp->numpmid > 0) { + unsigned long out_offset; + unsigned long peek_offset; + peek_offset = ftell(outarch.logctl.l_mfp); + sts = __pmEncodeResult(PDU_OVERRIDE2, inarch.rp, &inarch.logrec); + if (sts < 0) { + fprintf(stderr, "%s: Error: __pmEncodeResult: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } + peek_offset += ((__pmPDUHdr *)inarch.logrec)->len - sizeof(__pmPDUHdr) + 2*sizeof(int); + if (peek_offset > 0x7fffffff) { + /* + * data file size will exceed 2^31-1 bytes, so force + * volume switch + */ + newvolume(outarch.logctl.l_curvol+1); + } + out_offset = ftell(outarch.logctl.l_mfp); + if ((sts = __pmLogPutResult2(&outarch.logctl, inarch.logrec)) < 0) { + fprintf(stderr, "%s: Error: __pmLogPutResult2: log data: %s\n", + pmProgname, pmErrStr(sts)); + abandon(); + /*NOTREACHED*/ + } + /* do not free inarch.logrec ... this is a libpcp PDU buffer */ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + struct timeval stamp; + fprintf(stderr, "Log: write "); + stamp.tv_sec = inarch.rp->timestamp.tv_sec; + stamp.tv_usec = inarch.rp->timestamp.tv_usec; + __pmPrintStamp(stderr, &stamp); + fprintf(stderr, " numpmid=%d @ offset=%ld\n", inarch.rp->numpmid, out_offset); + } +#endif + } + + /* restore numpmid up so all vset[]s are freed */ + inarch.rp->numpmid = orig_numpmid; + /* + * put pmResult back the way it was (so pmFreeResult works correctly) + * and release any allocated memory used in the rewriting + */ + clean_vset(inarch.rp); + /* restore numval up so all vlist[]s are freed */ + for (i = 0; i < orig_numpmid; i++) + inarch.rp->vset[i]->numval = orig_numval[i]; + free(orig_numval); + + pmFreeResult(inarch.rp); +} diff --git a/src/pmlogrewrite/util.c b/src/pmlogrewrite/util.c new file mode 100644 index 0000000..360ac10 --- /dev/null +++ b/src/pmlogrewrite/util.c @@ -0,0 +1,302 @@ +/* + * Utiility routines for pmlogrewrite + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2011 Ken McDonell. All Rights Reserved. + * Copyright (c) 1997-2000 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 "logger.h" +#include <assert.h> +#include <ctype.h> +#include <sys/stat.h> +#include <unistd.h> + +void +yywarn(char *s) +{ + fprintf(stderr, "Warning [%s, line %d]\n%s\n", configfile, lineno, s); +} + +void +yyerror(char *s) +{ + fprintf(stderr, "Specification error in configuration file (%s)\n", + configfile); + fprintf(stderr, "[line %d] %s\n", lineno, s); + exit(1); +} + +void +yysemantic(char *s) +{ + fprintf(stderr, "Semantic error in configuration file (%s)\n", + configfile); + fprintf(stderr, "%s\n", s); + exit(1); +} + +/* + * instance name matching ... return + * 0 for no match + * 1 for match to first space + * 2 for complete match + * -1 if either name is empty or NULL + */ +int +inst_name_eq(const char *p, const char *q) +{ + if (p == NULL || *p == '\0') + return -1; + if (q == NULL || *q == '\0') + return -1; + + for ( ; ; p++, q++) { + if (*p == '\0' && *q == '\0') + return 2; + if (*p == '\0' || *p == ' ') { + if (*q == '\0' || *q == ' ') + return 1; + break; + } + if (*q == '\0' || *q == ' ') { + if (*p == '\0' || *p == ' ') + return 1; + break; + } + if (*p != *q) + break; + } + return 0; +} + +/* + * Rename all the physical archive files with basename of old to + * a basename of new. + * + * If _any_ error occurs, don't make any changes. + * + * Note: does not handle compressed versions of files. + * + * TODO - need global locking for PCP 3.6 version if this is promoted + * to libpcp + */ +int +_pmLogRename(const char *old, const char *new) +{ + int sts; + int nfound = 0; + char **found = NULL; + char *dname; + char *obase; + char path[MAXPATHLEN+1]; + char opath[MAXPATHLEN+1]; + char npath[MAXPATHLEN+1]; + DIR *dirp; + struct dirent *dp; + + strncpy(path, old, sizeof(path)); + path[sizeof(path)-1] = '\0'; + dname = dirname(path); + + if ((dirp = opendir(dname)) == NULL) + return -oserror(); + + strncpy(path, old, sizeof(path)); + path[sizeof(path)-1] = '\0'; + obase = basename(path); + + for ( ; ; ) { + setoserror(0); + if ((dp = readdir(dirp)) == NULL) + break; + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + if (strncmp(obase, dp->d_name, strlen(obase)) == 0) { + /* + * match the base part of the old archive name, now check + * for meta or index or a valid volume number + */ + char *p = &dp->d_name[strlen(obase)]; + int want = 0; + if (strcmp(p, ".meta") == 0) + want = 1; + else if (strcmp(p, ".index") == 0) + want = 1; + else if (*p == '.' && isdigit((int)p[1])) { + char *endp; + long vol; + vol = strtol(&p[1], &endp, 10); + if (vol >= 0 && *endp == '\0') + want = 1; + } + if (want) { + struct stat stbuf; + snprintf(opath, sizeof(opath), "%s%s", old, p); + snprintf(npath, sizeof(npath), "%s%s", new, p); + if (stat(npath, &stbuf) == 0) { + fprintf(stderr, "__pmLogRename: destination file %s already exists\n", npath); + goto revert; + } + if (rename(opath, npath) == -1) { + fprintf(stderr, "__pmLogRename: rename %s -> %s failed: %s\n", opath, npath, pmErrStr(-oserror())); + goto revert; + } + nfound++; + found = (char **)realloc(found, nfound*sizeof(found[0])); + if (found == NULL) { + __pmNoMem("__pmLogRename: realloc", nfound*sizeof(found[0]), PM_RECOV_ERR); + abandon(); + /*NOTREACHED*/ + } + if ((found[nfound-1] = strdup(p)) == NULL) { + __pmNoMem("__pmLogRename: strdup", strlen(p)+1, PM_RECOV_ERR); + abandon(); + /*NOTREACHED*/ + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "__pmLogRename: %s -> %s\n", opath, npath); +#endif + } + } + } + + if ((sts = oserror()) != 0) { + fprintf(stderr, "__pmLogRename: readdir for %s failed: %s\n", dname, pmErrStr(-sts)); + goto revert; + } + + sts = 0; + goto cleanup; + +revert: + while (nfound > 0) { + snprintf(opath, sizeof(opath), "%s%s", old, found[nfound-1]); + snprintf(npath, sizeof(npath), "%s%s", new, found[nfound-1]); + if (rename(npath, opath) == -1) { + fprintf(stderr, "__pmLogRename: arrgh trying to revert rename %s -> %s failed: %s\n", npath, opath, pmErrStr(-oserror())); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "__pmLogRename: revert %s <- %s\n", opath, npath); +#endif + nfound--; + } + sts = PM_ERR_GENERIC; + +cleanup: + closedir(dirp); + while (nfound > 0) { + free(found[nfound-1]); + nfound--; + } + if (found != NULL) + free(found); + + return sts; +} + +/* + * Remove all the physical archive files with basename of base. + * + * Note: does not handle compressed versions of files. + * + * TODO - need global locking for PCP 3.6 version if this is promoted + * to libpcp + */ +int +_pmLogRemove(const char *name) +{ + int sts; + int nfound = 0; + char *dname; + char *base; + char path[MAXPATHLEN+1]; + DIR *dirp; + struct dirent *dp; + + strncpy(path, name, sizeof(path)); + path[sizeof(path)-1] = '\0'; + dname = strdup(dirname(path)); + if (dname == NULL) { + __pmNoMem("__pmLogRemove: dirname strdup", strlen(dirname(path))+1, PM_RECOV_ERR); + abandon(); + /*NOTREACHED*/ + } + + if ((dirp = opendir(dname)) == NULL) { + free(dname); + return -oserror(); + } + + strncpy(path, name, sizeof(path)); + path[sizeof(path)-1] = '\0'; + base = strdup(basename(path)); + if (base == NULL) { + __pmNoMem("__pmLogRemove: basename strdup", strlen(basename(path))+1, PM_RECOV_ERR); + abandon(); + /*NOTREACHED*/ + } + + for ( ; ; ) { + setoserror(0); + if ((dp = readdir(dirp)) == NULL) + break; + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + if (strncmp(base, dp->d_name, strlen(base)) == 0) { + /* + * match the base part of the old archive name, now check + * for meta or index or a valid volume number + */ + char *p = &dp->d_name[strlen(base)]; + int want = 0; + if (strcmp(p, ".meta") == 0) + want = 1; + else if (strcmp(p, ".index") == 0) + want = 1; + else if (*p == '.' && isdigit((int)p[1])) { + char *endp; + long vol; + vol = strtol(&p[1], &endp, 10); + if (vol >= 0 && *endp == '\0') + want = 1; + } + if (want) { + snprintf(path, sizeof(path), "%s%s", name, p); + unlink(path); + nfound++; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LOG) + fprintf(stderr, "__pmLogRemove: %s\n", path); +#endif + } + } + } + + if ((sts = oserror()) != 0) { + fprintf(stderr, "__pmLogRemove: readdir for %s failed: %s\n", dname, pmErrStr(-sts)); + sts = -sts; + } + else + sts = nfound; + + closedir(dirp); + free(dname); + free(base); + + return sts; +} |