diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 11:19:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:07:37 +0000 |
commit | b485aab7e71c1625cfc27e0f92c9509f42378458 (patch) | |
tree | ae9abe108601079d1679194de237c9a435ae5b55 /libnetdata/eval | |
parent | Adding upstream version 1.44.3. (diff) | |
download | netdata-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.am | 8 | ||||
-rw-r--r-- | libnetdata/eval/eval.h | 87 | ||||
-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); + } +} |