TS-2977: move the stats processor to traffic_manager
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/9e6d233f Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/9e6d233f Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/9e6d233f Branch: refs/heads/master Commit: 9e6d233fe647863adb7b093cd6643739f73c7c8e Parents: 7386583 Author: James Peach <jpe...@apache.org> Authored: Thu Jul 31 10:53:44 2014 -0700 Committer: James Peach <jpe...@apache.org> Committed: Fri Aug 1 19:55:45 2014 -0700 ---------------------------------------------------------------------- cmd/traffic_manager/Makefile.am | 10 +- cmd/traffic_manager/StatProcessor.cc | 359 +++++++++ cmd/traffic_manager/StatProcessor.h | 94 +++ cmd/traffic_manager/StatType.cc | 1199 +++++++++++++++++++++++++++++ cmd/traffic_manager/StatType.h | 231 ++++++ cmd/traffic_manager/StatXML.cc | 82 ++ cmd/traffic_manager/StatXML.h | 47 ++ cmd/traffic_manager/stats.txt | 236 ++++++ configure.ac | 1 - mgmt/Makefile.am | 2 +- mgmt/stats/Makefile.am | 40 - mgmt/stats/StatProcessor.cc | 359 --------- mgmt/stats/StatProcessor.h | 94 --- mgmt/stats/StatType.cc | 1199 ----------------------------- mgmt/stats/StatType.h | 231 ------ mgmt/stats/StatXML.cc | 82 -- mgmt/stats/StatXML.h | 47 -- mgmt/stats/spec | 236 ------ 18 files changed, 2256 insertions(+), 2293 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/Makefile.am ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/Makefile.am b/cmd/traffic_manager/Makefile.am index 1bb5614..e547f1d 100644 --- a/cmd/traffic_manager/Makefile.am +++ b/cmd/traffic_manager/Makefile.am @@ -27,7 +27,6 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/mgmt \ -I$(top_srcdir)/mgmt/api/include \ -I$(top_srcdir)/mgmt/cluster \ - -I$(top_srcdir)/mgmt/stats \ -I$(top_srcdir)/mgmt/utils \ -I$(top_srcdir)/mgmt/web2 \ -I$(top_srcdir)/lib \ @@ -36,13 +35,18 @@ AM_CPPFLAGS = \ traffic_manager_SOURCES = \ AddConfigFilesHere.cc \ Main.cc \ - Main.h + Main.h \ + StatProcessor.cc \ + StatProcessor.h \ + StatType.cc \ + StatType.h \ + StatXML.cc \ + StatXML.h traffic_manager_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @EXPAT_LDFLAGS@ @LIBTOOL_LINK_FLAGS@ traffic_manager_LDADD = \ $(top_builddir)/mgmt/libmgmt_lm.a \ $(top_builddir)/mgmt/cluster/libcluster.a \ - $(top_builddir)/mgmt/stats/libstats.a \ $(top_builddir)/mgmt/web2/libweb.a \ $(top_builddir)/mgmt/api/libmgmtapilocal.a \ $(top_builddir)/mgmt/api/libtsmgmtshare.la \ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatProcessor.cc ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatProcessor.cc b/cmd/traffic_manager/StatProcessor.cc new file mode 100644 index 0000000..24cbaa9 --- /dev/null +++ b/cmd/traffic_manager/StatProcessor.cc @@ -0,0 +1,359 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/***************************************/ +/**************************************************************************** + * + * StatProcessor.cc - Functions for computing node and cluster stat + * aggregation + * + * + ****************************************************************************/ + +#include "ink_config.h" +#include "StatProcessor.h" +#include "FileManager.h" + +#define STAT_CONFIG_FILE "stats.config.xml" + +StatObjectList statObjectList; +StatXMLTag currentTag = INVALID_TAG; +StatObject *statObject = NULL; +char *exprContent = NULL; +static unsigned statCount = 0; // global statistics object counter +bool nodeVar; +bool sumClusterVar; + +// These helpers are used to work around the unsigned char'iness of xmlchar when +// using libxml2. We don't have any tags (now) which uses UTF8. +static int +xml_atoi(const xmlchar *nptr) +{ + return atoi((const char*)nptr); +} + +static double +xml_atof(const xmlchar *nptr) +{ + return atof((const char*)nptr); +} + +static int +xml_strcmp(const xmlchar *s1, const char *s2) +{ + return strcmp((const char *)s1, s2); +} + + +static void +elementStart(void * /* userData ATS_UNUSED */, const xmlchar *name, const xmlchar **atts) +{ + int i = 0; + + if (!xml_strcmp(name, "ink:statistics")) + currentTag = ROOT_TAG; + else if (!xml_strcmp(name, "statistics")) + currentTag = STAT_TAG; + else if (!xml_strcmp(name, "destination")) + currentTag = DST_TAG; + else if (!xml_strcmp(name, "expression")) + currentTag = EXPR_TAG; + else + currentTag = INVALID_TAG; + + switch (currentTag) { + case STAT_TAG: + statObject = new StatObject(++statCount); + Debug(MODULE_INIT, "\nStat #: ----------------------- %d -----------------------\n", statCount); + + if (atts) + for (i = 0; atts[i]; i += 2) { + ink_assert(atts[i + 1]); // Attribute comes in pairs, hopefully. + + if (!xml_strcmp(atts[i], "minimum")) { + statObject->m_stats_min = (MgmtFloat) xml_atof(atts[i + 1]); + statObject->m_has_min = true; + } else if (!xml_strcmp(atts[i], "maximum")) { + statObject->m_stats_max = (MgmtFloat) xml_atof(atts[i + 1]); + statObject->m_has_max = true; + } else if (!xml_strcmp(atts[i], "interval")) { + statObject->m_update_interval = (ink_hrtime) xml_atoi(atts[i + 1]); + } else if (!xml_strcmp(atts[i], "debug")) { + statObject->m_debug = (atts[i + 1] && atts[i + 1][0] == '1'); + } + + Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], atts[i + 1]); + } + break; + + case EXPR_TAG: + exprContent = (char*)ats_malloc(BUFSIZ * 10); + memset(exprContent, 0, BUFSIZ * 10); + break; + + case DST_TAG: + nodeVar = true; + sumClusterVar = true; // Should only be used with cluster variable + + if (atts) + for (i = 0; atts[i]; i += 2) { + ink_assert(atts[i + 1]); // Attribute comes in pairs, hopefully. + if (!xml_strcmp(atts[i], "scope")) { + nodeVar = (!xml_strcmp(atts[i + 1], "node") ? true : false); + } else if (!xml_strcmp(atts[i], "operation")) { + sumClusterVar = (!xml_strcmp(atts[i + 1], "sum") ? true : false); + } + + Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], atts[i + 1]); + } + + break; + + case INVALID_TAG: + Debug(MODULE_INIT, "==========================================>%s<=\n", name); + break; + + default: + break; + } +} + + +static void +elementEnd(void * /* userData ATS_UNUSED */, const xmlchar */* name ATS_UNUSED */) +{ + switch (currentTag) { + case STAT_TAG: + statObjectList.enqueue(statObject); + currentTag = ROOT_TAG; + break; + + case EXPR_TAG: + statObject->assignExpr(exprContent); // This hands over ownership of exprContent + // fall through + + default: + currentTag = STAT_TAG; + break; + } +} + + +static void +charDataHandler(void * /* userData ATS_UNUSED */, const xmlchar * name, int /* len ATS_UNUSED */) +{ + if (currentTag != EXPR_TAG && currentTag != DST_TAG) { + return; + } + + char content[BUFSIZ * 10]; + if (XML_extractContent((const char*)name, content, BUFSIZ * 10) == 0) { + return; + } + + if (currentTag == EXPR_TAG) { + ink_strlcat(exprContent, content, BUFSIZ * 10); // see above for the size + + } else { + statObject->assignDst(content, nodeVar, sumClusterVar); + } +} + + +StatProcessor::StatProcessor(FileManager * configFiles):m_lmgmt(NULL), m_overviewGenerator(NULL) +{ + rereadConfig(configFiles); +} + + +void +StatProcessor::rereadConfig(FileManager * configFiles) +{ + textBuffer *fileContent = NULL; + Rollback *fileRB = NULL; + char *fileBuffer = NULL; + version_t fileVersion; + int fileLen; + + statObjectList.clean(); + statCount = 0; // reset statistics counter + + int ret = configFiles->getRollbackObj(STAT_CONFIG_FILE, &fileRB); + if (!ret) { + Debug(MODULE_INIT, " Can't get Rollback for file: %s\n", STAT_CONFIG_FILE); + } + fileVersion = fileRB->getCurrentVersion(); + fileRB->getVersion(fileVersion, &fileContent); + fileBuffer = fileContent->bufPtr(); + fileLen = strlen(fileBuffer); + +#if HAVE_LIBEXPAT + /* + * Start the XML Praser -- the package used is EXPAT + */ + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, NULL); + XML_SetElementHandler(parser, elementStart, elementEnd); + XML_SetCharacterDataHandler(parser, charDataHandler); + + /* + * Substiture every newline with a space to get around + * the SetCharacterDataHandler problem. + */ + char *newlinePtr; + while ((newlinePtr = strchr(fileBuffer, '\n')) != NULL || (newlinePtr = strchr(fileBuffer, '\r')) != NULL) { + *newlinePtr = ' '; + } + + /* + * Parse the input file according to XML standard. + * Print error if we encounter any + */ + int status = XML_Parse(parser, fileBuffer, fileLen, true); + if (!status) { + mgmt_log(stderr, "%s at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); + } + + /* + * Cleaning upt + */ + XML_ParserFree(parser); +#else + /* Parse XML with libxml2 */ + xmlSAXHandler sax; + memset(&sax, 0, sizeof(xmlSAXHandler)); + sax.startElement = elementStart; + sax.endElement = elementEnd; + sax.characters = charDataHandler; + sax.initialized = 1; + xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(&sax, NULL, NULL, 0, NULL); + + int status = xmlParseChunk(parser, fileBuffer, fileLen, 1); + if (status != 0) { + xmlErrorPtr errptr = xmlCtxtGetLastError(parser); + mgmt_log(stderr, "%s at %s:%d\n", errptr->message, errptr->file, errptr->line); + } + xmlFreeParserCtxt(parser); +#endif + + + delete fileContent; + + Debug(MODULE_INIT, "\n\n---------- END OF PARSING & INITIALIZING ---------\n\n"); +} + + +StatProcessor::~StatProcessor() +{ + + Debug(MODULE_INIT, "[StatProcessor] Destructing Statistics Processor\n"); + +} + + +void +setTest() +{ + char var_name[64]; + + for (int i = 1; i <= 5; i++) { + memset(var_name, 0, 64); + snprintf(var_name, sizeof(var_name), "proxy.node.stats.test%d", i); + if (i == 4) { + MgmtFloat tmp; + varFloatFromName("proxy.node.stats.test4", &tmp); + varSetFloat(var_name, tmp + 1, true); + } else { + varSetFloat(var_name, i, true); + } + } +} + + +void +verifyTest() +{ + MgmtFloat tmp1, tmp2; + + // 1. simple copy + varFloatFromName("proxy.node.stats.test1", &tmp1); + varFloatFromName("proxy.node.stats.test2", &tmp2); + if (tmp1 == tmp2) { + Debug(MODULE_INIT, "PASS -- simple copy"); + } else { + Debug(MODULE_INIT, "FAIL -- simple copy"); + } + + // 2. simple interval + varFloatFromName("proxy.node.stats.test3", &tmp2); + if (tmp2 >= 10) { + Debug(MODULE_INIT, "PASS -- simple interval & constant"); + } else { + Debug(MODULE_INIT, "FAIL -- simple interval & constant %f", tmp2); + } + + // 3. delta + varFloatFromName("proxy.node.stats.test4", &tmp2); + if ((tmp2 > 150) && (tmp2 < 250)) { + Debug(MODULE_INIT, "PASS -- delta"); + } else { + Debug(MODULE_INIT, "FAIL -- delta %f", tmp2); + } +} + + +/** + * Updating the statistics NOW. + **/ +unsigned short +StatProcessor::processStat() +{ + unsigned short result = 0; + + Debug(MODULE_INIT, "[StatProcessor] Processing Statistics....\n"); + +// setTest(); + statObjectList.Eval(); +// verifyTest(); + + return (result); +} + + +/** + * ExpressionEval + * -------------- + * + */ +RecData +ExpressionEval(char *exprString) +{ + RecDataT result_type; + StatObject statObject; + + char content[BUFSIZ * 10]; + XML_extractContent(exprString, content, BUFSIZ * 10); + + statObject.assignExpr(content); + return statObject.NodeStatEval(&result_type, false); +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatProcessor.h ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatProcessor.h b/cmd/traffic_manager/StatProcessor.h new file mode 100644 index 0000000..71dab2f --- /dev/null +++ b/cmd/traffic_manager/StatProcessor.h @@ -0,0 +1,94 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#ifndef _STAT_PROCESSOR_H_ +#define _STAT_PROCESSOR_H_ + +/**************************************************************************** + * + * StatProcessor.h - Functions for computing node and cluster stat + * aggregation + * + * + ****************************************************************************/ + +#include "ink_platform.h" +#include <stdarg.h> +#include "MgmtUtils.h" +#include "MgmtDefs.h" +#include "WebMgmtUtils.h" +#include "ink_hrtime.h" +#include "LocalManager.h" +#include "WebOverview.h" + +#define _HEADER +#define _D(x) +#define _FOOTER +#include "DynamicStats.h" +#include "StatType.h" + +#if HAVE_LIBEXPAT +#include "expat.h" +typedef XML_Char xmlchar; +#elif HAVE_LIBXML2 +#include <libxml/parser.h> +#include <libxml/SAX.h> +typedef xmlChar xmlchar; +#else +# error "No XML parser - please configure expat or libxml2" +#endif + +#include <string.h> +#include <stdlib.h> + +class StatProcessor +{ +public: + + explicit StatProcessor(FileManager * configFiles); + ~StatProcessor(); + + // Member Fuctions + unsigned short processStat(); + void rereadConfig(FileManager * configFiles); + + LocalManager *m_lmgmt; + overviewPage *m_overviewGenerator; +}; + + +/** + * External expression evaluation API. + * + * INPUT: an expression string, e.g.: + * "(proxy.node.user_agent_total_bytes-proxy.node.origin_server_total_bytes) + * / proxy.node.user_agent_total_bytes" + * + * RETURN: the resulting value of the expression. + * NOTE: it returns -9999.0 if there is an error. + * + */ + +RecData ExpressionEval(char *); + +#endif http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatType.cc ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatType.cc b/cmd/traffic_manager/StatType.cc new file mode 100644 index 0000000..1c51b95 --- /dev/null +++ b/cmd/traffic_manager/StatType.cc @@ -0,0 +1,1199 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/***************************************/ +/**************************************************************************** + * + * StatType.cc - Functions for computing node and cluster stat + * aggregation + * + * + ****************************************************************************/ + +#include "ink_config.h" +#include "StatType.h" +#include "MgmtUtils.h" +#include "ink_hrtime.h" +#include "WebOverview.h" + +bool StatError = false; // global error flag +bool StatDebug = false; // global debug flag + +/** + * StatExprToken() + * --------------- + */ +StatExprToken::StatExprToken() + : m_arith_symbol('\0'), + m_token_name(NULL), + m_token_type(RECD_NULL), + m_sum_var(false), m_node_var(true) +{ + RecDataClear(RECD_NULL, &m_token_value); + RecDataClear(RECD_NULL, &m_token_value_max); + RecDataClear(RECD_NULL, &m_token_value_min); + memset(&m_token_value_delta, 0, sizeof(m_token_value_delta)); +} + + +/** + * StatExprToken::copy() + * --------------------- + */ +void +StatExprToken::copy(const StatExprToken & source) +{ + m_arith_symbol = source.m_arith_symbol; + + if (source.m_token_name != NULL) { + m_token_name = ats_strdup(source.m_token_name); + } + + m_token_type = source.m_token_type; + m_token_value = source.m_token_value; + m_token_value_min = source.m_token_value_min; + m_token_value_max = source.m_token_value_max; + + if (source.m_token_value_delta) { + m_token_value_delta = new StatDataSamples(); + m_token_value_delta->previous_time = source.m_token_value_delta->previous_time; + m_token_value_delta->current_time = source.m_token_value_delta->current_time; + m_token_value_delta->previous_value = source.m_token_value_delta->previous_value; + m_token_value_delta->current_value = source.m_token_value_delta->current_value; + } + + m_node_var = source.m_node_var; + m_sum_var = source.m_sum_var; +} + + +/** + * StatExprToken::assignTokenName() + * -------------------------------- + * Assign the token name. If the token is a predefined constant, + * assign the value as well. Also, assign the token type as well. + */ +void +StatExprToken::assignTokenName(const char *name) +{ + + if (isdigit(name[0])) { + // numerical constant + m_token_name = ats_strdup("CONSTANT"); + m_token_type = RECD_CONST; + } else { + m_token_name = ats_strdup(name); + assignTokenType(); + } + + switch (m_token_type) { + case RECD_INT: + case RECD_COUNTER: + case RECD_FLOAT: + break; + case RECD_CONST: + // assign pre-defined constant in here + // constant will be stored as RecFloat type. + if (!strcmp(m_token_name, "CONSTANT")) { + m_token_value.rec_float = (RecFloat) atof(name); + } else if (!strcmp(m_token_name, "$BYTES_TO_MB_SCALE")) { + m_token_value.rec_float = (RecFloat) BYTES_TO_MB_SCALE; + } else if (!strcmp(m_token_name, "$MBIT_TO_KBIT_SCALE")) { + m_token_value.rec_float = (RecFloat) MBIT_TO_KBIT_SCALE; + } else if (!strcmp(m_token_name, "$SECOND_TO_MILLISECOND_SCALE")) { + m_token_value.rec_float = (RecFloat) SECOND_TO_MILLISECOND_SCALE; + } else if (!strcmp(m_token_name, "$PCT_TO_INTPCT_SCALE")) { + m_token_value.rec_float = (RecFloat) PCT_TO_INTPCT_SCALE; + } else if (!strcmp(m_token_name, "$HRTIME_SECOND")) { + m_token_value.rec_float = (RecFloat) HRTIME_SECOND; + } else if (!strcmp(m_token_name, "$BYTES_TO_MBIT_SCALE")) { + m_token_value.rec_float = (RecFloat) BYTES_TO_MBIT_SCALE; + } else { + mgmt_log(stderr, "[StatPro] ERROR: Undefined constant: %s\n", m_token_name); + StatError = true; + } + case RECD_FX: + default: + break; + } +} + + +/** + * assignTokenType() + * ----------------- + * Assign the proper token type based on the token name. + * Do some token type conversion if necessary. Return true + * if the token type is recognizable; false otherwise. + */ +bool StatExprToken::assignTokenType() +{ + ink_assert(m_token_name != NULL); + m_token_type = varType(m_token_name); + + if (m_token_name[0] == '$') { + m_token_type = RECD_CONST; + } else if (m_token_name[0] == '_') { + m_token_type = RECD_FX; + } + + if (m_token_value_delta) { + m_token_value_delta->data_type = m_token_type; + } + + // I'm guessing here that we want to check if we're still RECD_NULL, + // it used to be INVALID, which is not in the m_token_type's enum. /leif + return (m_token_type != RECD_NULL); +} + + +void +StatExprToken::clean() +{ + ats_free(m_token_name); + delete m_token_value_delta; +} + + +/** + * FOR DEBUGGING ONLY + * Print the token according to its type in a human-readable format. :) + */ +void +StatExprToken::print(const char *prefix) +{ + if (m_token_name != NULL) { + printf("%s\t%s\n", prefix, m_token_name); + } else { + printf("%s\t%c\n", prefix, m_arith_symbol); + } +} + + +/** + * StatExprToken::precedence() + * --------------------------- + * Return the binary operator precedence. The higher returning value, + * the higher the precedence value. + */ +short +StatExprToken::precedence() +{ + switch (m_arith_symbol) { + case '(': + return 4; + case '^': // fall through + case '!': + return 3; + case '*': // fall through + case '/': + return 2; + case '+': // fall through + case '-': + return 1; + default: + return -1; + } +} + + +/** + * StatExprToken::statVarSet() + * --------------------------- + * This method is responsible for ensuring the assigning value + * fall within the min. and max. bound. If it's smaller than min. + * or larger than max, then the error value is assigned. If no + * error value is assigned, either min. or max. is assigned. + */ +bool StatExprToken::statVarSet(RecDataT type, RecData value) +{ + RecData converted_value; + + if (StatError) { + /* fix this after librecords is done + mgmt_log(stderr, + "[StatPro] ERROR in a statistics aggregation operations\n"); + */ + RecData err_value; + RecDataClear(m_token_type, &err_value); + return varSetData(m_token_type, m_token_name, err_value); + } + + /* + * do conversion if necessary. + */ + if (m_token_type != type) { + switch (m_token_type) { + case RECD_INT: + case RECD_COUNTER: + if (type == RECD_NULL) + converted_value = value; + else if (type == RECD_INT || type == RECD_COUNTER || type == RECD_FX) + converted_value.rec_int = value.rec_int; + else if (type == RECD_FLOAT || type == RECD_CONST) + converted_value.rec_int = (RecInt)value.rec_float; + else + Fatal("%s, invalid value type:%d\n", m_token_name, type); + break; + case RECD_FLOAT: + if (type == RECD_NULL) + converted_value = value; + else if (type == RECD_INT || type == RECD_COUNTER || type == RECD_FX) + converted_value.rec_float = (RecFloat)value.rec_int; + else if (type == RECD_FLOAT || type == RECD_CONST) + converted_value.rec_float = value.rec_float; + else + Fatal("%s, invalid value type:%d\n", m_token_name, type); + break; + default: + Fatal("%s, unsupported token type:%d\n", m_token_name, m_token_type); + } + } else { + converted_value = value; + } + + if (RecDataCmp(m_token_type, converted_value, m_token_value_min) < 0) { + value = m_token_value_min; + } + else if (RecDataCmp(m_token_type, converted_value, m_token_value_max) > 0) { + value = m_token_value_max; + } + + return varSetData(m_token_type, m_token_name, converted_value); +} + + +/*********************************************************************** + StatExprList + **********************************************************************/ + +/** + * StatExprList::StatExprList() + * ---------------------------- + */ +StatExprList::StatExprList() + : m_size(0) +{ +} + + +/** + * StatExprList::clean() + * --------------------- + */ +void +StatExprList::clean() +{ + StatExprToken *temp = NULL; + + while ((temp = m_tokenList.dequeue())) { + delete(temp); + m_size -= 1; + } + ink_assert(m_size == 0); +} + + +void +StatExprList::enqueue(StatExprToken * entry) +{ + ink_assert(entry); + m_tokenList.enqueue(entry); + m_size += 1; +} + + +void +StatExprList::push(StatExprToken * entry) +{ + ink_assert(entry); + m_tokenList.push(entry); + m_size += 1; +} + + +StatExprToken * +StatExprList::dequeue() +{ + if (m_size == 0) { + return NULL; + } + m_size -= 1; + return (StatExprToken *) m_tokenList.dequeue(); +} + + +StatExprToken * +StatExprList::pop() +{ + if (m_size == 0) { + return NULL; + } + m_size -= 1; + return m_tokenList.pop(); +} + + +StatExprToken * +StatExprList::top() +{ + if (m_size == 0) { + return NULL; + } + return m_tokenList.head; +} + + +StatExprToken * +StatExprList::first() +{ + if (m_size == 0) { + return NULL; + } + return m_tokenList.head; +} + + +StatExprToken * +StatExprList::next(StatExprToken * current) +{ + if (!current) { + return NULL; + } + return (current->link).next; +} + + +/** + * StatExprList::print() + * --------------------- + * Print the token in the expression in a human-readable format. :) + */ +void +StatExprList::print(const char *prefix) +{ + for (StatExprToken * token = first(); token; token = next(token)) { + token->print(prefix); + } +} + + +/** + * StatExprToken::count() + * ---------------------- + * Counts the number of token in the expression list and return it. + */ +unsigned +StatExprList::count() +{ + return m_size; +} + + +/*********************************************************************** + StatObject + **********************************************************************/ + + +/** + * StatObject::StatObject() + * ------------------------ + */ + +StatObject::StatObject() + : m_id(1), + m_debug(false), + m_expr_string(NULL), + m_node_dest(NULL), + m_cluster_dest(NULL), + m_expression(NULL), + m_postfix(NULL), + m_last_update(-1), + m_current_time(-1), m_update_interval(-1), + m_stats_max(FLT_MAX), m_stats_min(FLT_MIN), + m_has_max(false), m_has_min(false), m_has_delta(false) +{ +} + + +StatObject::StatObject(unsigned identifier) + : m_id(identifier), + m_debug(false), + m_expr_string(NULL), + m_node_dest(NULL), + m_cluster_dest(NULL), + m_expression(NULL), + m_postfix(NULL), + m_last_update(-1), + m_current_time(-1), m_update_interval(-1), + m_stats_max(FLT_MAX), m_stats_min(FLT_MIN), + m_has_max(false), m_has_min(false), m_has_delta(false) +{ +} + + +/** + * StatObject::clean() + * ------------------- + */ +void +StatObject::clean() +{ + ats_free(m_expr_string); + delete m_node_dest; + delete m_cluster_dest; + delete m_postfix; +} + + +/** + * StatObject::assignDst() + * ----------------------- + */ +void +StatObject::assignDst(const char *str, bool m_node_var, bool m_sum_var) +{ + if (StatDebug) { + Debug(MODULE_INIT, "DESTINTATION: %s\n", str); + } + + StatExprToken *statToken = new StatExprToken(); + + statToken->assignTokenName(str); + statToken->m_node_var = m_node_var; + statToken->m_sum_var = m_sum_var; + + // The type of dst token should be always not NULL + if (statToken->m_token_type == RECD_NULL) { + Fatal("token:%s, invalid token type!", statToken->m_token_name); + } + + // Set max/min value + if (m_has_max) + RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_max, + m_stats_max); + else + RecDataSetMax(statToken->m_token_type, &statToken->m_token_value_max); + + if (m_has_min) + RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_min, + m_stats_min); + else + RecDataSetMin(statToken->m_token_type, &statToken->m_token_value_min); + + if (m_node_var) { + ink_assert(m_node_dest == NULL); + m_node_dest = statToken; + } else { + ink_assert(m_cluster_dest == NULL); + m_cluster_dest = statToken; + } +} + + +/** + * StatObject::assignExpr() + * ------------------------ + */ +void +StatObject::assignExpr(char *str) +{ + StatExprToken *statToken = NULL; + + if (StatDebug) { + Debug(MODULE_INIT, "EXPRESSION: %s\n", str); + } + ink_assert(m_expr_string == NULL); + // We take ownership here + m_expr_string = str; + + Tokenizer exprTok(" "); + exprTok.Initialize(str); + tok_iter_state exprTok_state; + const char *token = exprTok.iterFirst(&exprTok_state); + + ink_assert(m_expression == NULL); + m_expression = new StatExprList(); + + while (token) { + + statToken = new StatExprToken(); + + if (isOperator(token[0])) { + + statToken->m_arith_symbol = token[0]; + ink_assert(statToken->m_token_name == NULL); + + if (StatDebug) { + Debug(MODULE_INIT, "\toperator: ->%c<-\n", statToken->m_arith_symbol); + } + + } else { + + ink_assert(statToken->m_arith_symbol == '\0'); + + // delta + if (token[0] == '#') { + + token += 1; // skip '#' + statToken->m_token_value_delta = new StatDataSamples(); + statToken->m_token_value_delta->previous_time = (ink_hrtime) 0; + statToken->m_token_value_delta->current_time = (ink_hrtime) 0; + statToken->m_token_value_delta->data_type = RECD_NULL; + RecDataClear(RECD_NULL, &statToken->m_token_value_delta->previous_value); + RecDataClear(RECD_NULL, &statToken->m_token_value_delta->current_value); + + } + + statToken->assignTokenName(token); + + if (StatDebug) { + Debug(MODULE_INIT, "\toperand: ->%s<-\n", token); + } + + } + + token = exprTok.iterNext(&exprTok_state); + m_expression->enqueue(statToken); + + } + + infix2postfix(); + +} + + +/** + * StatObject::infix2postfix() + * --------------------------- + * Takes the infix "expression" and convert it to a postfix for future + * evaluation. + * + * SIDE EFFECT: consume all token in "expression" + */ +void +StatObject::infix2postfix() +{ + StatExprList stack; + StatExprToken *tempToken = NULL; + StatExprToken *curToken = NULL; + m_postfix = new StatExprList(); + + while (m_expression->top()) { + curToken = m_expression->dequeue(); + + if (!isOperator(curToken->m_arith_symbol)) { + //printf("I2P~: enqueue %s\n", curToken->m_token_name); + m_postfix->enqueue(curToken); + + } else { + ink_assert(curToken->m_arith_symbol != '\0'); + + if (curToken->m_arith_symbol == '(') { + stack.push(curToken); + } else if (curToken->m_arith_symbol == ')') { + tempToken = (StatExprToken *) stack.pop(); + + while (tempToken->m_arith_symbol != '(') { + //printf("I2P@: enqueue %c\n", tempToken->m_arith_symbol); + m_postfix->enqueue(tempToken); + tempToken = (StatExprToken *) stack.pop(); + } + + // Free up memory for ')' + delete(curToken); + delete(tempToken); + + } else { + if (stack.count() == 0) { + stack.push(curToken); + } else { + tempToken = (StatExprToken *) stack.top(); + + while ((tempToken->m_arith_symbol != '(') && (tempToken->precedence() >= curToken->precedence())) { + tempToken = (StatExprToken *) stack.pop(); // skip the ( + //printf("I2P$: enqueue %c\n", tempToken->m_arith_symbol); + m_postfix->enqueue(tempToken); + if (stack.count() == 0) { + break; + } + tempToken = (StatExprToken *) stack.top(); + } // while + + stack.push(curToken); + } + } + } + } + + while (stack.count() > 0) { + tempToken = (StatExprToken *) stack.pop(); + //printf("I2P?: enqueue %c\n", tempToken->m_arith_symbol); + m_postfix->enqueue(tempToken); + } + + // dump infix expression + delete(m_expression); + m_expression = NULL; +} + + +/** + * StatObject::NodeStatEval() + * -------------------------- + * + * + */ +RecData StatObject::NodeStatEval(RecDataT *result_type, bool cluster) +{ + StatExprList stack; + StatExprToken *left = NULL; + StatExprToken *right = NULL; + StatExprToken *result = NULL; + StatExprToken *curToken = NULL; + RecData tempValue; + RecDataClear(RECD_NULL, &tempValue); + + *result_type = RECD_NULL; + + /* Express checkout lane -- Stat. object with on 1 source variable */ + if (m_postfix->count() == 1) { + StatExprToken * src = m_postfix->top(); + + // in librecords, not all statistics are register at initialization + // must assign proper type if it is undefined. + if (src->m_token_type == RECD_NULL) { + src->assignTokenType(); + } + + *result_type = src->m_token_type; + if (src->m_token_type == RECD_CONST) { + tempValue = src->m_token_value; + } else if (src->m_token_value_delta) { + tempValue = src->m_token_value_delta->diff_value(src->m_token_name); + } else if (!cluster) { + if (!varDataFromName(src->m_token_type, src->m_token_name, &tempValue)) { + RecDataClear(src->m_token_type, &tempValue); + } + } else { + if (!overviewGenerator->varClusterDataFromName(src->m_token_type, + src->m_token_name, + &tempValue)) { + RecDataClear(src->m_token_type, &tempValue); + } + } + } else { + + /* standard postfix evaluation */ + for (StatExprToken * token = m_postfix->first(); token; token = m_postfix->next(token)) { + /* carbon-copy the token. */ + curToken = new StatExprToken(); + curToken->copy(*token); + + if (!isOperator(curToken->m_arith_symbol)) { + stack.push(curToken); + } else { + ink_assert(isOperator(curToken->m_arith_symbol)); + right = stack.pop(); + left = stack.pop(); + + if (left->m_token_type == RECD_NULL) { + left->assignTokenType(); + } + if (right->m_token_type == RECD_NULL) { + right->assignTokenType(); + } + + result = StatBinaryEval(left, curToken->m_arith_symbol, right, cluster); + + stack.push(result); + delete(curToken); + delete(left); + delete(right); + } + } + + /* should only be 1 value left on stack -- the resulting value */ + if (stack.count() > 1) { + stack.print("\t"); + ink_assert(false); + } + + *result_type = stack.top()->m_token_type; + tempValue = stack.top()->m_token_value; + } + + return tempValue; + +} + + +/** + * StatObject::ClusterStatEval() + * ----------------------------- + * + * + */ +RecData StatObject::ClusterStatEval(RecDataT *result_type) +{ + /* Sanity check */ + ink_assert(m_cluster_dest && !m_cluster_dest->m_node_var); + + // what is this? + if ((m_node_dest == NULL) || (m_cluster_dest->m_sum_var == false)) { + return NodeStatEval(result_type, true); + } else { + RecData tempValue; + + if (!overviewGenerator->varClusterDataFromName(m_node_dest->m_token_type, + m_node_dest->m_token_name, + &tempValue)) { + *result_type = RECD_NULL; + RecDataClear(*result_type, &tempValue); + } + + return (tempValue); + } +} + + +/** + * StatObject::setTokenValue() + * --------------------------- + * The logic of the following code segment is the following. + * The objective is to extract the appropriate right->m_token_value. + * If 'right' is an intermediate value, nothing to do. + * If m_token_type is RECD_CONST, nothing to do. + * If m_token_type is RECD_FX, right->m_token_value is the diff. in time. + * If m_token_type is either RECD_INT or RECD_FLOAT, it can either + * by a cluster variable or a node variable. + * If it is a cluster variable, just use varClusterFloatFromName + * to set right->m_token_value. + * If it is a node variable, then it can either be a variable + * with delta. To determine whether it has a delta, simply search + * the m_token_name in the delta list. If found then it has delta. + * If it has delta then use the delta's diff. in value, + * otherwise simply set right->m_token_value with varFloatFromName. + */ +void +StatObject::setTokenValue(StatExprToken * token, bool cluster) +{ + if (token->m_token_name) { + // it is NOT an intermediate value + + switch (token->m_token_type) { + case RECD_CONST: + break; + + case RECD_FX: + // only support time function + // use rec_int to store time value + token->m_token_value.rec_int = (m_current_time - m_last_update); + break; + + case RECD_INT: // fallthought + case RECD_COUNTER: + case RECD_FLOAT: + if (cluster) { + if (!overviewGenerator->varClusterDataFromName(token->m_token_type, + token->m_token_name, + &(token->m_token_value))) + { + RecDataClear(token->m_token_type, &token->m_token_value); + } + } else { + if (token->m_token_value_delta) { + token->m_token_value = + token->m_token_value_delta->diff_value(token->m_token_name); + } else { + if (!varDataFromName(token->m_token_type, token->m_token_name, + &(token->m_token_value))) { + RecDataClear(token->m_token_type, &token->m_token_value); + } + } // delta? + } // cluster? + break; + + default: + if (StatDebug) { + Debug(MODULE, "Unrecognized token \"%s\" of type %d.\n", + token->m_token_name, token->m_token_type); + } + } // switch + } // m_token_name? +} + + +/** + * StatObject::StatBinaryEval() + * ------------------------ + * Take the left token, the right token, an binary operation and perform an + * arithmatic operations on them. This function is responsible for getting the + * correct value from: + * - (1) node variable + * - (2) node variable with a delta structure + * - (3) cluster variable + * - (4) an immediate value + */ +StatExprToken *StatObject::StatBinaryEval(StatExprToken * left, char op, + StatExprToken * right, bool cluster) +{ + RecData l, r; + StatExprToken *result = new StatExprToken(); + result->m_token_type = RECD_INT; + + if (left->m_token_type == RECD_NULL + && right->m_token_type == RECD_NULL) { + return result; + } + + if (left->m_token_type != RECD_NULL) { + setTokenValue(left, cluster); + result->m_token_type = left->m_token_type; + } + + if (right->m_token_type != RECD_NULL) { + setTokenValue(right, cluster); + switch (result->m_token_type) { + case RECD_NULL: + result->m_token_type = right->m_token_type; + break; + case RECD_FX: + case RECD_INT: + case RECD_COUNTER: + /* + * When types of left and right are different, select RECD_FLOAT + * as result type. It's may lead to loss of precision when do + * conversion, be careful! + */ + if (right->m_token_type == RECD_FLOAT + || right->m_token_type == RECD_CONST) { + result->m_token_type = right->m_token_type; + } + break; + case RECD_CONST: + case RECD_FLOAT: + break; + default: + Fatal("Unexpected RecData Type:%d", result->m_token_type); + break; + } + } + + /* + * We should make the operands with the same type before calculating. + */ + RecDataClear(RECD_NULL, &l); + RecDataClear(RECD_NULL, &r); + + if (left->m_token_type == right->m_token_type ) { + l = left->m_token_value; + r = right->m_token_value; + } else if (result->m_token_type != left->m_token_type) { + if (left->m_token_type != RECD_NULL) { + ink_assert(result->m_token_type == RECD_FLOAT + || result->m_token_type == RECD_CONST); + + l.rec_float = (RecFloat)left->m_token_value.rec_int; + } + r = right->m_token_value; + ink_assert(result->m_token_type == right->m_token_type); + } else { + l = left->m_token_value; + if (right->m_token_type != RECD_NULL) { + ink_assert(result->m_token_type == RECD_FLOAT + || result->m_token_type == RECD_CONST); + + r.rec_float = (RecFloat)right->m_token_value.rec_int; + } + ink_assert(result->m_token_type == left->m_token_type); + } + + /* + * Start to calculate + */ + switch (op) { + case '+': + result->m_token_value = RecDataAdd(result->m_token_type, l, r); + break; + + case '-': + result->m_token_value = RecDataSub(result->m_token_type, l, r); + break; + + case '*': + result->m_token_value = RecDataMul(result->m_token_type, l, r); + break; + + case '/': + RecData recTmp; + RecDataClear(RECD_NULL, &recTmp); + + /* + * Force the type of result to be RecFloat on div operation + */ + if (result->m_token_type != RECD_FLOAT && result->m_token_type != RECD_CONST) { + RecFloat t; + + result->m_token_type = RECD_FLOAT; + + t = (RecFloat)l.rec_int; + l.rec_float = t; + + t = (RecFloat)r.rec_int; + r.rec_float = t; + } + + if (RecDataCmp(result->m_token_type, r, recTmp)) { + result->m_token_value = RecDataDiv(result->m_token_type, l, r); + } + break; + + default: + // should never reach here + StatError = true; + } + + return (result); +} + + +/*********************************************************************** + StatObjectList + **********************************************************************/ + +StatObjectList::StatObjectList() + : m_size(0) +{ +} + + +void +StatObjectList::clean() +{ + StatObject *temp = NULL; + + while ((temp = m_statList.dequeue())) { + m_size -= 1; + delete(temp); + } + + ink_assert(m_size == 0); +} + + +void +StatObjectList::enqueue(StatObject * object) +{ + for (StatExprToken * token = object->m_postfix->first(); token; token = object->m_postfix->next(token)) { + if (token->m_token_value_delta) { + object->m_has_delta = true; + break; + } + } + + m_statList.enqueue(object); + m_size += 1; +} + + +StatObject * +StatObjectList::first() +{ + return m_statList.head; +} + + +StatObject * +StatObjectList::next(StatObject * current) +{ + return (current->link).next; +} + + +/** + * StatObjectList::Eval() + * ---------------------- + * The statisitic processor entry point to perform the calculation. + */ +short +StatObjectList::Eval() +{ + RecData tempValue; + RecData result; + RecDataT result_type; + ink_hrtime threshold = 0; + ink_hrtime delta = 0; + short count = 0; + + RecDataClear(RECD_NULL, &tempValue); + RecDataClear(RECD_NULL, &result); + + for (StatObject * object = first(); object; object = next(object)) { + StatError = false; + StatDebug = object->m_debug; + + if (StatDebug) { + Debug(MODULE, "\n##### %d #####\n", object->m_id); + } + + if (object->m_update_interval <= 0) { + // non-time statistics + object->m_current_time = ink_get_hrtime_internal(); + + if (object->m_node_dest) { + result = object->NodeStatEval(&result_type, false); + object->m_node_dest->statVarSet(result_type, result); + } + + if (object->m_cluster_dest) { + result = object->ClusterStatEval(&result_type); + object->m_cluster_dest->statVarSet(result_type, result); + } + + object->m_last_update = object->m_current_time; + } else { + // timed statisitics + object->m_current_time = ink_get_hrtime_internal(); + + threshold = object->m_update_interval * HRTIME_SECOND; + delta = object->m_current_time - object->m_last_update; + + if (StatDebug) { + Debug(MODULE, "\tUPDATE:%" PRId64 " THRESHOLD:%" PRId64 ", DELTA:%" PRId64 "\n", object->m_update_interval, threshold, delta); + } + + /* Should we do the calculation? */ + if ((delta > threshold) || /* sufficient elapsed time? */ + (object->m_last_update == -1) || /* first time? */ + (object->m_last_update > object->m_current_time)) { /*wrapped */ + + if (StatDebug) { + if (delta > threshold) { + Debug(MODULE, "\t\tdelta > threshold IS TRUE!\n"); + } + if (object->m_last_update == -1) { + Debug(MODULE, "\t\tm_last_update = -1 IS TRUE!\n"); + } + if (object->m_last_update > object->m_current_time) { + Debug(MODULE, "\t\tm_last_update > m_current_time IS TRUE\n"); + } + } + + if (!object->m_has_delta) { + + if (StatDebug) { + Debug(MODULE, "\tEVAL: Simple time-condition.\n"); + } + + if (object->m_node_dest) { + result = object->NodeStatEval(&result_type, false); + object->m_node_dest->statVarSet(result_type, result); + } + + if (object->m_cluster_dest) { + result = object->ClusterStatEval(&result_type); + object->m_cluster_dest->statVarSet(result_type, result); + } + + object->m_last_update = object->m_current_time; + } else { + /* has delta */ + if (StatDebug) { + Debug(MODULE, "\tEVAL: Complicated time-condition.\n"); + } + // scroll old values + for (StatExprToken * token = object->m_postfix->first(); token; token = object->m_expression->next(token)) { + + // in librecords, not all statistics are register at initialization + // must assign proper type if it is undefined. + if (!isOperator(token->m_arith_symbol) && token->m_token_type == RECD_NULL) { + token->assignTokenType(); + } + + if (token->m_token_value_delta) { + if (!varDataFromName(token->m_token_type, token->m_token_name, + &tempValue)) { + RecDataClear(RECD_NULL, &tempValue); + } + + token->m_token_value_delta->previous_time = token->m_token_value_delta->current_time; + token->m_token_value_delta->previous_value = token->m_token_value_delta->current_value; + token->m_token_value_delta->current_time = object->m_current_time; + token->m_token_value_delta->current_value = tempValue; + } + } + + if (delta > threshold) { + if (object->m_node_dest) { + result = object->NodeStatEval(&result_type, false); + object->m_node_dest->statVarSet(result_type, result); + } + + if (object->m_cluster_dest) { + result = object->ClusterStatEval(&result_type); + object->m_cluster_dest->statVarSet(result_type, result); + } + + object->m_last_update = object->m_current_time; + } else { + if (StatDebug) { + Debug(MODULE, "\tEVAL: Timer not expired, do nothing\n"); + } + } + } /* delta? */ + } else { + if (StatDebug) { + Debug(MODULE, "\tEVAL: Timer not expired, nor 1st time, nor wrapped, SORRY!\n"); + } + } /* timed event */ + } + count += 1; + } /* for */ + + return count; +} /* Eval() */ + + +/** + * StatObjectList::print() + * -------------------------- + * Print the list of of statistics object in a human-readable format. :) + */ +void +StatObjectList::print(const char *prefix) +{ + for (StatObject * object = first(); object; object = next(object)) { + if (StatDebug) { + Debug(MODULE, "\n%sSTAT OBJECT#: %d\n", prefix, object->m_id); + } + + if (object->m_expression) { + object->m_expression->print("\t"); + } + + if (object->m_postfix) { + object->m_postfix->print("\t"); + } + } +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatType.h ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatType.h b/cmd/traffic_manager/StatType.h new file mode 100644 index 0000000..673e1e7 --- /dev/null +++ b/cmd/traffic_manager/StatType.h @@ -0,0 +1,231 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/***************************************/ +/**************************************************************************** + * + * StatType.h - Functions for computing node and cluster stat + * aggregation + * + * + ****************************************************************************/ + +#ifndef _STATTYPE_H_ +#define _STATTYPE_H_ + +#include "StatXML.h" +#include "Main.h" // Debug() +#include "WebMgmtUtils.h" + +#define BYTES_TO_MBIT_SCALE (8/1000000.0) + +/* Structs used in Average Statistics calculations */ +struct StatDataSamples +{ + ink_hrtime previous_time; + ink_hrtime current_time; + RecDataT data_type; + RecData previous_value; + RecData current_value; + + RecData diff_value(const char *name) + { + RecData tmp; + + if (data_type == RECD_NULL) { + data_type = varType(name); + } + + if (data_type != RECD_NULL) + return RecDataSub(data_type, current_value, previous_value); + else { + RecDataClear(RECD_NULL, &tmp); + return tmp; + } + } + ink_hrtime diff_time() + { + return (current_time - previous_time); + } +}; + +// Urgly workaround -- no optimization in HPUX +#if defined(hpux) +#define inline +#endif + +#define MODULE "StatPro" // Statistics processor debug tag +#define MODULE_INIT "StatProInit" // Statistics processor debug tag + +/*************************************************************** + * StatExprToken + * a statistics expression token can either be a binary operator, + * name '+', '-', '*', '/', or parenthesis '(', ')' or a TS variable. + * In the former case, the arithSymbol stores the operator or + * paranthesis; otherwise arithSymbol is '/0'; + ***************************************************************/ +class StatExprToken +{ + +public: + + char m_arith_symbol; + char *m_token_name; + RecDataT m_token_type; + RecData m_token_value; + RecData m_token_value_max; + RecData m_token_value_min; + StatDataSamples *m_token_value_delta; + bool m_sum_var; + bool m_node_var; + + // Member Functions + void assignTokenName(const char *); + bool assignTokenType(); + void print(const char *); + short precedence(); + void copy(const StatExprToken &); + + LINK(StatExprToken, link); + StatExprToken(); + inline ~ StatExprToken() + { + clean(); + }; + void clean(); + + bool statVarSet(RecDataT, RecData); +}; + + +/** + * StatExprList + * simply a list of StatExprToken. + **/ +class StatExprList +{ + +public: + + StatExprList(); + inline ~ StatExprList() + { + clean(); + }; + void clean(); + + void enqueue(StatExprToken *); + void push(StatExprToken *); + StatExprToken *dequeue(); + StatExprToken *pop(); + StatExprToken *top(); + StatExprToken *first(); + StatExprToken *next(StatExprToken *); + unsigned count(); + void print(const char *); + +private: + + size_t m_size; + Queue<StatExprToken> m_tokenList; +}; + +/*************************************************************** + * StatObject + * Each entry in the statistics XML file is represented by a + * StatObject. + ***************************************************************/ +class StatObject +{ + +public: + + unsigned m_id; + bool m_debug; + char *m_expr_string; /* for debugging using only */ + StatExprToken *m_node_dest; + StatExprToken *m_cluster_dest; + StatExprList *m_expression; + StatExprList *m_postfix; + ink_hrtime m_last_update; + ink_hrtime m_current_time; + ink_hrtime m_update_interval; + RecFloat m_stats_max; + RecFloat m_stats_min; + bool m_has_max; + bool m_has_min; + bool m_has_delta; + LINK(StatObject, link); + + // Member functions + StatObject(); + StatObject(unsigned); + inline ~ StatObject() + { + clean(); + }; + void clean(); + void assignDst(const char *, bool, bool); + void assignExpr(char *); + + StatExprToken *StatBinaryEval(StatExprToken *, char, StatExprToken *, bool cluster = false); + RecData NodeStatEval(RecDataT *result_type, bool cluster); + RecData ClusterStatEval(RecDataT *result_type); + void setTokenValue(StatExprToken *, bool cluster = false); + +private: + + void infix2postfix(); +}; + + +/** + * StatObjectList + * simply a list of StatObject. + **/ +class StatObjectList +{ + +public: + + // Member functions + StatObjectList(); + inline ~ StatObjectList() + { + clean(); + }; + void clean(); + void enqueue(StatObject * object); + StatObject *first(); + StatObject *next(StatObject * current); + void print(const char *prefix = ""); + short Eval(); // return the number of statistics object processed + + size_t m_size; + +private: + + Queue<StatObject> m_statList; +}; + +#endif http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatXML.cc ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatXML.cc b/cmd/traffic_manager/StatXML.cc new file mode 100644 index 0000000..277b84a --- /dev/null +++ b/cmd/traffic_manager/StatXML.cc @@ -0,0 +1,82 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "ink_config.h" +#include "StatXML.h" +#include <stdlib.h> +#include <ctype.h> + +// +// Extract the text between a pair of XML tag and returns the length +// of the extracted text. +// +unsigned short +XML_extractContent(const char *name, char *content, size_t result_len) +{ + + char c; + int contentIndex = 0; + + memset(content, 0, result_len); + for (unsigned short nameIndex = 0; name[nameIndex] != '<'; nameIndex += 1) { + c = name[nameIndex]; + + if (isspace(c)) { + continue; + } + + if (isOperator(c)) { + content[contentIndex++] = ' '; + content[contentIndex++] = c; + content[contentIndex++] = ' '; + } else { + content[contentIndex++] = c; + } + } + + return (strlen(content)); + +} + + +// +// Returns true if 'c'is an operator (in our definition), +// false otherwise +// +bool +isOperator(char c) +{ + + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '(': + case ')': + return true; + default: + return false; + } + +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatXML.h ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/StatXML.h b/cmd/traffic_manager/StatXML.h new file mode 100644 index 0000000..b31063b --- /dev/null +++ b/cmd/traffic_manager/StatXML.h @@ -0,0 +1,47 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#ifndef _STATXML_H_ +#define _STATXML_H_ + +#include "WebMgmtUtils.h" +#include "List.h" + +typedef enum +{ + INVALID_TAG = -1, + ROOT_TAG, + STAT_TAG, + DST_TAG, + EXPR_TAG +} StatXMLTag; + +/*************************************************************** + * General Methods + ***************************************************************/ +bool isOperator(char); +int XML_getContent(const char *, int, char *, StatXMLTag); +unsigned short XML_extractContent(const char *, char *, size_t); + +#endif http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/stats.txt ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/stats.txt b/cmd/traffic_manager/stats.txt new file mode 100644 index 0000000..97bdb90 --- /dev/null +++ b/cmd/traffic_manager/stats.txt @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------- +// +// Statistics Processor +// +// Last Update: 04/11/2001 +// +//----------------------------------------------------------------------------- +// -*- coding: utf-8 -*- +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//----------------------------------------------------------------------------- +// Design +//----------------------------------------------------------------------------- + +This statistics processor is a Cougar II (Tsunami) feature. +It is designed to replace the StatAggregate.cc and portion of WebOverview.cc +for scalability, maintainability, and ability to customize reasons. + +The statistics processor aggregate/calculate traffic server statistics based +on a XML-based configuration file. At the processor's initialization, the +configuration file is read and parsed. Each set of calculation is then stored +as a C++ object, called StatObject; and each StatObject is linked into a list, +called StatObjectList. + +The mgmt/traffic_manager threads will invoke the StatObjectList->Eval() to +perform the statistics aggregation and calculation within its event loop. As +Eval() is call, each StatObject in StatObjectList is evaluated. + +Recall: StatAggregate.cc aggregates/calculates/copies proxy.process.* TS +variables to proxy.node.* variables. Similarly, WebOverview.cc aggregate +proxy.node.* variables to their corresponding proxy.cluster.* variable. + +So there are two types of calculations in the statistics processor: NodeEval +and ClusterEval. As their names imply, they aggregate node-based statistics +and clsuter-based statistics, respectively. We call the different basis of +statistics aggregation as "scope". (See "Destination Attributes") + +In the cluster-based statistics, the aggregation is further divided into two +types: sum and re-calculate. Sum refers calculating the proxy.cluster.* +variable by simply summing all required proxy.node.* variables from nodes in +the cluster. Re-calculate refers to summing all proxy.nodes.* variables that +are used in the process of calculation before performing the calculation. +An analogy would be, summing all open connection in the cluster vs. the +average hit rate in the cluster. + +//----------------------------------------------------------------------------- +// Destination Attributes +//----------------------------------------------------------------------------- + + "scope" + - "node" + - "cluster" + + "operation" + - "sum" + summing the corresponding node variable across all nodes in the cluster. + - "re-calculate" + + + "define" + - "custom" + - "built-in" + +//----------------------------------------------------------------------------- +// Predefined Constants and Functions +//----------------------------------------------------------------------------- + + Predefined Constants + + . BYTES_TO_MB_SCALE (1/(1024*1024.0)) + - convert bytes to mega-bytes + + . MBIT_TO_KBIT_SCALE (1000.0) + - convert mega-bits to kilo-bits + + . SECOND_TO_MILLISECOND_SCALE (1000.0) + - convert seconds to milliseconds + + . PCT_TO_INTPCT_SCALE (100.0) + - convert ratios to percentage + + . HRTIME_SECOND + - converting milli-seconds to seconds + + . BYTES_TO_MBIT_SCALE (8/1000000.0) + - convert bytes to mega-bits + + Predefined Functions + . DIFFTIME + - the number of milliseconds since last update. Usually used in + combination of HRTIME_SECOND which computes the number of seconds + since last update. + +//----------------------------------------------------------------------------- +// Unit test plan +//----------------------------------------------------------------------------- + +The statistics processor is designed to replace StatAggregate.cc and part of +the WebOverview. The first thing to test StatProcessor is to comment the +aggregateNodeRecords() and doClusterAg() calls from mgmt/Main.cc. + +The next step is to replace the above function calls with StatProcessor:: +processStat(). + +This statistics processor is a rather complicated module in traffic manager. +Hence it can't be easily tested. We divided the test into multiple sections. + +1) Node-based Simple Aggregation + - simply performs those aggregation that are node-based and the aggregation + is performed every time statProcess() is invoked. + E.g.: hit rate = doc. hit / doc. served. + +2) Node-based Time-Delta Aggregation + - performs those aggregation that are node-based but the operation is only + perform in a regular interval AND one of more variables in the + calculation is obtained by calculating the difference between the last + updated value and the current value. E.g. average connections per second + is calculated by subtracting the 10 seconds ago connection count from the + current connection count and divide the quotient by 10. + +Repeat the about 2 testes with cluster-based variables. So, we have, at least, +4 test cases. + +Developing a PASS/FAIL unit test that will test the statistics processor is not +cost-efficient. The approach we are going to use is to display the input value +and the output value before and after the calculation is done. + +Let's subdivide the testes in two stages: + +Stage 1 : Synthetic Data +------------------------ +We control the testing environment by setting the input values. This will test +the correctness of the statistics processor in a controlled environment. PASS/ +FAIL is determined by matching the input/output values. + +Stage 2 : Load Data +------------------- +Submitting network traffic through traffic server with load tools like jtest and +ftest, dumps the statistics to a text file, periodically and examines the +resulting values + +//----------------------------------------------------------------------------- +// For QA Engineer +//----------------------------------------------------------------------------- +The most concerning question for QA engineers is "how can I tell if the +Statistics Processor is working correctly?" + +Recall, the new Statistics Processor is meant to replace the StatAggregate.cc +and part of the WebOverview.cc. In essence, you should not see any apparent +change. + +If you ever see a value of -9999.0 (or -9999), then there is an error in +computing that value. + + +<expr> + - %d + - %f + - %k + +<dst> + - specifies the variable that stores that result value. + - ATTRIBUTE: type + built-in: variables that are built-in/defined in traffic server + custom: variables that are introducted to be temporary storage or + variables that are introdcuted by the client. + - default attributes: + type = built-in + +<src> + - variable need to computer the <dst> + - ATTRIBUTE: type + node: this is a proxy.node.* variables + cluster: the is a proxy.node.* variables but summing over all + nodes in the cluster. + - default attributes: + type = node + +<min> + - specifics what is the smallest possible value for <dst>. For values + smaller than <min>, the <defualt> is used. + +<max> + - specifics what is the largest possible value for <dst>. For values + larger than <max>, the <defualt> is used. + +<default> + - specifics what value to be assigned to <dst> is the result <dst> + value is smaller then <min> or larger then <max> + +RULES: (some of these are enfored by the DTD anyways) +- all operator and operand in <expr> MUST BE separated by a single space. +- the order of the tags matters +- the order of each entry matters +- each statistics entry has have at most 1 <dst> + + +DEFINED CONSTANT (in alphabetical order) +* _BYTES_TO_MB_SCALE + * Origin: utils/WebMgmtUtils.h + * Value: (1/(1024*1024.0)) + +* _HRTIME_SECOND + * Origin: + * Value: + +* _MBIT_TO_KBIT_SCALE + * Origin: utils/WebMgmtUtils.h + * Value: (1000.0); + +* _PCT_TO_INTPCT_SCALE + * Origin: utils/WebMgmtUtils.h + * Value: (100.0); + +* _SECOND_TO_MILLISECOND_SCALE + * Origin: utils/WebMgmtUtils.h + * Value: (1000.0); + + + __DIFFTIME http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index 192b967..0915270 100644 --- a/configure.ac +++ b/configure.ac @@ -1890,7 +1890,6 @@ AC_CONFIG_FILES([ mgmt/api/Makefile mgmt/api/include/Makefile mgmt/cluster/Makefile - mgmt/stats/Makefile mgmt/utils/Makefile mgmt/web2/Makefile plugins/Makefile http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/Makefile.am ---------------------------------------------------------------------- diff --git a/mgmt/Makefile.am b/mgmt/Makefile.am index 2b69cce..e76f70d 100644 --- a/mgmt/Makefile.am +++ b/mgmt/Makefile.am @@ -17,7 +17,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SUBDIRS = cluster utils web2 stats api +SUBDIRS = cluster utils web2 api noinst_LIBRARIES = libmgmt_p.a libmgmt_lm.a http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/Makefile.am ---------------------------------------------------------------------- diff --git a/mgmt/stats/Makefile.am b/mgmt/stats/Makefile.am deleted file mode 100644 index cc6c0c0..0000000 --- a/mgmt/stats/Makefile.am +++ /dev/null @@ -1,40 +0,0 @@ -# -# Makefile.am for the Enterprise Management module. -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -AM_CPPFLAGS = \ - -I$(top_srcdir)/lib/records \ - -I$(top_srcdir)/lib/ts \ - -I$(top_srcdir)/mgmt \ - -I$(top_srcdir)/mgmt/cluster \ - -I$(top_srcdir)/mgmt/utils \ - -I$(top_srcdir)/mgmt/api/include \ - -I$(top_srcdir)/mgmt/web2 \ - -I$(top_srcdir)/proxy \ - -I$(top_srcdir)/lib \ - -I$(top_builddir)/lib - -noinst_LIBRARIES = libstats.a - -libstats_a_SOURCES = \ - StatProcessor.cc \ - StatProcessor.h \ - StatType.cc \ - StatType.h \ - StatXML.cc \ - StatXML.h http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/StatProcessor.cc ---------------------------------------------------------------------- diff --git a/mgmt/stats/StatProcessor.cc b/mgmt/stats/StatProcessor.cc deleted file mode 100644 index 24cbaa9..0000000 --- a/mgmt/stats/StatProcessor.cc +++ /dev/null @@ -1,359 +0,0 @@ -/** @file - - A brief file description - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -/***************************************/ -/**************************************************************************** - * - * StatProcessor.cc - Functions for computing node and cluster stat - * aggregation - * - * - ****************************************************************************/ - -#include "ink_config.h" -#include "StatProcessor.h" -#include "FileManager.h" - -#define STAT_CONFIG_FILE "stats.config.xml" - -StatObjectList statObjectList; -StatXMLTag currentTag = INVALID_TAG; -StatObject *statObject = NULL; -char *exprContent = NULL; -static unsigned statCount = 0; // global statistics object counter -bool nodeVar; -bool sumClusterVar; - -// These helpers are used to work around the unsigned char'iness of xmlchar when -// using libxml2. We don't have any tags (now) which uses UTF8. -static int -xml_atoi(const xmlchar *nptr) -{ - return atoi((const char*)nptr); -} - -static double -xml_atof(const xmlchar *nptr) -{ - return atof((const char*)nptr); -} - -static int -xml_strcmp(const xmlchar *s1, const char *s2) -{ - return strcmp((const char *)s1, s2); -} - - -static void -elementStart(void * /* userData ATS_UNUSED */, const xmlchar *name, const xmlchar **atts) -{ - int i = 0; - - if (!xml_strcmp(name, "ink:statistics")) - currentTag = ROOT_TAG; - else if (!xml_strcmp(name, "statistics")) - currentTag = STAT_TAG; - else if (!xml_strcmp(name, "destination")) - currentTag = DST_TAG; - else if (!xml_strcmp(name, "expression")) - currentTag = EXPR_TAG; - else - currentTag = INVALID_TAG; - - switch (currentTag) { - case STAT_TAG: - statObject = new StatObject(++statCount); - Debug(MODULE_INIT, "\nStat #: ----------------------- %d -----------------------\n", statCount); - - if (atts) - for (i = 0; atts[i]; i += 2) { - ink_assert(atts[i + 1]); // Attribute comes in pairs, hopefully. - - if (!xml_strcmp(atts[i], "minimum")) { - statObject->m_stats_min = (MgmtFloat) xml_atof(atts[i + 1]); - statObject->m_has_min = true; - } else if (!xml_strcmp(atts[i], "maximum")) { - statObject->m_stats_max = (MgmtFloat) xml_atof(atts[i + 1]); - statObject->m_has_max = true; - } else if (!xml_strcmp(atts[i], "interval")) { - statObject->m_update_interval = (ink_hrtime) xml_atoi(atts[i + 1]); - } else if (!xml_strcmp(atts[i], "debug")) { - statObject->m_debug = (atts[i + 1] && atts[i + 1][0] == '1'); - } - - Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], atts[i + 1]); - } - break; - - case EXPR_TAG: - exprContent = (char*)ats_malloc(BUFSIZ * 10); - memset(exprContent, 0, BUFSIZ * 10); - break; - - case DST_TAG: - nodeVar = true; - sumClusterVar = true; // Should only be used with cluster variable - - if (atts) - for (i = 0; atts[i]; i += 2) { - ink_assert(atts[i + 1]); // Attribute comes in pairs, hopefully. - if (!xml_strcmp(atts[i], "scope")) { - nodeVar = (!xml_strcmp(atts[i + 1], "node") ? true : false); - } else if (!xml_strcmp(atts[i], "operation")) { - sumClusterVar = (!xml_strcmp(atts[i + 1], "sum") ? true : false); - } - - Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], atts[i + 1]); - } - - break; - - case INVALID_TAG: - Debug(MODULE_INIT, "==========================================>%s<=\n", name); - break; - - default: - break; - } -} - - -static void -elementEnd(void * /* userData ATS_UNUSED */, const xmlchar */* name ATS_UNUSED */) -{ - switch (currentTag) { - case STAT_TAG: - statObjectList.enqueue(statObject); - currentTag = ROOT_TAG; - break; - - case EXPR_TAG: - statObject->assignExpr(exprContent); // This hands over ownership of exprContent - // fall through - - default: - currentTag = STAT_TAG; - break; - } -} - - -static void -charDataHandler(void * /* userData ATS_UNUSED */, const xmlchar * name, int /* len ATS_UNUSED */) -{ - if (currentTag != EXPR_TAG && currentTag != DST_TAG) { - return; - } - - char content[BUFSIZ * 10]; - if (XML_extractContent((const char*)name, content, BUFSIZ * 10) == 0) { - return; - } - - if (currentTag == EXPR_TAG) { - ink_strlcat(exprContent, content, BUFSIZ * 10); // see above for the size - - } else { - statObject->assignDst(content, nodeVar, sumClusterVar); - } -} - - -StatProcessor::StatProcessor(FileManager * configFiles):m_lmgmt(NULL), m_overviewGenerator(NULL) -{ - rereadConfig(configFiles); -} - - -void -StatProcessor::rereadConfig(FileManager * configFiles) -{ - textBuffer *fileContent = NULL; - Rollback *fileRB = NULL; - char *fileBuffer = NULL; - version_t fileVersion; - int fileLen; - - statObjectList.clean(); - statCount = 0; // reset statistics counter - - int ret = configFiles->getRollbackObj(STAT_CONFIG_FILE, &fileRB); - if (!ret) { - Debug(MODULE_INIT, " Can't get Rollback for file: %s\n", STAT_CONFIG_FILE); - } - fileVersion = fileRB->getCurrentVersion(); - fileRB->getVersion(fileVersion, &fileContent); - fileBuffer = fileContent->bufPtr(); - fileLen = strlen(fileBuffer); - -#if HAVE_LIBEXPAT - /* - * Start the XML Praser -- the package used is EXPAT - */ - XML_Parser parser = XML_ParserCreate(NULL); - XML_SetUserData(parser, NULL); - XML_SetElementHandler(parser, elementStart, elementEnd); - XML_SetCharacterDataHandler(parser, charDataHandler); - - /* - * Substiture every newline with a space to get around - * the SetCharacterDataHandler problem. - */ - char *newlinePtr; - while ((newlinePtr = strchr(fileBuffer, '\n')) != NULL || (newlinePtr = strchr(fileBuffer, '\r')) != NULL) { - *newlinePtr = ' '; - } - - /* - * Parse the input file according to XML standard. - * Print error if we encounter any - */ - int status = XML_Parse(parser, fileBuffer, fileLen, true); - if (!status) { - mgmt_log(stderr, "%s at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); - } - - /* - * Cleaning upt - */ - XML_ParserFree(parser); -#else - /* Parse XML with libxml2 */ - xmlSAXHandler sax; - memset(&sax, 0, sizeof(xmlSAXHandler)); - sax.startElement = elementStart; - sax.endElement = elementEnd; - sax.characters = charDataHandler; - sax.initialized = 1; - xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(&sax, NULL, NULL, 0, NULL); - - int status = xmlParseChunk(parser, fileBuffer, fileLen, 1); - if (status != 0) { - xmlErrorPtr errptr = xmlCtxtGetLastError(parser); - mgmt_log(stderr, "%s at %s:%d\n", errptr->message, errptr->file, errptr->line); - } - xmlFreeParserCtxt(parser); -#endif - - - delete fileContent; - - Debug(MODULE_INIT, "\n\n---------- END OF PARSING & INITIALIZING ---------\n\n"); -} - - -StatProcessor::~StatProcessor() -{ - - Debug(MODULE_INIT, "[StatProcessor] Destructing Statistics Processor\n"); - -} - - -void -setTest() -{ - char var_name[64]; - - for (int i = 1; i <= 5; i++) { - memset(var_name, 0, 64); - snprintf(var_name, sizeof(var_name), "proxy.node.stats.test%d", i); - if (i == 4) { - MgmtFloat tmp; - varFloatFromName("proxy.node.stats.test4", &tmp); - varSetFloat(var_name, tmp + 1, true); - } else { - varSetFloat(var_name, i, true); - } - } -} - - -void -verifyTest() -{ - MgmtFloat tmp1, tmp2; - - // 1. simple copy - varFloatFromName("proxy.node.stats.test1", &tmp1); - varFloatFromName("proxy.node.stats.test2", &tmp2); - if (tmp1 == tmp2) { - Debug(MODULE_INIT, "PASS -- simple copy"); - } else { - Debug(MODULE_INIT, "FAIL -- simple copy"); - } - - // 2. simple interval - varFloatFromName("proxy.node.stats.test3", &tmp2); - if (tmp2 >= 10) { - Debug(MODULE_INIT, "PASS -- simple interval & constant"); - } else { - Debug(MODULE_INIT, "FAIL -- simple interval & constant %f", tmp2); - } - - // 3. delta - varFloatFromName("proxy.node.stats.test4", &tmp2); - if ((tmp2 > 150) && (tmp2 < 250)) { - Debug(MODULE_INIT, "PASS -- delta"); - } else { - Debug(MODULE_INIT, "FAIL -- delta %f", tmp2); - } -} - - -/** - * Updating the statistics NOW. - **/ -unsigned short -StatProcessor::processStat() -{ - unsigned short result = 0; - - Debug(MODULE_INIT, "[StatProcessor] Processing Statistics....\n"); - -// setTest(); - statObjectList.Eval(); -// verifyTest(); - - return (result); -} - - -/** - * ExpressionEval - * -------------- - * - */ -RecData -ExpressionEval(char *exprString) -{ - RecDataT result_type; - StatObject statObject; - - char content[BUFSIZ * 10]; - XML_extractContent(exprString, content, BUFSIZ * 10); - - statObject.assignExpr(content); - return statObject.NodeStatEval(&result_type, false); -} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/StatProcessor.h ---------------------------------------------------------------------- diff --git a/mgmt/stats/StatProcessor.h b/mgmt/stats/StatProcessor.h deleted file mode 100644 index 71dab2f..0000000 --- a/mgmt/stats/StatProcessor.h +++ /dev/null @@ -1,94 +0,0 @@ -/** @file - - A brief file description - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#ifndef _STAT_PROCESSOR_H_ -#define _STAT_PROCESSOR_H_ - -/**************************************************************************** - * - * StatProcessor.h - Functions for computing node and cluster stat - * aggregation - * - * - ****************************************************************************/ - -#include "ink_platform.h" -#include <stdarg.h> -#include "MgmtUtils.h" -#include "MgmtDefs.h" -#include "WebMgmtUtils.h" -#include "ink_hrtime.h" -#include "LocalManager.h" -#include "WebOverview.h" - -#define _HEADER -#define _D(x) -#define _FOOTER -#include "DynamicStats.h" -#include "StatType.h" - -#if HAVE_LIBEXPAT -#include "expat.h" -typedef XML_Char xmlchar; -#elif HAVE_LIBXML2 -#include <libxml/parser.h> -#include <libxml/SAX.h> -typedef xmlChar xmlchar; -#else -# error "No XML parser - please configure expat or libxml2" -#endif - -#include <string.h> -#include <stdlib.h> - -class StatProcessor -{ -public: - - explicit StatProcessor(FileManager * configFiles); - ~StatProcessor(); - - // Member Fuctions - unsigned short processStat(); - void rereadConfig(FileManager * configFiles); - - LocalManager *m_lmgmt; - overviewPage *m_overviewGenerator; -}; - - -/** - * External expression evaluation API. - * - * INPUT: an expression string, e.g.: - * "(proxy.node.user_agent_total_bytes-proxy.node.origin_server_total_bytes) - * / proxy.node.user_agent_total_bytes" - * - * RETURN: the resulting value of the expression. - * NOTE: it returns -9999.0 if there is an error. - * - */ - -RecData ExpressionEval(char *); - -#endif