summaryrefslogtreecommitdiffstats
path: root/libnetdata/eval
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 11:19:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:07:37 +0000
commitb485aab7e71c1625cfc27e0f92c9509f42378458 (patch)
treeae9abe108601079d1679194de237c9a435ae5b55 /libnetdata/eval
parentAdding upstream version 1.44.3. (diff)
downloadnetdata-b485aab7e71c1625cfc27e0f92c9509f42378458.tar.xz
netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.zip
Adding upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--libnetdata/eval/Makefile.am8
-rw-r--r--libnetdata/eval/eval.h87
-rw-r--r--src/libnetdata/eval/README.md (renamed from libnetdata/eval/README.md)0
-rw-r--r--src/libnetdata/eval/eval.c (renamed from libnetdata/eval/eval.c)298
4 files changed, 174 insertions, 219 deletions
diff --git a/libnetdata/eval/Makefile.am b/libnetdata/eval/Makefile.am
deleted file mode 100644
index 161784b8f..000000000
--- a/libnetdata/eval/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-AUTOMAKE_OPTIONS = subdir-objects
-MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
-
-dist_noinst_DATA = \
- README.md \
- $(NULL)
diff --git a/libnetdata/eval/eval.h b/libnetdata/eval/eval.h
deleted file mode 100644
index 05a26936b..000000000
--- a/libnetdata/eval/eval.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#ifndef NETDATA_EVAL_H
-#define NETDATA_EVAL_H 1
-
-#include "../libnetdata.h"
-
-#define EVAL_MAX_VARIABLE_NAME_LENGTH 300
-
-typedef enum rrdcalc_status {
- RRDCALC_STATUS_REMOVED = -2,
- RRDCALC_STATUS_UNDEFINED = -1,
- RRDCALC_STATUS_UNINITIALIZED = 0,
- RRDCALC_STATUS_CLEAR = 1,
- RRDCALC_STATUS_RAISED = 2, // DO NOT CHANGE THESE NUMBERS
- RRDCALC_STATUS_WARNING = 3, // DO NOT CHANGE THESE NUMBERS
- RRDCALC_STATUS_CRITICAL = 4, // DO NOT CHANGE THESE NUMBERS
-} RRDCALC_STATUS;
-
-typedef struct eval_variable {
- STRING *name;
- struct eval_variable *next;
-} EVAL_VARIABLE;
-
-typedef struct eval_expression {
- const char *source;
- const char *parsed_as;
-
- RRDCALC_STATUS *status;
- NETDATA_DOUBLE *myself;
- time_t *after;
- time_t *before;
-
- NETDATA_DOUBLE result;
-
- int error;
- BUFFER *error_msg;
-
- // hidden EVAL_NODE *
- void *nodes;
-
- // custom data to be used for looking up variables
- struct rrdcalc *rrdcalc;
-} EVAL_EXPRESSION;
-
-#define EVAL_VALUE_INVALID 0
-#define EVAL_VALUE_NUMBER 1
-#define EVAL_VALUE_VARIABLE 2
-#define EVAL_VALUE_EXPRESSION 3
-
-// parsing and evaluation
-#define EVAL_ERROR_OK 0
-
-// parsing errors
-#define EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION 1
-#define EVAL_ERROR_UNKNOWN_OPERAND 2
-#define EVAL_ERROR_MISSING_OPERAND 3
-#define EVAL_ERROR_MISSING_OPERATOR 4
-#define EVAL_ERROR_REMAINING_GARBAGE 5
-#define EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE 6
-
-// evaluation errors
-#define EVAL_ERROR_INVALID_VALUE 101
-#define EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS 102
-#define EVAL_ERROR_VALUE_IS_NAN 103
-#define EVAL_ERROR_VALUE_IS_INFINITE 104
-#define EVAL_ERROR_UNKNOWN_VARIABLE 105
-
-// parse the given string as an expression and return:
-// a pointer to an expression if it parsed OK
-// NULL in which case the pointer to error has the error code
-EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error);
-
-// free all resources allocated for an expression
-void expression_free(EVAL_EXPRESSION *expression);
-
-// convert an error code to a message
-const char *expression_strerror(int error);
-
-// evaluate an expression and return
-// 1 = OK, the result is in: expression->result
-// 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg)
-int expression_evaluate(EVAL_EXPRESSION *expression);
-
-int health_variable_lookup(STRING *variable, struct rrdcalc *rc, NETDATA_DOUBLE *result);
-
-#endif //NETDATA_EVAL_H
diff --git a/libnetdata/eval/README.md b/src/libnetdata/eval/README.md
index 8b1378917..8b1378917 100644
--- a/libnetdata/eval/README.md
+++ b/src/libnetdata/eval/README.md
diff --git a/libnetdata/eval/eval.c b/src/libnetdata/eval/eval.c
index a1ac4483c..7e968632a 100644
--- a/libnetdata/eval/eval.c
+++ b/src/libnetdata/eval/eval.c
@@ -2,11 +2,23 @@
#include "../libnetdata.h"
+typedef enum __attribute__((packed)) {
+ EVAL_VALUE_INVALID = 0,
+ EVAL_VALUE_NUMBER,
+ EVAL_VALUE_VARIABLE,
+ EVAL_VALUE_EXPRESSION
+} EVAL_VALUE_TYPE;
+
// ----------------------------------------------------------------------------
// data structures for storing the parsed expression in memory
+typedef struct eval_variable {
+ STRING *name;
+ struct eval_variable *next;
+} EVAL_VARIABLE;
+
typedef struct eval_value {
- int type;
+ EVAL_VALUE_TYPE type;
union {
NETDATA_DOUBLE number;
@@ -24,6 +36,21 @@ typedef struct eval_node {
EVAL_VALUE ops[];
} EVAL_NODE;
+struct eval_expression {
+ STRING *source;
+ STRING *parsed_as;
+
+ NETDATA_DOUBLE result;
+
+ int error;
+ BUFFER *error_msg;
+
+ EVAL_NODE *nodes;
+
+ void *variable_lookup_cb_data;
+ eval_expression_variable_lookup_t variable_lookup_cb;
+};
+
// these are used for EVAL_NODE.operator
// they are used as internal IDs to identify an operator
// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
@@ -62,124 +89,9 @@ static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n);
// evaluation of expressions
static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
- static STRING
- *this_string = NULL,
- *now_string = NULL,
- *after_string = NULL,
- *before_string = NULL,
- *status_string = NULL,
- *removed_string = NULL,
- *uninitialized_string = NULL,
- *undefined_string = NULL,
- *clear_string = NULL,
- *warning_string = NULL,
- *critical_string = NULL;
-
NETDATA_DOUBLE n;
- if(unlikely(this_string == NULL)) {
- this_string = string_strdupz("this");
- now_string = string_strdupz("now");
- after_string = string_strdupz("after");
- before_string = string_strdupz("before");
- status_string = string_strdupz("status");
- removed_string = string_strdupz("REMOVED");
- uninitialized_string = string_strdupz("UNINITIALIZED");
- undefined_string = string_strdupz("UNDEFINED");
- clear_string = string_strdupz("CLEAR");
- warning_string = string_strdupz("WARNING");
- critical_string = string_strdupz("CRITICAL");
- }
-
- if(unlikely(v->name == this_string)) {
- n = (exp->myself)?*exp->myself:NAN;
- buffer_strcat(exp->error_msg, "[ $this = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == after_string)) {
- n = (exp->after && *exp->after)?*exp->after:NAN;
- buffer_strcat(exp->error_msg, "[ $after = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == before_string)) {
- n = (exp->before && *exp->before)?*exp->before:NAN;
- buffer_strcat(exp->error_msg, "[ $before = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == now_string)) {
- n = (NETDATA_DOUBLE)now_realtime_sec();
- buffer_strcat(exp->error_msg, "[ $now = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == status_string)) {
- n = (exp->status)?*exp->status:RRDCALC_STATUS_UNINITIALIZED;
- buffer_strcat(exp->error_msg, "[ $status = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == removed_string)) {
- n = RRDCALC_STATUS_REMOVED;
- buffer_strcat(exp->error_msg, "[ $REMOVED = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == uninitialized_string)) {
- n = RRDCALC_STATUS_UNINITIALIZED;
- buffer_strcat(exp->error_msg, "[ $UNINITIALIZED = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == undefined_string)) {
- n = RRDCALC_STATUS_UNDEFINED;
- buffer_strcat(exp->error_msg, "[ $UNDEFINED = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == clear_string)) {
- n = RRDCALC_STATUS_CLEAR;
- buffer_strcat(exp->error_msg, "[ $CLEAR = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == warning_string)) {
- n = RRDCALC_STATUS_WARNING;
- buffer_strcat(exp->error_msg, "[ $WARNING = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(unlikely(v->name == critical_string)) {
- n = RRDCALC_STATUS_CRITICAL;
- buffer_strcat(exp->error_msg, "[ $CRITICAL = ");
- print_parsed_as_constant(exp->error_msg, n);
- buffer_strcat(exp->error_msg, " ] ");
- return n;
- }
-
- if(exp->rrdcalc && health_variable_lookup(v->name, exp->rrdcalc, &n)) {
+ if(exp->variable_lookup_cb && exp->variable_lookup_cb(v->name, exp->variable_lookup_cb_data, &n)) {
buffer_sprintf(exp->error_msg, "[ ${%s} = ", string2str(v->name));
print_parsed_as_constant(exp->error_msg, n);
buffer_strcat(exp->error_msg, " ] ");
@@ -1074,7 +986,7 @@ int expression_evaluate(EVAL_EXPRESSION *expression) {
expression->error = EVAL_ERROR_OK;
buffer_reset(expression->error_msg);
- expression->result = eval_node(expression, (EVAL_NODE *)expression->nodes, &expression->error);
+ expression->result = eval_node(expression, expression->nodes, &expression->error);
if(unlikely(isnan(expression->result))) {
if(expression->error == EVAL_ERROR_OK)
@@ -1104,6 +1016,9 @@ int expression_evaluate(EVAL_EXPRESSION *expression) {
}
EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
+ if(!string || !*string)
+ return NULL;
+
const char *s = string;
int err = EVAL_ERROR_OK;
@@ -1137,12 +1052,12 @@ EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, in
EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
- exp->source = strdupz(string);
- exp->parsed_as = strdupz(buffer_tostring(out));
+ exp->source = string_strdupz(string);
+ exp->parsed_as = string_strdupz(buffer_tostring(out));
buffer_free(out);
exp->error_msg = buffer_create(100, NULL);
- exp->nodes = (void *)op;
+ exp->nodes = op;
return exp;
}
@@ -1150,9 +1065,9 @@ EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, in
void expression_free(EVAL_EXPRESSION *expression) {
if(!expression) return;
- if(expression->nodes) eval_node_free((EVAL_NODE *)expression->nodes);
- freez((void *)expression->source);
- freez((void *)expression->parsed_as);
+ if(expression->nodes) eval_node_free(expression->nodes);
+ string_freez((void *)expression->source);
+ string_freez((void *)expression->parsed_as);
buffer_free(expression->error_msg);
freez(expression);
}
@@ -1199,3 +1114,138 @@ const char *expression_strerror(int error) {
return "unknown error";
}
}
+
+const char *expression_source(EVAL_EXPRESSION *expression) {
+ if(!expression)
+ return string2str(NULL);
+
+ return string2str(expression->source);
+}
+
+const char *expression_parsed_as(EVAL_EXPRESSION *expression) {
+ if(!expression)
+ return string2str(NULL);
+
+ return string2str(expression->parsed_as);
+}
+
+const char *expression_error_msg(EVAL_EXPRESSION *expression) {
+ if(!expression || !expression->error_msg)
+ return "";
+
+ return buffer_tostring(expression->error_msg);
+}
+
+NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression) {
+ if(!expression)
+ return NAN;
+
+ return expression->result;
+}
+
+void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data) {
+ if(!expression)
+ return;
+
+ expression->variable_lookup_cb = cb;
+ expression->variable_lookup_cb_data = data;
+}
+
+static size_t expression_hardcode_node_variable(EVAL_NODE *node, STRING *variable, NETDATA_DOUBLE value) {
+ size_t matches = 0;
+
+ for(int i = 0; i < node->count; i++) {
+ switch(node->ops[i].type) {
+ case EVAL_VALUE_NUMBER:
+ case EVAL_VALUE_INVALID:
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ if(node->ops[i].variable->name == variable) {
+ string_freez(node->ops[i].variable->name);
+ freez(node->ops[i].variable);
+ node->ops[i].type = EVAL_VALUE_NUMBER;
+ node->ops[i].number = value;
+ matches++;
+ }
+ break;
+
+ case EVAL_VALUE_EXPRESSION:
+ matches += expression_hardcode_node_variable(node->ops[i].expression, variable, value);
+ break;
+ }
+ }
+
+ return matches;
+}
+
+void expression_hardcode_variable(EVAL_EXPRESSION *expression, STRING *variable, NETDATA_DOUBLE value) {
+ if (!expression || !variable || isnan(value))
+ return;
+
+ size_t matches = expression_hardcode_node_variable(expression->nodes, variable, value);
+ if (matches) {
+ char replace[1024];
+ snprintfz(replace, sizeof(replace), NETDATA_DOUBLE_FORMAT_AUTO, value);
+ size_t replace_len = strlen(replace);
+
+ size_t source_len = string_strlen(expression->source);
+ const char *source_str = string2str(expression->source);
+
+ // Allocate enough space to accommodate all replacements.
+ char buf[source_len + 1 + matches * (replace_len + 1)];
+
+ char find1[string_strlen(variable) + 1 + 1];
+ snprintfz(find1, sizeof(find1), "$%s", string2str(variable));
+ size_t find1_len = strlen(find1);
+
+ char find2[string_strlen(variable) + 1 + 3];
+ snprintfz(find2, sizeof(find2), "${%s}", string2str(variable));
+ size_t find2_len = strlen(find2);
+
+ size_t found = 0;
+ char *buf_ptr = buf;
+ const char *source_ptr = source_str;
+
+ while (*source_ptr) {
+ char *s1 = strstr(source_ptr, find1);
+ char *s2 = strstr(source_ptr, find2);
+
+ char *s = s1;
+ size_t len = find1_len;
+ if (s2 && (!s1 || s2 < s1)) {
+ s = s2;
+ len = find2_len;
+ }
+
+ if (s) {
+ if (s == s1 && (isalnum(s[len]) || s[len] == '_')) {
+ // Move past the variable if it's part of a larger word.
+ source_ptr = s + len;
+ continue;
+ }
+
+ // Copy the part before the variable.
+ memcpy(buf_ptr, source_ptr, s - source_ptr);
+ buf_ptr += (s - source_ptr);
+
+ // Copy the replacement.
+ memcpy(buf_ptr, replace, replace_len);
+ buf_ptr += replace_len;
+ *buf_ptr = '\0';
+
+ // Move the source pointer past the replaced variable.
+ source_ptr = s + len;
+ found++;
+ } else {
+ // Copy the rest of the string if no more variables are found.
+ strcpy(buf_ptr, source_ptr);
+ break;
+ }
+ }
+
+ // Update the expression source with the new string.
+ string_freez(expression->source);
+ expression->source = string_strdupz(buf);
+ }
+}