Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDmitrijs Goloscapovs <dmitrijs.goloscapovs@zabbix.com>2021-05-07 11:05:23 +0300
committerDmitrijs Goloscapovs <dmitrijs.goloscapovs@zabbix.com>2021-05-07 11:05:23 +0300
commitf16d6d7611d80be18b522afa6ac1c978b3e7679e (patch)
tree2d332fbf7e8bfaaf90b6c6d172d8334cd491f548 /src
parent42036f98c37f8cfe6f945ccdb15e3da5118fe90b (diff)
parentb0e412a0f3e098cf67b3b77edd78bdadcb17b14e (diff)
........S. [ZBXNEXT-6544] fixed merge conflict (from 6547)
Diffstat (limited to 'src')
-rw-r--r--src/libs/zbxeval/Makefile.am3
-rw-r--r--src/libs/zbxeval/calc.c494
-rw-r--r--src/libs/zbxeval/execute.c88
-rw-r--r--src/libs/zbxserver/evalfunc2.c429
4 files changed, 980 insertions, 34 deletions
diff --git a/src/libs/zbxeval/Makefile.am b/src/libs/zbxeval/Makefile.am
index c6d4dc4c93a..d04ae19b526 100644
--- a/src/libs/zbxeval/Makefile.am
+++ b/src/libs/zbxeval/Makefile.am
@@ -6,4 +6,5 @@ libzbxeval_a_SOURCES = \
parse.c \
execute.c \
misc.c \
- query.c
+ query.c \
+ calc.c
diff --git a/src/libs/zbxeval/calc.c b/src/libs/zbxeval/calc.c
new file mode 100644
index 00000000000..e3a9b52370e
--- /dev/null
+++ b/src/libs/zbxeval/calc.c
@@ -0,0 +1,494 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2020 Zabbix SIA
+**
+** 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.
+**/
+
+#include "common.h"
+#include "zbxalgo.h"
+#include "zbxeval.h"
+
+static int zbx_is_normal_double(double dbl)
+{
+ if (FP_ZERO != fpclassify(dbl) && FP_NORMAL != fpclassify(dbl))
+ return FAIL;
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: calc_arithmetic_mean *
+ * *
+ * Purpose: calculate arithmetic mean (i.e. average) *
+ * *
+ * Parameters: v - [IN] non-empty vector with input data *
+ * *
+ * Return value: arithmetic mean value *
+ * *
+ ******************************************************************************/
+static double calc_arithmetic_mean(const zbx_vector_dbl_t *v)
+{
+ double sum = 0;
+ int i;
+
+ for (i = 0; i < v->values_num; i++)
+ sum += v->values[i];
+
+ return sum / v->values_num;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_kurtosis *
+ * *
+ * Purpose: evaluate function 'kurtosis' *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_kurtosis(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, second_moment = 0, fourth_moment = 0, second_moment2, res;
+ int i;
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the second and the fourth moments */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ second_moment += diff * diff;
+ fourth_moment += diff * diff * diff * diff;
+ }
+
+ second_moment /= values->values_num;
+ fourth_moment /= values->values_num;
+
+ /* step 3: calculate kurtosis */
+
+ second_moment2 = second_moment * second_moment;
+
+ if (FP_NORMAL != fpclassify(second_moment2) || SUCCEED != zbx_is_normal_double(fourth_moment))
+ goto err;
+
+ res = fourth_moment / second_moment2;
+
+ if (SUCCEED != zbx_is_normal_double(res))
+ goto err;
+
+ *result = res;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate kurtosis() value");
+
+ return FAIL;
+}
+
+static int zbx_vector_dbl_compare(const void *d1, const void *d2)
+{
+ const double *p1 = (const double *)d1;
+ const double *p2 = (const double *)d2;
+
+ ZBX_RETURN_IF_NOT_EQUAL(*p1, *p2);
+
+ return 0;
+}
+
+/******************************************************************************
+ * *
+ * Function: find_median *
+ * *
+ * Purpose: find median (helper function) *
+ * *
+ * Parameters: v - [IN/OUT] non-empty vector with input data. *
+ * NOTE: it will be modified (sorted in place). *
+ * *
+ * Return value: median *
+ * *
+ ******************************************************************************/
+static double find_median(zbx_vector_dbl_t *v)
+{
+ zbx_vector_dbl_sort(v, zbx_vector_dbl_compare);
+
+ if (0 == v->values_num % 2) /* number of elements is even */
+ return (v->values[v->values_num / 2 - 1] + v->values[v->values_num / 2]) / 2.0;
+ else
+ return v->values[v->values_num / 2];
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_mad *
+ * *
+ * Purpose: calculate 'median absolute deviation' *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data. *
+ * NOTE: its elements will be modified and should *
+ * not be used in the caller! *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_mad(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double median;
+ int i;
+
+ /* step 1: find median of input data */
+ median = find_median(values);
+
+ if (SUCCEED != zbx_is_normal_double(median))
+ goto err;
+
+ /* step 2: find absolute differences of input data and median. Reuse input data vector. */
+
+ for (i = 0; i < values->values_num; i++)
+ values->values[i] = fabs(values->values[i] - median);
+
+ /* step 3: find median of the differences */
+ median = find_median(values);
+
+ if (SUCCEED != zbx_is_normal_double(median))
+ goto err;
+
+ *result = median;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate mad() value");
+
+ return FAIL;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_skewness *
+ * *
+ * Purpose: evaluate 'skewness' function *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_skewness(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, std_dev = 0, sum_diff3 = 0, divisor;
+ int i;
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the standard deviation and sum_diff3 */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ std_dev += diff * diff;
+ sum_diff3 += diff * diff * diff;
+ }
+
+ std_dev = sqrt(std_dev / values->values_num);
+
+ /* step 3: calculate skewness */
+
+ divisor = values->values_num * std_dev * std_dev * std_dev;
+
+ if (FP_NORMAL != fpclassify(divisor) || SUCCEED != zbx_is_normal_double(sum_diff3))
+ goto err;
+
+ *result = sum_diff3 / divisor;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate skewness() value");
+
+ return FAIL;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_stddevpop *
+ * *
+ * Purpose: evaluate function 'stdevpop' (population standard deviation) *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ * Comments: the algorithm was taken from "Population standard deviation of *
+ * grades of eight students" in *
+ * https://en.wikipedia.org/wiki/Standard_deviation *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_stddevpop(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, std_dev = 0;
+ int i;
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the standard deviation */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ std_dev += diff * diff;
+ }
+
+ std_dev = sqrt(std_dev / values->values_num);
+
+ if (SUCCEED != zbx_is_normal_double(std_dev))
+ goto err;
+
+ *result = std_dev;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate stddevpop() value");
+
+ return FAIL;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_stddevsamp *
+ * *
+ * Purpose: evaluate function 'stddevsamp' (sample standard deviation) *
+ * *
+ * Parameters: values - [IN] vector with input data with at least 2 elements *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ * Comments: the algorithm was taken from "Population standard deviation of *
+ * grades of eight students" in *
+ * https://en.wikipedia.org/wiki/Standard_deviation *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_stddevsamp(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, std_dev = 0;
+ int i;
+
+ if (2 > values->values_num) /* stddevsamp requires at least 2 data values */
+ {
+ *error = zbx_strdup(*error, "not enough data");
+ return FAIL;
+ }
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the standard deviation */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ std_dev += diff * diff;
+ }
+
+ std_dev = sqrt(std_dev / (values->values_num - 1)); /* divided by 'n - 1' because */
+ /* sample standard deviation */
+ if (SUCCEED != zbx_is_normal_double(std_dev))
+ goto err;
+
+ *result = std_dev;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate stddevsamp() value");
+
+ return FAIL;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_sumofsquares *
+ * *
+ * Purpose: calculate sum of squares *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_sumofsquares(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double sum = 0;
+ int i;
+
+ for (i = 0; i < values->values_num; i++)
+ sum += values->values[i] * values->values[i];
+
+ if (SUCCEED != zbx_is_normal_double(sum))
+ {
+ *error = zbx_strdup(*error, "cannot calculate sumofsquares() value");
+ return FAIL;
+ }
+
+ *result = sum;
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_varpop *
+ * *
+ * Purpose: evaluate function 'varpop' (population variance) *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ * Comments: the algorithm was taken from "Population variance" in *
+ * https://en.wikipedia.org/wiki/Variance#Population_variance *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_varpop(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, res = 0;
+ int i;
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the population variance */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ res += diff * diff;
+ }
+
+ res /= values->values_num; /* divide by 'number of values' for population variance */
+
+ if (SUCCEED != zbx_is_normal_double(res))
+ goto err;
+
+ *result = res;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate varpop() value");
+
+ return FAIL;
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_eval_calc_varsamp *
+ * *
+ * Purpose: evaluate function 'varsamp' (sample variance) *
+ * *
+ * Parameters: values - [IN] non-empty vector with input data *
+ * result - [OUT] calculated value *
+ * error - [OUT] dynamically allocated error message *
+ * *
+ * Return value: SUCCEED - evaluated successfully *
+ * FAIL - failed to evaluate function (see 'error') *
+ * *
+ * Comments: the algorithm was taken from "Sample variance" in *
+ * https://en.wikipedia.org/wiki/Variance#Population_variance *
+ * *
+ ******************************************************************************/
+int zbx_eval_calc_varsamp(zbx_vector_dbl_t *values, double *result, char **error)
+{
+ double mean, res = 0;
+ int i;
+
+ if (2 > values->values_num) /* varsamp requires at least 2 data values */
+ {
+ *error = zbx_strdup(*error, "not enough data");
+ return FAIL;
+ }
+
+ /* step 1: calculate arithmetic mean */
+ mean = calc_arithmetic_mean(values);
+
+ if (SUCCEED != zbx_is_normal_double(mean))
+ goto err;
+
+ /* step 2: calculate the sample variance */
+
+ for (i = 0; i < values->values_num; i++)
+ {
+ double diff = values->values[i] - mean;
+
+ res += diff * diff;
+ }
+
+ res /= values->values_num - 1; /* divide by 'number of values' - 1 for unbiased sample variance */
+
+ if (SUCCEED != zbx_is_normal_double(res))
+ goto err;
+
+ *result = res;
+
+ return SUCCEED;
+err:
+ *error = zbx_strdup(*error, "cannot calculate varsamp() value");
+
+ return FAIL;
+}
diff --git a/src/libs/zbxeval/execute.c b/src/libs/zbxeval/execute.c
index cb1249d8036..603ed06c279 100644
--- a/src/libs/zbxeval/execute.c
+++ b/src/libs/zbxeval/execute.c
@@ -1402,6 +1402,78 @@ static int eval_execute_function_left(const zbx_eval_context_t *ctx, const zbx_e
return SUCCEED;
}
+static int eval_validate_statistical_function_args(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token,
+ zbx_vector_var_t *output, char **error)
+{
+ int i, ret;
+
+ if (UNKNOWN != (ret = eval_validate_function_args(ctx, token, output, error)))
+ return ret;
+
+ if (1 != token->opt)
+ {
+ *error = zbx_dsprintf(*error, "invalid number of arguments for function at \"%s\"",
+ ctx->expression + token->loc.l);
+ return FAIL;
+ }
+
+ i = output->values_num - 1;
+
+ if (ZBX_VARIANT_DBL_VECTOR != output->values[i].type)
+ {
+ *error = zbx_dsprintf(*error, "invalid argument type \"%s\" for function at \"%s\"",
+ zbx_variant_type_desc(&output->values[i]), ctx->expression + token->loc.l);
+ return FAIL;
+ }
+
+ if (0 == output->values[i].data.dbl_vector->values_num)
+ {
+ *error = zbx_dsprintf(*error, "empty vector argument for function at \"%s\"",
+ ctx->expression + token->loc.l);
+ return FAIL;
+ }
+
+ return UNKNOWN;
+}
+
+/******************************************************************************
+ * *
+ * Function: eval_execute_statistical_function *
+ * *
+ * Purpose: common operations for aggregate function calculation *
+ * *
+ * Parameters: ctx - [IN] the evaluation context *
+ * token - [IN] the function token *
+ * stat_func - [IN] pointer to aggregate function to be called *
+ * output - [IN/OUT] the output value stack *
+ * error - [OUT] the error message in the case of failure *
+ * *
+ * Return value: SUCCEED - function evaluation succeeded *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int eval_execute_statistical_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token,
+ zbx_statistical_func_t stat_func, zbx_vector_var_t *output, char **error)
+{
+ int ret;
+ double result;
+ zbx_variant_t value;
+ zbx_vector_dbl_t *dbl_vector;
+
+ if (UNKNOWN != (ret = eval_validate_statistical_function_args(ctx, token, output, error)))
+ return ret;
+
+ dbl_vector = output->values[output->values_num - (int)token->opt].data.dbl_vector;
+
+ if (FAIL == stat_func(dbl_vector, &result, error))
+ return FAIL;
+
+ zbx_variant_set_dbl(&value, result);
+ eval_function_return((int)token->opt, &value, output);
+
+ return SUCCEED;
+}
+
/******************************************************************************
* *
* Function: eval_execute_function_right *
@@ -2632,6 +2704,22 @@ static int eval_execute_common_function(const zbx_eval_context_t *ctx, const zbx
return eval_execute_math_return_value(ctx, token, output, error, ZBX_MATH_CONST_E);
if (SUCCEED == eval_compare_token(ctx, &token->loc, "rand", ZBX_CONST_STRLEN("rand")))
return eval_execute_math_return_value(ctx, token, output, error, ZBX_MATH_RANDOM);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "kurtosis", ZBX_CONST_STRLEN("kurtosis")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_kurtosis, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "mad", ZBX_CONST_STRLEN("mad")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_mad, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "skewness", ZBX_CONST_STRLEN("skewness")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_skewness, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "stddevpop", ZBX_CONST_STRLEN("stddevpop")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_stddevpop, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "stddevsamp", ZBX_CONST_STRLEN("stddevsamp")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_stddevsamp, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "sumofsquares", ZBX_CONST_STRLEN("sumofsquares")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_sumofsquares, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "varpop", ZBX_CONST_STRLEN("varpop")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_varpop, output, error);
+ if (SUCCEED == eval_compare_token(ctx, &token->loc, "varsamp", ZBX_CONST_STRLEN("varsamp")))
+ return eval_execute_statistical_function(ctx, token, zbx_eval_calc_varsamp, output, error);
if (NULL != ctx->common_func_cb)
return eval_execute_cb_function(ctx, token, ctx->common_func_cb, output, error);
diff --git a/src/libs/zbxserver/evalfunc2.c b/src/libs/zbxserver/evalfunc2.c
index 24bee12b429..6ce6149f237 100644
--- a/src/libs/zbxserver/evalfunc2.c
+++ b/src/libs/zbxserver/evalfunc2.c
@@ -585,6 +585,93 @@ out:
return ret;
}
+static int history_record_float_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
+{
+ ZBX_RETURN_IF_NOT_EQUAL(d1->value.dbl, d2->value.dbl);
+
+ return 0;
+}
+
+static int history_record_uint64_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
+{
+ ZBX_RETURN_IF_NOT_EQUAL(d1->value.ui64, d2->value.ui64);
+
+ return 0;
+}
+
+static int history_record_str_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
+{
+ return strcmp(d1->value.str, d2->value.str);
+}
+
+static int history_record_log_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
+{
+ int value_match;
+
+ if (0 != (value_match = strcmp(d1->value.log->value, d2->value.log->value)))
+ return value_match;
+
+ if (NULL != d1->value.log->source && NULL != d2->value.log->source)
+ return strcmp(d1->value.log->source, d2->value.log->source);
+
+ if (NULL != d2->value.log->source)
+ return -1;
+
+ if (NULL != d1->value.log->source)
+ return 1;
+
+ return 0;
+}
+
+/* specialized versions of zbx_vector_history_record_*_uniq() because */
+/* standard versions do not release memory occupied by duplicate elements */
+
+static void zbx_vector_history_record_str_uniq(zbx_vector_history_record_t *vector, zbx_compare_func_t compare_func)
+{
+ if (2 <= vector->values_num)
+ {
+ int i = 0, j = 1;
+
+ while (j < vector->values_num)
+ {
+ if (0 != compare_func(&vector->values[i], &vector->values[j]))
+ {
+ i++;
+ j++;
+ }
+ else
+ {
+ zbx_free(vector->values[j].value.str);
+ zbx_vector_history_record_remove(vector, j);
+ }
+ }
+ }
+}
+
+static void zbx_vector_history_record_log_uniq(zbx_vector_history_record_t *vector, zbx_compare_func_t compare_func)
+{
+ if (2 <= vector->values_num)
+ {
+ int i = 0, j = 1;
+
+ while (j < vector->values_num)
+ {
+ if (0 != compare_func(&vector->values[i], &vector->values[j]))
+ {
+ i++;
+ j++;
+ }
+ else
+ {
+ zbx_free(vector->values[j].value.log->source);
+ zbx_free(vector->values[j].value.log->value);
+ zbx_free(vector->values[j].value.log);
+ zbx_vector_history_record_remove(vector, j);
+ }
+ }
+ }
+}
+
#define OP_UNKNOWN -1
#define OP_EQ 0
#define OP_NE 1
@@ -693,6 +780,10 @@ static void count_one_str(int *count, int op, const char *value, const char *pat
}
}
+/* flags for evaluate_COUNT() */
+#define COUNT_ALL 0
+#define COUNT_UNIQUE 1
+
/******************************************************************************
* *
* Function: evaluate_COUNT *
@@ -713,6 +804,8 @@ static void count_one_str(int *count, int op, const char *value, const char *pat
* ts - [IN] the function evaluation time *
* limit - [IN] the limit of counted values, will return *
* when the limit is reached *
+ * unique - [IN] COUNT_ALL - count all values, *
+ * COUNT_UNIQUE - count unique values *
* error - [OUT] the error message *
* *
* Return value: SUCCEED - evaluated successfully, result is stored in 'value'*
@@ -720,7 +813,7 @@ static void count_one_str(int *count, int op, const char *value, const char *pat
* *
******************************************************************************/
static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts,
- int limit, char **error)
+ int limit, int unique, char **error)
{
int arg1, op = OP_UNKNOWN, numeric_search, nparams, count = 0, i, ret = FAIL;
int seconds = 0, nvalues = 0, time_shift;
@@ -916,6 +1009,36 @@ static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *param
goto out;
}
+ if (COUNT_UNIQUE == unique)
+ {
+ switch (item->value_type)
+ {
+ case ITEM_VALUE_TYPE_UINT64:
+ zbx_vector_history_record_sort(&values,
+ (zbx_compare_func_t)history_record_uint64_compare);
+ zbx_vector_history_record_uniq(&values,
+ (zbx_compare_func_t)history_record_uint64_compare);
+ break;
+ case ITEM_VALUE_TYPE_FLOAT:
+ zbx_vector_history_record_sort(&values,
+ (zbx_compare_func_t)history_record_float_compare);
+ zbx_vector_history_record_uniq(&values,
+ (zbx_compare_func_t)history_record_float_compare);
+ break;
+ case ITEM_VALUE_TYPE_LOG:
+ zbx_vector_history_record_sort(&values,
+ (zbx_compare_func_t)history_record_log_compare);
+ zbx_vector_history_record_log_uniq(&values,
+ (zbx_compare_func_t)history_record_log_compare);
+ break;
+ default:
+ zbx_vector_history_record_sort(&values,
+ (zbx_compare_func_t)history_record_str_compare);
+ zbx_vector_history_record_str_uniq(&values,
+ (zbx_compare_func_t)history_record_str_compare);
+ }
+ }
+
/* skip counting values one by one if both pattern and operator are empty or "" is searched in text values */
if ((NULL != pattern && '\0' != *pattern) || (NULL != operator && '\0' != *operator &&
OP_LIKE != op && OP_REGEXP != op && OP_IREGEXP != op))
@@ -1022,7 +1145,7 @@ out:
******************************************************************************/
static int evaluate_SUM(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error)
{
- int nparams, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift;
+ int arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift;
zbx_value_type_t arg1_type;
zbx_vector_history_record_t values;
history_value_t result;
@@ -1038,7 +1161,7 @@ static int evaluate_SUM(zbx_variant_t *value, DC_ITEM *item, const char *paramet
goto out;
}
- if (1 != (nparams = num_param(parameters)))
+ if (1 != num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto out;
@@ -1111,7 +1234,7 @@ out:
******************************************************************************/
static int evaluate_AVG(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error)
{
- int nparams, arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift;
+ int arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift;
zbx_value_type_t arg1_type;
zbx_vector_history_record_t values;
zbx_timespec_t ts_end = *ts;
@@ -1126,7 +1249,7 @@ static int evaluate_AVG(zbx_variant_t *value, DC_ITEM *item, const char *parame
goto out;
}
- if (1 != (nparams = num_param(parameters)))
+ if (1 != num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto out;
@@ -1240,7 +1363,7 @@ static int evaluate_LAST(zbx_variant_t *value, DC_ITEM *item, const char *parame
******************************************************************************/
static int evaluate_MIN(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error)
{
- int nparams, arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift;
+ int arg1, i, ret = FAIL, seconds = 0, nvalues = 0, time_shift;
zbx_value_type_t arg1_type;
zbx_vector_history_record_t values;
zbx_timespec_t ts_end = *ts;
@@ -1255,7 +1378,7 @@ static int evaluate_MIN(zbx_variant_t *value, DC_ITEM *item, const char *paramet
goto out;
}
- if (1 != (nparams = num_param(parameters)))
+ if (1 != num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto out;
@@ -1340,7 +1463,7 @@ out:
******************************************************************************/
static int evaluate_MAX(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts, char **error)
{
- int nparams, arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift;
+ int arg1, ret = FAIL, i, seconds = 0, nvalues = 0, time_shift;
zbx_value_type_t arg1_type;
zbx_vector_history_record_t values;
zbx_timespec_t ts_end = *ts;
@@ -1355,7 +1478,7 @@ static int evaluate_MAX(zbx_variant_t *value, DC_ITEM *item, const char *paramet
goto out;
}
- if (1 != (nparams = num_param(parameters)))
+ if (1 != num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto out;
@@ -1426,20 +1549,6 @@ out:
return ret;
}
-static int __history_record_float_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
-{
- ZBX_RETURN_IF_NOT_EQUAL(d1->value.dbl, d2->value.dbl);
-
- return 0;
-}
-
-static int __history_record_uint64_compare(const zbx_history_record_t *d1, const zbx_history_record_t *d2)
-{
- ZBX_RETURN_IF_NOT_EQUAL(d1->value.ui64, d2->value.ui64);
-
- return 0;
-}
-
/******************************************************************************
* *
* Function: evaluate_PERCENTILE *
@@ -1458,7 +1567,7 @@ static int __history_record_uint64_compare(const zbx_history_record_t *d1, const
static int evaluate_PERCENTILE(zbx_variant_t *value, DC_ITEM *item, const char *parameters,
const zbx_timespec_t *ts, char **error)
{
- int nparams, arg1, time_shift, ret = FAIL, seconds = 0, nvalues = 0;
+ int arg1, time_shift, ret = FAIL, seconds = 0, nvalues = 0;
zbx_value_type_t arg1_type;
double percentage;
zbx_vector_history_record_t values;
@@ -1474,7 +1583,7 @@ static int evaluate_PERCENTILE(zbx_variant_t *value, DC_ITEM *item, const char
goto out;
}
- if (2 != (nparams = num_param(parameters)))
+ if (2 != num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto out;
@@ -1520,9 +1629,9 @@ static int evaluate_PERCENTILE(zbx_variant_t *value, DC_ITEM *item, const char
int index;
if (ITEM_VALUE_TYPE_FLOAT == item->value_type)
- zbx_vector_history_record_sort(&values, (zbx_compare_func_t)__history_record_float_compare);
+ zbx_vector_history_record_sort(&values, (zbx_compare_func_t)history_record_float_compare);
else
- zbx_vector_history_record_sort(&values, (zbx_compare_func_t)__history_record_uint64_compare);
+ zbx_vector_history_record_sort(&values, (zbx_compare_func_t)history_record_uint64_compare);
if (0 == percentage)
index = 1;
@@ -1833,7 +1942,7 @@ static int evaluate_BITAND(zbx_variant_t *value, DC_ITEM *item, const char *para
char **error)
{
char *last_parameters = NULL;
- int nparams, ret = FAIL;
+ int ret = FAIL;
zbx_uint64_t mask;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
@@ -1844,7 +1953,7 @@ static int evaluate_BITAND(zbx_variant_t *value, DC_ITEM *item, const char *para
goto clean;
}
- if (2 < (nparams = num_param(parameters)))
+ if (2 < num_param(parameters))
{
*error = zbx_strdup(*error, "invalid number of parameters");
goto clean;
@@ -2269,6 +2378,219 @@ out:
return ret;
}
+static int validate_params_and_get_data(DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts,
+ zbx_vector_history_record_t *values, char **error)
+{
+ int arg1, seconds = 0, nvalues = 0, time_shift;
+ zbx_value_type_t arg1_type;
+ zbx_timespec_t ts_end = *ts;
+
+ if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type)
+ {
+ *error = zbx_strdup(*error, "invalid value type");
+ return FAIL;
+ }
+
+ if (1 != num_param(parameters))
+ {
+ *error = zbx_strdup(*error, "invalid number of parameters");
+ return FAIL;
+ }
+
+ if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift) ||
+ ZBX_VALUE_NONE == arg1_type)
+ {
+ *error = zbx_strdup(*error, "invalid parameter");
+ return FAIL;
+ }
+
+ ts_end.sec -= time_shift;
+
+ switch (arg1_type)
+ {
+ case ZBX_VALUE_SECONDS:
+ seconds = arg1;
+ break;
+ case ZBX_VALUE_NVALUES:
+ nvalues = arg1;
+ break;
+ case ZBX_VALUE_NONE:
+ default:
+ *error = zbx_strdup(*error, "invalid type of first argument");
+ THIS_SHOULD_NEVER_HAPPEN;
+ return FAIL;
+ }
+
+ if (FAIL == zbx_vc_get_values(item->itemid, item->value_type, values, seconds, nvalues, &ts_end))
+ {
+ *error = zbx_strdup(*error, "cannot get values from value cache");
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Function: evaluate_FIRST *
+ * *
+ * Purpose: evaluate function 'first' for the item *
+ * *
+ * Parameters: value - dynamic buffer *
+ * item - item (performance metric) *
+ * parameters - Nth first value and time shift (optional) *
+ * *
+ * Return value: SUCCEED - evaluated successfully, result is stored in 'value'*
+ * FAIL - failed to evaluate function *
+ * *
+ ******************************************************************************/
+static int evaluate_FIRST(zbx_variant_t *value, DC_ITEM *item, const char *parameters, const zbx_timespec_t *ts,
+ char **error)
+{
+ int arg1 = 1, ret = FAIL, seconds = 0, time_shift;
+ zbx_value_type_t arg1_type = ZBX_VALUE_NVALUES;
+ zbx_vector_history_record_t values;
+ zbx_timespec_t ts_end = *ts;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_history_record_vector_create(&values);
+
+ if (1 != num_param(parameters))
+ {
+ *error = zbx_strdup(*error, "invalid number of parameters");
+ goto out;
+ }
+
+ if (SUCCEED != get_function_parameter_hist_range(ts->sec, parameters, 1, &arg1, &arg1_type, &time_shift))
+ {
+ *error = zbx_strdup(*error, "invalid parameter");
+ goto out;
+ }
+
+ switch (arg1_type)
+ {
+ case ZBX_VALUE_SECONDS:
+ seconds = arg1;
+ break;
+ case ZBX_VALUE_NONE:
+ *error = zbx_strdup(*error, "the first argument is not specified");
+ goto out;
+ case ZBX_VALUE_NVALUES:
+ *error = zbx_strdup(*error, "the first argument cannot be number of value");
+ goto out;
+ default:
+ *error = zbx_strdup(*error, "invalid type of first argument");
+ THIS_SHOULD_NEVER_HAPPEN;
+ goto out;
+ }
+
+ if (0 >= arg1)
+ {
+ *error = zbx_strdup(*error, "the first argument must be greater than 0");
+ goto out;
+ }
+
+ ts_end.sec -= time_shift;
+
+ if (SUCCEED == zbx_vc_get_values(item->itemid, item->value_type, &values, seconds, 0, &ts_end))
+ {
+ if (0 < values.values_num)
+ {
+ zbx_history_value2variant(&values.values[values.values_num - 1].value, item->value_type, value);
+ ret = SUCCEED;
+ }
+ else
+ {
+ *error = zbx_strdup(*error, "not enough data");
+ goto out;
+ }
+ }
+ else
+ *error = zbx_strdup(*error, "cannot get values from value cache");
+out:
+ zbx_history_record_vector_destroy(&values, item->value_type);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
+static void history_to_dbl_vector(const zbx_history_record_t *v, int n, unsigned char value_type,
+ zbx_vector_dbl_t *values)
+{
+ int i;
+
+ zbx_vector_dbl_reserve(values, (size_t)n);
+
+ if (ITEM_VALUE_TYPE_FLOAT == value_type)
+ {
+ for (i = 0; i < n; i++)
+ zbx_vector_dbl_append(values, v[i].value.dbl);
+ }
+ else
+ {
+ for (i = 0; i < n; i++)
+ zbx_vector_dbl_append(values, (double)v[i].value.ui64);
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: evaluate_statistical_func *
+ * *
+ * Purpose: common operations for aggregate function calculation *
+ * *
+ * Parameters: value - [OUT] result *
+ * item - [IN] item (performance metric) *
+ * parameters - [IN] number of seconds/values and time shift *
+ * (optional) *
+ * ts - [IN] time shift *
+ * stat_func - [IN] pointer to aggregate function to be called *
+ * min_values - [IN] minimum data values required *
+ * error - [OUT] the error message in the case of failure *
+ * *
+ * Return value: SUCCEED - evaluated successfully, result is stored in 'value'*
+ * FAIL - failed to evaluate function *
+ * *
+ ******************************************************************************/
+static int evaluate_statistical_func(zbx_variant_t *value, DC_ITEM *item, const char *parameters,
+ const zbx_timespec_t *ts, zbx_statistical_func_t stat_func, int min_values, char **error)
+{
+ int ret = FAIL;
+ zbx_vector_history_record_t values;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_history_record_vector_create(&values);
+
+ if (SUCCEED != validate_params_and_get_data(item, parameters, ts, &values, error))
+ goto out;
+
+ if (min_values <= values.values_num)
+ {
+ zbx_vector_dbl_t values_dbl;
+ double result;
+
+ zbx_vector_dbl_create(&values_dbl);
+
+ history_to_dbl_vector(values.values, values.values_num, item->value_type, &values_dbl);
+
+ if (SUCCEED == (ret = stat_func(&values_dbl, &result, error)))
+ zbx_variant_set_dbl(value, result);
+
+ zbx_vector_dbl_destroy(&values_dbl);
+ }
+ else
+ *error = zbx_strdup(*error, "not enough data");
+out:
+ zbx_history_record_vector_destroy(&values, item->value_type);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+}
+
/******************************************************************************
* *
* Function: evaluate_function *
@@ -2317,7 +2639,11 @@ int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function
}
else if (0 == strcmp(function, "count"))
{
- ret = evaluate_COUNT(value, item, parameter, ts, ZBX_MAX_UINT31_1, error);
+ ret = evaluate_COUNT(value, item, parameter, ts, ZBX_MAX_UINT31_1, COUNT_ALL, error);
+ }
+ else if (0 == strcmp(function, "countunique"))
+ {
+ ret = evaluate_COUNT(value, item, parameter, ts, ZBX_MAX_UINT31_1, COUNT_UNIQUE, error);
}
else if (0 == strcmp(function, "nodata"))
{
@@ -2329,7 +2655,7 @@ int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function
}
else if (0 == strcmp(function, "find"))
{
- ret = evaluate_COUNT(value, item, parameter, ts, 1, error);
+ ret = evaluate_COUNT(value, item, parameter, ts, 1, COUNT_ALL, error);
}
else if (0 == strcmp(function, "fuzzytime"))
{
@@ -2363,6 +2689,42 @@ int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function
{
ret = evaluate_TREND(value, item, function + 5, parameter, ts, error);
}
+ else if (0 == strcmp(function, "first"))
+ {
+ ret = evaluate_FIRST(value, item, parameter, ts, error);
+ }
+ else if (0 == strcmp(function, "kurtosis"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_kurtosis, 1, error);
+ }
+ else if (0 == strcmp(function, "mad"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_mad, 1, error);
+ }
+ else if (0 == strcmp(function, "skewness"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_skewness, 1, error);
+ }
+ else if (0 == strcmp(function, "stddevpop"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_stddevpop, 1, error);
+ }
+ else if (0 == strcmp(function, "stddevsamp"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_stddevsamp, 2, error);
+ }
+ else if (0 == strcmp(function, "sumofsquares"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_sumofsquares, 1, error);
+ }
+ else if (0 == strcmp(function, "varpop"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_varpop, 1, error);
+ }
+ else if (0 == strcmp(function, "varsamp"))
+ {
+ ret = evaluate_statistical_func(value, item, parameter, ts, zbx_eval_calc_varsamp, 2, error);
+ }
else
{
*error = zbx_strdup(*error, "function is not supported");
@@ -2395,9 +2757,10 @@ int zbx_is_trigger_function(const char *name, size_t len)
"trendavg", "trendcount", "trendmax", "trendmin", "trendsum", "abs", "cbrt", "ceil", "exp",
"floor", "log", "log10", "power", "round", "rand", "signum", "sqrt", "truncate", "acos",
"asin", "atan", "cos", "cosh", "cot", "sin", "sinh", "tan", "degrees", "radians", "mod", "pi",
- "e", "expm1", "atan2",
+ "e", "expm1", "atan2", "first", "kurtosis", "mad", "skewness", "stddevpop", "stddevsamp",
+ "sumofsquares", "varpop", "varsamp",
NULL};
- char **ptr;
+ const char **ptr;
for (ptr = functions; NULL != *ptr; ptr++)
{