From e6815a42d9275e9ef30f944beef5c78f6380f65b Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Fri, 31 May 2024 09:16:56 -0500 Subject: [PATCH] Refactor JSON parsing to use libjansson The libjansson parser has better documentation and better error reporting. The API is easier to use resulting in less code required for JSON parsing. The __decomp decorator in front of most plugin symbols has also been removed as this is unnecessary for static symbols and variables. --- configure.ac | 2 + ldms/src/decomp/as_is/Makefile.am | 2 +- ldms/src/decomp/as_is/decomp_as_is.c | 262 +++--- ldms/src/decomp/flex/Makefile.am | 2 +- ldms/src/decomp/flex/decomp_flex.c | 383 +++++---- ldms/src/decomp/static/Makefile.am | 2 +- ldms/src/decomp/static/decomp_static.c | 1009 ++++++++++-------------- ldms/src/ldmsd/Makefile.am | 2 +- ldms/src/ldmsd/ldmsd.h | 12 +- ldms/src/ldmsd/ldmsd_decomp.c | 178 ++--- ldms/src/ldmsd/ldmsd_row_cache.c | 140 +++- 11 files changed, 896 insertions(+), 1098 deletions(-) diff --git a/configure.ac b/configure.ac index 14a2c8aa3..897d6ae9f 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,8 @@ AC_C_FLEXIBLE_ARRAY_MEMBER OVIS_PKGLIBDIR AC_LIB_HAVE_LINKFLAGS([jansson], [], [#include ]) +AS_IF([test "x$HAVE_LIBJANSSON" = xno], + [AC_MSG_ERROR([libjansson or jansson.h not found])]) AM_CONDITIONAL([HAVE_LIBJANSSON], [test "x$HAVE_LIBJANSSON" = xyes]) dnl change sharedstatedir default diff --git a/ldms/src/decomp/as_is/Makefile.am b/ldms/src/decomp/as_is/Makefile.am index 32209f076..73966998e 100644 --- a/ldms/src/decomp/as_is/Makefile.am +++ b/ldms/src/decomp/as_is/Makefile.am @@ -8,5 +8,5 @@ DECOMP_LIBADD = ../../core/libldms.la \ ../../ldmsd/libldmsd_request.la libdecomp_as_is_la_SOURCES = decomp_as_is.c -libdecomp_as_is_la_LIBADD = $(DECOMP_LIBADD) +libdecomp_as_is_la_LIBADD = $(DECOMP_LIBADD) $(LTLIBJANSSON) pkglib_LTLIBRARIES += libdecomp_as_is.la diff --git a/ldms/src/decomp/as_is/decomp_as_is.c b/ldms/src/decomp/as_is/decomp_as_is.c index d9ebef928..2c30b50a2 100644 --- a/ldms/src/decomp/as_is/decomp_as_is.c +++ b/ldms/src/decomp/as_is/decomp_as_is.c @@ -58,108 +58,62 @@ #include -#include "ovis_json/ovis_json.h" +#include #include "coll/rbt.h" #include "ldmsd.h" #include "ldmsd_request.h" -static ovis_log_t mylog; +static ovis_log_t as_is_log; +/* Macro to put error message in both ldmsd log and `reqc` */ +#define AS_IS_ERR(reqc, rc, fmt, ...) do { \ + ovis_log(as_is_log, OVIS_LERROR, fmt, ##__VA_ARGS__); \ + if (reqc) { \ + (reqc)->errcode = rc; \ + Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ + } \ +} while (0) -static ldmsd_decomp_t __decomp_as_is_config(ldmsd_strgp_t strgp, - json_entity_t cfg, ldmsd_req_ctxt_t reqc); -static int __decomp_as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, + +static ldmsd_decomp_t as_is_config(ldmsd_strgp_t strgp, + json_t *cfg, ldmsd_req_ctxt_t reqc); +static int as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int *row_count); -static void __decomp_as_is_release_rows(ldmsd_strgp_t strgp, +static void as_is_release_rows(ldmsd_strgp_t strgp, ldmsd_row_list_t row_list); -static void __decomp_as_is_release_decomp(ldmsd_strgp_t strgp); +static void as_is_release_decomp(ldmsd_strgp_t strgp); -struct ldmsd_decomp_s __decomp_as_is = { - .config = __decomp_as_is_config, - .decompose = __decomp_as_is_decompose, - .release_rows = __decomp_as_is_release_rows, - .release_decomp = __decomp_as_is_release_decomp, +static struct ldmsd_decomp_s decomp_as_is = { + .config = as_is_config, + .decompose = as_is_decompose, + .release_rows = as_is_release_rows, + .release_decomp = as_is_release_decomp, }; ldmsd_decomp_t get() { - mylog = ovis_log_register("store.decomp.as_is", "Messages for the as_is decomposition"); - if (!mylog) { - ovis_log(NULL, OVIS_LWARN, "Failed to create the as_is " - "decomposition plugin's log subsystem. " - "Error %d.\n", errno); - } - return &__decomp_as_is; -} - -/* ==== JSON helpers ==== */ - -static json_entity_t __jdict_ent(json_entity_t dict, const char *key) -{ - json_entity_t attr; - json_entity_t val; - - attr = json_attr_find(dict, key); - if (!attr) { - errno = ENOKEY; - return NULL; - } - val = json_attr_value(attr); - return val; -} - -/* Access dict[key], expecting it to be a list */ -static json_list_t __jdict_list(json_entity_t dict, const char *key) -{ - json_entity_t val; - - val = __jdict_ent(dict, key); - if (!val) - return NULL; - if (val->type != JSON_LIST_VALUE) { - errno = EINVAL; - return NULL; - } - return val->value.list_; -} - -/* Access dict[key], expecting it to be a str */ -static json_str_t __jdict_str(json_entity_t dict, const char *key) -{ - json_entity_t val; - - val = __jdict_ent(dict, key); - if (!val) - return NULL; - if (val->type != JSON_STRING_VALUE) { - errno = EINVAL; - return NULL; + as_is_log = ovis_log_register("store.decomp.as_is", "Messages for the as_is decomposition"); + if (!as_is_log) { + ovis_log(NULL, OVIS_LWARN, + "Failed to create the as_is " + "decomposition plugin's log subsystem. " + "Error %d.\n", errno); } - return val->value.str_; + return &decomp_as_is; } -/* ==== generic decomp ==== */ -/* convenient macro to put error message in both ldmsd log and `reqc` */ -#define DECOMP_ERR(reqc, rc, fmt, ...) do { \ - ovis_log(mylog, OVIS_LERROR, fmt, ##__VA_ARGS__); \ - if (reqc) { \ - (reqc)->errcode = rc; \ - Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ - } \ - } while (0) - /* common index config descriptor */ -typedef struct __decomp_index_s { +typedef struct as_is_index_s { char *name; int col_count; char **col_names; /* names of cols for the index */ int *col_idx; /* dst columns composing the index */ -} *__decomp_index_t; +} *as_is_index_t; /* ==== as-is decomposition === */ /* describing a column */ -typedef struct __decomp_as_is_col_cfg_s { +typedef struct as_is_col_cfg_s { enum ldms_value_type col_type; /* value type of the column */ char *name; /* column name */ int set_mid; /* metric ID */ @@ -167,29 +121,29 @@ typedef struct __decomp_as_is_col_cfg_s { int set_mlen; /* array length of set[mid] */ int rec_mid; /* record member ID (if metric is a record) */ int array_len; /* length of the array, if col_type is array */ -} *__decomp_as_is_col_cfg_t; +} *as_is_col_cfg_t; -typedef struct __decomp_as_is_row_cfg_s { +typedef struct as_is_row_cfg_s { struct rbn rbn; /* rbn (inserted to cfg->row_cfg_rbt) */ char *schema_name; /* row schema name */ int col_count; int idx_count; - __decomp_as_is_col_cfg_t cols; /* array of struct */ - __decomp_index_t idxs; /* array of struct */ + as_is_col_cfg_t cols; /* array of struct */ + as_is_index_t idxs; /* array of struct */ size_t row_sz; -} *__decomp_as_is_row_cfg_t; +} *as_is_row_cfg_t; -int __row_cfg_cmp(void *tree_key, const void *key) +static int row_cfg_cmp(void *tree_key, const void *key) { return strcmp(tree_key, key); } -typedef struct __decomp_as_is_cfg_s { +typedef struct as_is_cfg_s { struct ldmsd_decomp_s decomp; int idx_count; struct rbt row_cfg_rbt; - struct __decomp_index_s idxs[OVIS_FLEX]; -} *__decomp_as_is_cfg_t; + struct as_is_index_s idxs[OVIS_FLEX]; +} *as_is_cfg_t; /* str - int pair */ struct str_int_s { @@ -208,10 +162,10 @@ int str_int_cmp(const void *a, const void *b) return strcmp(sa->str, sb->str); } -static void __decomp_as_is_cfg_free(__decomp_as_is_cfg_t dcfg) +static void as_is_cfg_free(as_is_cfg_t dcfg) { int i, j; - __decomp_index_t didx; + as_is_index_t didx; for (i = 0; i < dcfg->idx_count; i++) { didx = &dcfg->idxs[i]; /* names in idx */ @@ -226,109 +180,115 @@ static void __decomp_as_is_cfg_free(__decomp_as_is_cfg_t dcfg) } static void -__decomp_as_is_release_decomp(ldmsd_strgp_t strgp) +as_is_release_decomp(ldmsd_strgp_t strgp) { if (strgp->decomp) { - __decomp_as_is_cfg_free((void*)strgp->decomp); + as_is_cfg_free((void*)strgp->decomp); strgp->decomp = NULL; } } -static ldmsd_decomp_t -__decomp_as_is_config(ldmsd_strgp_t strgp, json_entity_t jcfg, - ldmsd_req_ctxt_t reqc) +static ldmsd_decomp_t as_is_config(ldmsd_strgp_t strgp, json_t *jcfg, + ldmsd_req_ctxt_t reqc) { - json_str_t jname; - json_list_t jidxs, jidx_cols; - json_entity_t jidx, jidx_col; - __decomp_as_is_cfg_t dcfg = NULL; - __decomp_index_t didx; + as_is_cfg_t dcfg = NULL; + as_is_index_t didx; int j, k, n_idx; + json_t *jname, *jidxs, *jidx_cols, *jidx_col, *jidx; /* indices */ - jidxs = __jdict_list(jcfg, "indices"); + jidxs = json_object_get(jcfg, "indices"); if (!jidxs) { n_idx = 0; } else { - n_idx = jidxs->item_count; + if (!json_is_array(jidxs)) { + AS_IS_ERR(reqc, EINVAL, + "The 'indices' key must refer to an array.\n"); + errno = 0; + return NULL; + } + n_idx = json_array_size(jidxs); } dcfg = calloc(1, sizeof(*dcfg) + n_idx * sizeof(dcfg->idxs[0])); if (!dcfg) goto err_enomem; - rbt_init(&dcfg->row_cfg_rbt, __row_cfg_cmp); - dcfg->decomp = __decomp_as_is; + rbt_init(&dcfg->row_cfg_rbt, row_cfg_cmp); + dcfg->decomp = decomp_as_is; dcfg->idx_count = n_idx; if (!n_idx) goto out; /* foreach index */ - j = 0; - TAILQ_FOREACH(jidx, &jidxs->item_list, item_entry) { + json_array_foreach(jidxs, j, jidx) { didx = &dcfg->idxs[j]; - if (jidx->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': index '%d': " - "an index entry must be a dictionary.\n", - strgp->obj.name, j); + if (!json_is_object(jidx)) { + AS_IS_ERR(reqc, EINVAL, + "strgp '%s': index '%d': " + "an index entry must be a dictionary.\n", + strgp->obj.name, j); goto err_0; } - jname = __jdict_str(jidx, "name"); + jname = json_object_get(jidx, "name"); if (!jname) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': index '%d': " - "index['name'] is required.\n", - strgp->obj.name, j); + AS_IS_ERR(reqc, EINVAL, + "strgp '%s': index '%d': " + "index['name'] is required.\n", + strgp->obj.name, j); + goto err_0; + } + if (!json_is_string(jname)) { + AS_IS_ERR(reqc, EINVAL, + "strgp '%s': index '%d': " + "index['name'] must be a string.\n", + strgp->obj.name, j); goto err_0; } - didx->name = strdup(jname->str); + didx->name = strdup(json_string_value(jname)); if (!didx->name) goto err_enomem; - jidx_cols = __jdict_list(jidx, "cols"); - if (!jidx_cols) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': index '%d': " - "index['cols'] is required.\n", - strgp->obj.name, j); + jidx_cols = json_object_get(jidx, "cols"); + if (!jidx_cols || !json_is_array(jidx_cols)) { + AS_IS_ERR(reqc, EINVAL, + "strgp '%s': index '%d': " + "index['cols'] is required.\n", + strgp->obj.name, j); goto err_0; } - didx->col_count = jidx_cols->item_count; - didx->col_names = calloc(1, didx->col_count*sizeof(didx->col_names[0])); + didx->col_count = json_array_size(jidx_cols); + didx->col_names = calloc(1, didx->col_count * sizeof(didx->col_names[0])); if (!didx->col_names) goto err_enomem; - k = 0; - TAILQ_FOREACH(jidx_col, &jidx_cols->item_list, item_entry) { - if (jidx_col->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': index '%d':" - "index['cols'][%d] must be a string.\n", - strgp->obj.name, j, k); + json_array_foreach(jidx_cols, k, jidx_col) { + if (!json_is_string(jidx_col)) { + AS_IS_ERR(reqc, EINVAL, + "strgp '%s': index '%d': " + "index['cols'][%d] must be a string.\n", + strgp->obj.name, j, k); goto err_0; } - didx->col_names[k] = strdup(jidx_col->value.str_->str); + didx->col_names[k] = strdup(json_string_value(jidx_col)); if (!didx->col_names[k]) goto err_enomem; - k++; } - assert(k == didx->col_count); - j++; - } - assert(j == jidxs->item_count); +} out: - return &dcfg->decomp; - err_enomem: - DECOMP_ERR(reqc, errno, "Not enough memory\n"); + AS_IS_ERR(reqc, errno, "Not enough memory\n"); err_0: if (dcfg) - __decomp_as_is_cfg_free(dcfg); + as_is_cfg_free(dcfg); return NULL; } /* protected by strgp->lock */ -static __decomp_as_is_row_cfg_t -__get_row_cfg(__decomp_as_is_cfg_t dcfg, ldms_set_t set) +static as_is_row_cfg_t +get_row_cfg(as_is_cfg_t dcfg, ldms_set_t set) { char *name = NULL; int i, j, k, rec_card, set_card, name_len, col_count; - __decomp_as_is_row_cfg_t drow = NULL; - __decomp_as_is_col_cfg_t dcol = NULL; + as_is_row_cfg_t drow = NULL; + as_is_col_cfg_t dcol = NULL; enum ldms_value_type mtype; ldms_mval_t lh, le, rec_array, rec; size_t marray_len, col_array_len; @@ -553,8 +513,8 @@ __get_row_cfg(__decomp_as_is_cfg_t dcfg, ldms_set_t set) /* index */ for (i = 0; i < dcfg->idx_count; i++) { - __decomp_index_t didx = &dcfg->idxs[i]; - __decomp_index_t ridx = &drow->idxs[i]; + as_is_index_t didx = &dcfg->idxs[i]; + as_is_index_t ridx = &drow->idxs[i]; *ridx = *didx; ridx->col_idx = calloc(ridx->col_count, sizeof(ridx->col_idx[0])); if (!ridx->col_idx) @@ -607,12 +567,12 @@ __get_row_cfg(__decomp_as_is_cfg_t dcfg, ldms_set_t set) static union ldms_value fill = {0}; -static int __decomp_as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, - ldmsd_row_list_t row_list, int *row_count) +static int as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, + ldmsd_row_list_t row_list, int *row_count) { - __decomp_as_is_cfg_t dcfg = (void*)strgp->decomp; - __decomp_as_is_row_cfg_t drow; - __decomp_as_is_col_cfg_t dcol; + as_is_cfg_t dcfg = (void*)strgp->decomp; + as_is_row_cfg_t drow; + as_is_col_cfg_t dcol; ldmsd_row_t row; ldmsd_col_t col; ldmsd_row_index_t idx; @@ -622,7 +582,7 @@ static int __decomp_as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, int j, k, c, mid, rc, rec_mid; int col_count; struct _col_mval_s { - __decomp_as_is_col_cfg_t dcol; + as_is_col_cfg_t dcol; union { ldms_mval_t set_mval; ldms_mval_t lh; @@ -672,7 +632,7 @@ static int __decomp_as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, if (row_schema_name_len < 0) return errno; - drow = __get_row_cfg(dcfg, set); + drow = get_row_cfg(dcfg, set); if (!drow) return errno; @@ -884,11 +844,11 @@ static int __decomp_as_is_decompose(ldmsd_strgp_t strgp, ldms_set_t set, /* clean up stuff here */ if (col_mvals) free(col_mvals); - __decomp_as_is_release_rows(strgp, row_list); + as_is_release_rows(strgp, row_list); return rc; } -static void __decomp_as_is_release_rows(ldmsd_strgp_t strgp, +static void as_is_release_rows(ldmsd_strgp_t strgp, ldmsd_row_list_t row_list) { ldmsd_row_t row; diff --git a/ldms/src/decomp/flex/Makefile.am b/ldms/src/decomp/flex/Makefile.am index a562708ea..c6ecf849e 100644 --- a/ldms/src/decomp/flex/Makefile.am +++ b/ldms/src/decomp/flex/Makefile.am @@ -8,5 +8,5 @@ DECOMP_LIBADD = ../../core/libldms.la \ ../../ldmsd/libldmsd_request.la libdecomp_flex_la_SOURCES = decomp_flex.c -libdecomp_flex_la_LIBADD = $(DECOMP_LIBADD) +libdecomp_flex_la_LIBADD = $(DECOMP_LIBADD) $(LTLIBJANSSON) pkglib_LTLIBRARIES += libdecomp_flex.la diff --git a/ldms/src/decomp/flex/decomp_flex.c b/ldms/src/decomp/flex/decomp_flex.c index 7c65be912..155d0fd3b 100644 --- a/ldms/src/decomp/flex/decomp_flex.c +++ b/ldms/src/decomp/flex/decomp_flex.c @@ -67,108 +67,84 @@ /* Implementation is in ldmsd_decomp.c */ ldmsd_decomp_t ldmsd_decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc); -static ovis_log_t mylog; +static ovis_log_t flex_log; +#define FLEX_ERR(reqc, rc, fmt, ...) do { \ + ovis_log(flex_log, OVIS_LERROR, fmt, ##__VA_ARGS__); \ + if (reqc) { \ + (reqc)->errcode = rc; \ + Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +typedef struct flex_decomp_s { + struct rbn rbn; + struct ldmsd_decomp_s *decomp_api; + struct ldmsd_strgp strgp; + char name[OVIS_FLEX]; /* also rbn key */ +} *flex_decomp_t; -static ldmsd_decomp_t __decomp_flex_config(ldmsd_strgp_t strgp, - json_entity_t cfg, ldmsd_req_ctxt_t reqc); -static int __decomp_flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, +typedef struct flex_digest_s { + struct rbn rbn; + struct ldms_digest_s digest; /* also rbn key */ + int n_decomp; /* number of decomposers to apply */ + flex_decomp_t decomp[OVIS_FLEX]; /* refs to the utilized decomposers */ +} *flex_digest_t; + +typedef struct flex_cfg_s { + struct ldmsd_decomp_s decomp; + struct rbt digest_tree; + struct rbt decomp_tree; + flex_digest_t default_digest; +} *flex_cfg_t; + +static ldmsd_decomp_t flex_config(ldmsd_strgp_t strgp, + json_t *cfg, ldmsd_req_ctxt_t reqc); +static int flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int *row_count); -static void __decomp_flex_release_rows(ldmsd_strgp_t strgp, +static void flex_release_rows(ldmsd_strgp_t strgp, ldmsd_row_list_t row_list); -static void __decomp_flex_release_decomp(ldmsd_strgp_t strgp); +static void flex_release_decomp(ldmsd_strgp_t strgp); struct ldmsd_decomp_s __decomp_flex = { - .config = __decomp_flex_config, - .decompose = __decomp_flex_decompose, - .release_rows = __decomp_flex_release_rows, - .release_decomp = __decomp_flex_release_decomp, + .config = flex_config, + .decompose = flex_decompose, + .release_rows = flex_release_rows, + .release_decomp = flex_release_decomp, }; ldmsd_decomp_t get() { - mylog = ovis_log_register("store.decomp.flex", "Messages for the flex decomposition plugin"); - if (!mylog) { + flex_log = ovis_log_register("store.decomp.flex", "Messages for the flex decomposition plugin"); + if (!flex_log) { ovis_log(NULL, OVIS_LWARN, "Failed to create the flex decomposition " "plugin's log subsytem. Error %d.\n", errno); } return &__decomp_flex; } -/* ==== JSON helpers ==== */ - -static json_entity_t __jdict_ent(json_entity_t dict, const char *key) -{ - json_entity_t attr; - json_entity_t val; - - attr = json_attr_find(dict, key); - if (!attr) { - errno = ENOKEY; - return NULL; - } - val = json_attr_value(attr); - return val; -} - -#define JSTR(P) ((P)->value.str_) - -/* ==== generic decomp ==== */ -/* convenient macro to put error message in both ldmsd log and `reqc` */ -#define DECOMP_ERR(reqc, rc, fmt, ...) do { \ - ovis_log(mylog, OVIS_LERROR, fmt, ##__VA_ARGS__); \ - if (reqc) { \ - (reqc)->errcode = rc; \ - Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ - } \ - } while (0) - -/* ==== flex decomposition === */ - -typedef struct __decomp_flex_decomp_rbn_s { - struct rbn rbn; - struct ldmsd_decomp_s *decomp_api; - struct ldmsd_strgp strgp; - char name[OVIS_FLEX]; /* also rbn key */ -} *__decomp_flex_decomp_rbn_t; - -static -int __decomp_flex_decomp_rbn_s_cmp(void *tree_key, const void *key) +static int flex_decomp_cmp(void *tree_key, const void *key) { return strcmp(tree_key, key); } -typedef struct __decomp_flex_digest_rbn_s { - struct rbn rbn; - struct ldms_digest_s digest; /* also rbn key */ - int n_decomp; /* number of decomposers to apply */ - __decomp_flex_decomp_rbn_t decomp_rbn[OVIS_FLEX]; /* refs to the utilized decomposers */ -} *__decomp_flex_digest_rbn_t; - static -int __decomp_flex_digest_rbn_s_cmp(void *tree_key, const void *key) +int flex_digest_cmp(void *tree_key, const void *key) { return memcmp(tree_key, key, sizeof(struct ldms_digest_s)); } -typedef struct __decomp_flex_cfg_s { - struct ldmsd_decomp_s decomp; - struct rbt digest_rbt; - struct rbt decomp_rbt; - __decomp_flex_digest_rbn_t default_digest; -} *__decomp_flex_cfg_t; - -static void __decomp_flex_cfg_free(__decomp_flex_cfg_t dcfg) +static void flex_cfg_free(flex_cfg_t dcfg) { struct rbn *rbn; - __decomp_flex_decomp_rbn_t decomp_rbn; + flex_decomp_t decomp_rbn; if (dcfg->default_digest) free(dcfg->default_digest); - while ((rbn = rbt_min(&dcfg->digest_rbt))) { - rbt_del(&dcfg->digest_rbt, rbn); + while ((rbn = rbt_min(&dcfg->digest_tree))) { + rbt_del(&dcfg->digest_tree, rbn); free(rbn); } - while ((decomp_rbn = (void*)rbt_min(&dcfg->decomp_rbt))) { - rbt_del(&dcfg->decomp_rbt, &decomp_rbn->rbn); + while ((decomp_rbn = (void*)rbt_min(&dcfg->decomp_tree))) { + rbt_del(&dcfg->decomp_tree, &decomp_rbn->rbn); if (decomp_rbn->strgp.decomp) decomp_rbn->decomp_api->release_decomp(&decomp_rbn->strgp); free(decomp_rbn); @@ -177,202 +153,207 @@ static void __decomp_flex_cfg_free(__decomp_flex_cfg_t dcfg) } static void -__decomp_flex_release_decomp(ldmsd_strgp_t strgp) +flex_release_decomp(ldmsd_strgp_t strgp) { if (strgp->decomp) { - __decomp_flex_cfg_free((void*)strgp->decomp); + flex_cfg_free((void*)strgp->decomp); strgp->decomp = NULL; } } static ldmsd_decomp_t -__decomp_flex_config(ldmsd_strgp_t strgp, json_entity_t jcfg, +flex_config(ldmsd_strgp_t strgp, json_t *jcfg, ldmsd_req_ctxt_t reqc) { - __decomp_flex_cfg_t dcfg = NULL; + flex_cfg_t dcfg = NULL; + flex_decomp_t decomp; + flex_digest_t digest; + struct ldmsd_decomp_s *decomp_api; int n_decomp, i; - json_entity_t jdecomp, jdigest, jattr, jkey, jval, jtype; + const char *key; + json_t *jdecomp, *jdigest, *jschema, *jtype; /* decomposition */ - jdecomp = __jdict_ent(jcfg, "decomposition"); + jdecomp = json_object_get(jcfg, "decomposition"); if (!jdecomp) { - DECOMP_ERR(reqc, EINVAL, "'decomposition' attribute is missing\n"); + FLEX_ERR(reqc, EINVAL, "'decomposition' attribute is missing\n"); goto err_0; } - if (jdecomp->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "'decomposition' must be a dictionary\n"); + if (!json_is_object(jdecomp)) { + FLEX_ERR(reqc, EINVAL, "'decomposition' must be a dictionary\n"); goto err_0; } /* digest */ - jdigest = __jdict_ent(jcfg, "digest"); + jdigest = json_object_get(jcfg, "digest"); if (!jdigest) { - DECOMP_ERR(reqc, EINVAL, "'digest' attribute is missing\n"); + FLEX_ERR(reqc, EINVAL, "'digest' attribute is missing\n"); goto err_0; } - if (jdigest->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "'digest' must be a dictionary\n"); + if (!json_is_object(jdigest)) { + FLEX_ERR(reqc, EINVAL, "'digest' must be a dictionary\n"); goto err_0; } dcfg = calloc(1, sizeof(*dcfg)); - if (!dcfg) { - DECOMP_ERR(reqc, ENOMEM, "Not enough memory\n"); - goto err_0; - } + if (!dcfg) + goto enomem; dcfg->decomp = __decomp_flex; - rbt_init(&dcfg->decomp_rbt, __decomp_flex_decomp_rbn_s_cmp); - rbt_init(&dcfg->digest_rbt, __decomp_flex_digest_rbn_s_cmp); - - __decomp_flex_decomp_rbn_t decomp_rbn; - struct ldmsd_decomp_s *decomp_api; - - /* processing decompotision */ - for (jattr = json_attr_first(jdecomp); jattr; jattr = json_attr_next(jattr)) { - jkey = jattr->value.attr_->name; - assert(jkey->type == JSON_STRING_VALUE); - jval = jattr->value.attr_->value; - if (jval->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "decomposition['%s'] must be " - "a dictionary\n", JSTR(jkey)->str); + rbt_init(&dcfg->decomp_tree, flex_decomp_cmp); + rbt_init(&dcfg->digest_tree, flex_digest_cmp); + + /* processing decomposition */ + json_object_foreach(jdecomp, key, jschema) { + if (!json_is_object(jschema)) { + FLEX_ERR(reqc, EINVAL, + "decomposition['%s'] must be " + "a dictionary\n", key); goto err_1; } - jtype = __jdict_ent(jval, "type"); + jtype = json_object_get(jschema, "type"); if (!jtype) { - DECOMP_ERR(reqc, EINVAL, "decomposition['%s'] must " - "specify 'type' attribute\n", - JSTR(jkey)->str); + FLEX_ERR(reqc, EINVAL, + "decomposition['%s'] must " + "decomposition 'type' attribute is required: " + "'as_is', or 'static'.\n", key); goto err_1; } - if (jtype->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "decomposition['%s']['type'] " - "must be a string\n", - JSTR(jkey)->str); + if (!json_is_string(jtype)) { + FLEX_ERR(reqc, EINVAL, + "decomposition['%s']['type'] " + "must be a string\n", key); goto err_1; } - decomp_api = ldmsd_decomp_get(JSTR(jtype)->str, reqc); + decomp_api = ldmsd_decomp_get(json_string_value(jtype), reqc); if (!decomp_api) { /* ldmsd_decomp_get() already populate reqc error */ goto err_1; } - decomp_rbn = calloc(1, sizeof(*decomp_rbn) + - jkey->value.str_->str_len + 1); - if (!decomp_rbn) { - DECOMP_ERR(reqc, ENOMEM, "Not enough memory\n"); + decomp = calloc(1, sizeof(*decomp) + strlen(key) + 1); + if (!decomp) + goto enomem; + strcpy(decomp->name, key); + (void)asprintf(&decomp->strgp.obj.name, "%s.%s", strgp->obj.name, key); + decomp->decomp_api = decomp_api; + decomp->strgp.decomp = decomp_api->config(&decomp->strgp, jschema, reqc); + if (!decomp->strgp.decomp) { + free(decomp); goto err_1; } - memcpy(decomp_rbn->name, JSTR(jkey)->str, JSTR(jkey)->str_len); - decomp_rbn->decomp_api = decomp_api; - decomp_rbn->strgp.decomp = decomp_api->config(&decomp_rbn->strgp, jval, reqc); - if (!decomp_rbn->strgp.decomp) { - /* reqc error has been populated */ - free(decomp_rbn); - goto err_1; - } - rbn_init(&decomp_rbn->rbn, decomp_rbn->name); - rbt_ins(&dcfg->decomp_rbt, &decomp_rbn->rbn); + rbn_init(&decomp->rbn, decomp->name); + rbt_ins(&dcfg->decomp_tree, &decomp->rbn); } - json_entity_t jlist; - __decomp_flex_digest_rbn_t digest_rbn; - - /* processing digest */ - for (jattr = json_attr_first(jdigest); jattr; jattr = json_attr_next(jattr)) { - jkey = jattr->value.attr_->name; - assert(jkey->type == JSON_STRING_VALUE); - jval = jattr->value.attr_->value; - jlist = NULL; - if (jval->type == JSON_LIST_VALUE) { - jlist = jval; - jval = TAILQ_FIRST(&jlist->value.list_->item_list); - n_decomp = jlist->value.list_->item_count; - } else if (jval->type == JSON_STRING_VALUE) { - n_decomp = 1; - } else { - /* invalid value */ - DECOMP_ERR(reqc, EINVAL, "digest['%s'] value must be" - "a string or a list of strings.\n", - JSTR(jkey)->str); - goto err_1; - } + /* Process digest */ + json_object_foreach(jdigest, key, jschema) { + /* "01FC.....0102" : [ "schema_a", "schema_b" ], + * "02FC.....0102" : "schema_c", + * "*" : "the_default" + * ... + */ int rc; - struct ldms_digest_s digest = {}; - if (0 == strcmp(JSTR(jkey)->str, "*")) { - digest_rbn = dcfg->default_digest; + struct ldms_digest_s digest_key = {}; + + /* Check for duplicate definition */ + digest = NULL; + if (0 == strcmp(key, "*")) { + digest = dcfg->default_digest; } else { - rc = ldms_str_digest(JSTR(jkey)->str, &digest); + rc = ldms_str_digest(key, &digest_key); if (rc) { - DECOMP_ERR(reqc, rc, "Invalid digest '%s'.\n", - JSTR(jkey)->str); + FLEX_ERR(reqc, rc, "Invalid digest key '%s'.\n", key); goto err_1; } - digest_rbn = (void*)rbt_find(&dcfg->digest_rbt, &digest); + struct rbn *rbn = rbt_find(&dcfg->digest_tree, &digest_key); + if (rbn) + digest = container_of(rbn, struct flex_digest_s, rbn); } - if (digest_rbn) { - DECOMP_ERR(reqc, EINVAL, - "Multiple definition of digest['%s'].\n", - JSTR(jkey)->str); - goto err_1; + if (digest) { + FLEX_ERR(reqc, EINVAL, + "Ignoring duplicate definition of " + "digest['%s'].\n", key); + continue; } - digest_rbn = calloc(1, sizeof(*digest_rbn) + - n_decomp*sizeof(digest_rbn->decomp_rbn[0])); - if (!digest_rbn) { - DECOMP_ERR(reqc, ENOMEM, "Not enough memory\n"); + + /* Determine how many decompositions are matched to the digest */ + if (json_is_string(jschema)) { + n_decomp = 1; + } else if (json_is_array(jschema)) { + n_decomp = json_array_size(jschema); + } else { + FLEX_ERR(reqc, EINVAL, + "Invalid value for decomposition attribute " + "in 'digest'.\n"); goto err_1; } - memcpy(&digest_rbn->digest, &digest, sizeof(digest)); - rbn_init(&digest_rbn->rbn, &digest_rbn->digest); - digest_rbn->n_decomp = n_decomp; - if (0 == strcmp(JSTR(jkey)->str, "*")) { - dcfg->default_digest = digest_rbn; + + /* Allocate a new digest */ + digest = calloc(1, sizeof(*digest) + + n_decomp * sizeof(digest->decomp[0])); + if (!digest) + goto enomem; + + /* Insert digest into tree */ + memcpy(&digest->digest, &digest_key, sizeof(digest_key)); + rbn_init(&digest->rbn, &digest->digest); + digest->n_decomp = n_decomp; + if (0 == strcmp(key, "*")) { + dcfg->default_digest = digest; } else { - rbt_ins(&dcfg->digest_rbt, &digest_rbn->rbn); + rbt_ins(&dcfg->digest_tree, &digest->rbn); } - i = 0; - while (jval) { - if (jval->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "digest['%s'] value must be" - "a string or a list of strings.\n", - JSTR(jkey)->str); - goto err_1; - } - decomp_rbn = (void*)rbt_find(&dcfg->decomp_rbt, JSTR(jval)->str); - if (!decomp_rbn) { - DECOMP_ERR(reqc, ENOENT, "decomposition '%s' is not defined.\n", - JSTR(jval)->str); + /* Look up the decomposition for each decomp key */ + if (n_decomp == 1) { + struct rbn *rbn = rbt_find(&dcfg->decomp_tree, + json_string_value(jschema)); + if (!rbn) { + FLEX_ERR(reqc, EINVAL, + "The specified decomposition " + "schema ('%s') was not found.\n", + json_string_value(jschema)); goto err_1; } - digest_rbn->decomp_rbn[i] = decomp_rbn; - if (jlist) { - jval = TAILQ_NEXT(jval, item_entry); - } else { - jval = NULL; + decomp = container_of(rbn, struct flex_decomp_s, rbn); + digest->decomp[0] = decomp; + } else { + json_array_foreach(jschema, i, jdecomp) { + struct rbn *rbn = rbt_find(&dcfg->decomp_tree, + json_string_value(jdecomp)); + if (!rbn) { + FLEX_ERR(reqc, EINVAL, + "The specified decomposition " + "schema ('%s') was not found.\n", + json_string_value(jdecomp)); + goto err_1; + } + decomp = container_of(rbn, struct flex_decomp_s, rbn); + digest->decomp[i] = decomp; } - i++; } - assert(i == n_decomp); } return &dcfg->decomp; - err_1: - __decomp_flex_cfg_free(dcfg); - err_0: +enomem: + FLEX_ERR(reqc, ENOMEM, "Insufficient memory.\n"); +err_1: + flex_cfg_free(dcfg); +err_0: return NULL; } -static int __decomp_flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, +static int flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int *row_count) { - __decomp_flex_cfg_t dcfg = (void*)strgp->decomp; + flex_cfg_t dcfg = (void*)strgp->decomp; ldms_digest_t digest = ldms_set_digest_get(set); struct ldmsd_row_list_s rlist; int rcount, i, rc; - __decomp_flex_digest_rbn_t digest_rbn; - __decomp_flex_decomp_rbn_t decomp_rbn; + flex_digest_t digest_rbn; + flex_decomp_t decomp_rbn; TAILQ_INIT(&rlist); - digest_rbn = (void*)rbt_find(&dcfg->digest_rbt, digest); + digest_rbn = (void*)rbt_find(&dcfg->digest_tree, digest); if (!digest_rbn) { if (!dcfg->default_digest) return 0; @@ -381,7 +362,7 @@ static int __decomp_flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, *row_count = 0; for (i = 0; i < digest_rbn->n_decomp; i++) { rcount = 0; - decomp_rbn = digest_rbn->decomp_rbn[i]; + decomp_rbn = digest_rbn->decomp[i]; rc = decomp_rbn->decomp_api->decompose( &decomp_rbn->strgp, set, &rlist, &rcount); @@ -394,12 +375,12 @@ static int __decomp_flex_decompose(ldmsd_strgp_t strgp, ldms_set_t set, return 0; err_0: - __decomp_flex_release_rows(strgp, row_list); + flex_release_rows(strgp, row_list); return rc; } -static void __decomp_flex_release_rows(ldmsd_strgp_t strgp, - ldmsd_row_list_t row_list) +static void flex_release_rows(ldmsd_strgp_t strgp, + ldmsd_row_list_t row_list) { ldmsd_row_t row; while ((row = TAILQ_FIRST(row_list))) { diff --git a/ldms/src/decomp/static/Makefile.am b/ldms/src/decomp/static/Makefile.am index 47a35a390..d81a920d3 100644 --- a/ldms/src/decomp/static/Makefile.am +++ b/ldms/src/decomp/static/Makefile.am @@ -8,5 +8,5 @@ DECOMP_LIBADD = ../../core/libldms.la \ ../../ldmsd/libldmsd_request.la libdecomp_static_la_SOURCES = decomp_static.c -libdecomp_static_la_LIBADD = $(DECOMP_LIBADD) +libdecomp_static_la_LIBADD = $(DECOMP_LIBADD) $(LTLIBJANSSON) pkglib_LTLIBRARIES += libdecomp_static.la diff --git a/ldms/src/decomp/static/decomp_static.c b/ldms/src/decomp/static/decomp_static.c index 48e693c0d..295c0ad37 100644 --- a/ldms/src/decomp/static/decomp_static.c +++ b/ldms/src/decomp/static/decomp_static.c @@ -58,16 +58,25 @@ #include -#include "ovis_json/ovis_json.h" +#include #include "coll/rbt.h" #include "ldmsd.h" #include "ldmsd_request.h" -static ovis_log_t mylog; +static ovis_log_t static_log; +/* convenient macro to put error message in both ldmsd log and `reqc` */ +#define THISLOG(reqc, rc, fmt, ...) do { \ + ovis_log(static_log, OVIS_LERROR, fmt, ##__VA_ARGS__); \ + if (reqc) { \ + (reqc)->errcode = rc; \ + Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + static ldmsd_decomp_t decomp_static_config(ldmsd_strgp_t strgp, - json_entity_t cfg, ldmsd_req_ctxt_t reqc); + json_t *cfg, ldmsd_req_ctxt_t reqc); static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int *row_count); static void decomp_static_release_rows(ldmsd_strgp_t strgp, @@ -83,239 +92,84 @@ static struct ldmsd_decomp_s decomp_static = { ldmsd_decomp_t get() { - mylog = ovis_log_register("store.decomp.static", "Messages for the static decomposition"); - if (!mylog) { - ovis_log(NULL, OVIS_LWARN, "Failed to create decomp_static's " - "log subsystem. Error %d.\n", errno); + static_log = ovis_log_register("store.decomp.static", "Messages for the static decomposition"); + if (!static_log) { + ovis_log(NULL, OVIS_LWARN, + "Failed to create decomp_static's " + "log subsystem. Error %d.\n", errno); } return &decomp_static; } -/* ==== JSON helpers ==== */ - -static json_entity_t __jdict_ent(json_entity_t dict, const char *key) -{ - json_entity_t attr; - json_entity_t val; - - attr = json_attr_find(dict, key); - if (!attr) { - errno = ENOKEY; - return NULL; - } - val = json_attr_value(attr); - return val; -} - -/* Access dict[key], expecting it to be a list */ -static json_list_t __jdict_list(json_entity_t dict, const char *key) -{ - json_entity_t val; - - val = __jdict_ent(dict, key); - if (!val) - return NULL; - if (val->type != JSON_LIST_VALUE) { - errno = EINVAL; - return NULL; - } - return val->value.list_; -} - -/* Access dict[key], expecting it to be a str */ -static json_str_t __jdict_str(json_entity_t dict, const char *key) -{ - json_entity_t val; - - val = __jdict_ent(dict, key); - if (!val) - return NULL; - if (val->type != JSON_STRING_VALUE) { - errno = EINVAL; - return NULL; - } - return val->value.str_; -} - -static enum json_value_e __ldms_json_type(enum ldms_value_type type) -{ - switch (type) { - case LDMS_V_CHAR: /* maps to json string of 1 character */ - case LDMS_V_CHAR_ARRAY: - return JSON_STRING_VALUE; - case LDMS_V_U8: - case LDMS_V_S8: - case LDMS_V_U8_ARRAY: - case LDMS_V_S8_ARRAY: - case LDMS_V_U16: - case LDMS_V_S16: - case LDMS_V_U16_ARRAY: - case LDMS_V_S16_ARRAY: - case LDMS_V_U32: - case LDMS_V_S32: - case LDMS_V_U32_ARRAY: - case LDMS_V_S32_ARRAY: - case LDMS_V_U64: - case LDMS_V_S64: - case LDMS_V_U64_ARRAY: - case LDMS_V_S64_ARRAY: - return JSON_INT_VALUE; - case LDMS_V_F32: - case LDMS_V_F32_ARRAY: - case LDMS_V_D64: - case LDMS_V_D64_ARRAY: - return JSON_FLOAT_VALUE; - default: - assert(0 == "Not supported"); - return JSON_NULL_VALUE; - } -} - -static int __array_fill_from_json(ldms_mval_t v, enum ldms_value_type type, - json_list_t jlist) +static int +mval_from_json(ldms_mval_t *v, enum ldms_value_type *mtype, int *mlen, json_t *jent) { + json_t *item; + json_type type; + ldms_mval_t mval = NULL; int i; - json_entity_t ent; - enum json_value_e jtype; - jtype = __ldms_json_type(type); - i = 0; - TAILQ_FOREACH(ent, &jlist->item_list, item_entry) { - if (ent->type != jtype) - return EINVAL; + switch (json_typeof(jent)) { + case JSON_REAL: + *mtype = LDMS_V_D64; + mval = calloc(1, sizeof(mval->v_d)); + mval->v_d = json_real_value(jent); + break; + case JSON_STRING: + *mtype = LDMS_V_CHAR_ARRAY; + *mlen = json_string_length(jent) + 1; + mval = malloc(*mlen); + if (!mval) + goto err; + memcpy(mval->a_char, json_string_value(jent), *mlen); + break; + case JSON_INTEGER: + mval = calloc(1, sizeof(mval->v_s64)); + mval->v_s64 = json_integer_value(jent); + break; + case JSON_ARRAY: + *mlen = json_array_size(jent); + item = json_array_get(jent, 0); + if (!item) + goto err; + type = json_typeof(item); switch (type) { - case LDMS_V_U8_ARRAY: - v->a_u8[i] = (uint8_t)ent->value.int_; - break; - case LDMS_V_S8_ARRAY: - v->a_s8[i] = (int8_t)ent->value.int_; - break; - case LDMS_V_U16_ARRAY: - v->a_u16[i] = htole16((uint16_t)ent->value.int_); + case JSON_INTEGER: + *mtype = LDMS_V_S64_ARRAY; + mval = calloc(*mlen, sizeof(mval->v_s64)); + if (!mval) + goto err; break; - case LDMS_V_S16_ARRAY: - v->a_s16[i] = htole16((int16_t)ent->value.int_); - break; - case LDMS_V_U32_ARRAY: - v->a_u32[i] = htole32((uint32_t)ent->value.int_); - break; - case LDMS_V_S32_ARRAY: - v->a_s32[i] = htole32((int32_t)ent->value.int_); - break; - case LDMS_V_U64_ARRAY: - v->a_u64[i] = htole64((uint64_t)ent->value.int_); - break; - case LDMS_V_S64_ARRAY: - v->a_s64[i] = htole64((int64_t)ent->value.int_); - break; - case LDMS_V_F32_ARRAY: - v->a_f[i] = htole32((float)ent->value.double_); - break; - case LDMS_V_D64_ARRAY: - v->a_d[i] = htole64((double)ent->value.double_); + case JSON_REAL: + *mtype = LDMS_V_D64_ARRAY; + mval = calloc(*mlen, sizeof(mval->v_d)); + if (!mval) + goto err; break; + case JSON_OBJECT: + case JSON_ARRAY: + case JSON_STRING: + case JSON_NULL: + case JSON_TRUE: + case JSON_FALSE: default: - return EINVAL; + goto err; + } + json_array_foreach(jent, i, item) { + if (type == JSON_INTEGER) + ldms_mval_array_set_s64(mval, i, json_integer_value(item)); + else + ldms_mval_array_set_double(mval, i, json_real_value(item)); } - i++; - } - return 0; -} - -static int __prim_fill_from_json(ldms_mval_t v, enum ldms_value_type type, - json_entity_t jent) -{ - enum json_value_e jtype; - - jtype = __ldms_json_type(type); - if (jent->type != jtype) - return EINVAL; - switch (type) { - case LDMS_V_U8: - v->v_u8 = (uint8_t)jent->value.int_; - break; - case LDMS_V_S8: - v->v_s8 = (int8_t)jent->value.int_; - break; - case LDMS_V_U16: - v->v_u16 = htole16((uint16_t)jent->value.int_); - break; - case LDMS_V_S16: - v->v_s16 = htole16((int16_t)jent->value.int_); - break; - case LDMS_V_U32: - v->v_u32 = htole32((uint32_t)jent->value.int_); - break; - case LDMS_V_S32: - v->v_s32 = htole32((int32_t)jent->value.int_); - break; - case LDMS_V_U64: - v->v_u64 = htole64((uint64_t)jent->value.int_); - break; - case LDMS_V_S64: - v->v_s64 = htole64((int64_t)jent->value.int_); - break; - case LDMS_V_F32: - v->v_f = htole32((float)jent->value.double_); - break; - case LDMS_V_D64: - v->v_d = htole64((double)jent->value.double_); - break; default: - return EINVAL; + goto err; } + *v = mval; return 0; +err: + return EINVAL; } -/* ==== Helpers ==== */ - -static inline int __ldms_vsz(enum ldms_value_type t) -{ - switch (t) { - case LDMS_V_CHAR: - case LDMS_V_U8: - case LDMS_V_S8: - case LDMS_V_CHAR_ARRAY: - case LDMS_V_U8_ARRAY: - case LDMS_V_S8_ARRAY: - return sizeof(char); - case LDMS_V_U16: - case LDMS_V_S16: - case LDMS_V_U16_ARRAY: - case LDMS_V_S16_ARRAY: - return sizeof(int16_t); - case LDMS_V_U32: - case LDMS_V_S32: - case LDMS_V_U32_ARRAY: - case LDMS_V_S32_ARRAY: - return sizeof(int32_t); - case LDMS_V_U64: - case LDMS_V_S64: - case LDMS_V_U64_ARRAY: - case LDMS_V_S64_ARRAY: - return sizeof(int64_t); - case LDMS_V_F32: - case LDMS_V_F32_ARRAY: - return sizeof(float); - case LDMS_V_D64: - case LDMS_V_D64_ARRAY: - return sizeof(double); - default: - assert(0 == "Unsupported type"); - } - return -1; -} - -/* ==== generic decomp ==== */ -/* convenient macro to put error message in both ldmsd log and `reqc` */ -#define DECOMP_ERR(reqc, rc, fmt, ...) do { \ - ovis_log(mylog, OVIS_LERROR, fmt, ##__VA_ARGS__); \ - if (reqc) { \ - (reqc)->errcode = rc; \ - Snprintf(&(reqc)->line_buf, &(reqc)->line_len, fmt, ##__VA_ARGS__); \ - } \ - } while (0) - /* common index config descriptor */ typedef struct decomp_index_s { char *name; @@ -323,8 +177,6 @@ typedef struct decomp_index_s { int *col_idx; /* dst columns composing the index */ } *decomp_index_t; -/* ==== static decomposition === */ - /* describing a src-dst column pair */ typedef struct decomp_static_col_cfg_s { enum ldms_value_type type; /* value type */ @@ -450,7 +302,7 @@ static int init_row_cache(ldmsd_strgp_t strgp, int row_limit) return 1; } -static int get_col_no(decomp_static_row_cfg_t cfg_row, char *name) +static int get_col_no(decomp_static_row_cfg_t cfg_row, const char *name) { int i; for (i = 0; i < cfg_row->col_count; i++) { @@ -460,426 +312,400 @@ static int get_col_no(decomp_static_row_cfg_t cfg_row, char *name) return -1; } -static ldmsd_decomp_t -decomp_static_config(ldmsd_strgp_t strgp, json_entity_t jcfg, - ldmsd_req_ctxt_t reqc) +static int handle_indices( + ldmsd_strgp_t strgp, json_t *jidxs, + decomp_static_row_cfg_t cfg_row, int row_no, + ldmsd_req_ctxt_t reqc) { - json_str_t jstr, jsrc, jdst, jrec_member, jname, jtype, jop; - json_list_t jrows, jcols, jidxs, jidx_cols, jlist; - json_entity_t jrow, jcol, jidx, jfill, jarray_len, jgroup, jent; - decomp_static_cfg_t dcfg = NULL; - decomp_static_row_cfg_t cfg_row; - decomp_static_col_cfg_t cfg_col; - decomp_index_t didx; - int i, j, k, rc; struct str_int_tbl_s *col_id_tbl = NULL; struct str_int_s key, *tbl_ent; + decomp_index_t didx; + json_t *jidx, *jidx_cols, *jcol, *jname; + int rc, j, k; - jrows = __jdict_list(jcfg, "rows"); - if (!jrows) { - DECOMP_ERR(reqc, errno, "strgp '%s': The 'rows' attribute is missing, " - "or its value is not a list.\n", - strgp->obj.name); - goto err_0; + if (!jidxs) + return 0; + + if (!json_is_array(jidxs)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "the 'indices' value must be an array.\n", + strgp->obj.name, row_no); + return EINVAL; } - dcfg = calloc(1, sizeof(*dcfg) + jrows->item_count * sizeof(dcfg->rows[0])); - if (!dcfg) { - DECOMP_ERR(reqc, errno, "out of memory\n"); + + cfg_row->idx_count = json_array_size(jidxs); + cfg_row->idxs = calloc(1, cfg_row->idx_count * sizeof(cfg_row->idxs[0])); + if (!cfg_row->idxs) + goto enomem; + cfg_row->row_sz += cfg_row->idx_count * sizeof(ldmsd_row_index_t); + /* prep temporary col-id table */ + col_id_tbl = calloc(1, sizeof(*col_id_tbl) + + (cfg_row->col_count * sizeof(col_id_tbl->ent[0]))); + if (!col_id_tbl) + goto enomem; + col_id_tbl->len = cfg_row->col_count; + for (j = 0; j < cfg_row->col_count; j++) { + col_id_tbl->ent[j].str = cfg_row->cols[j].dst; + col_id_tbl->ent[j].i = j; + } + + qsort(col_id_tbl->ent, col_id_tbl->len, sizeof(col_id_tbl->ent[0]), str_int_cmp); + /* foreach index */ + json_array_foreach(jidxs, j, jidx) { + didx = &cfg_row->idxs[j]; + if (!json_is_object(jidx)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "an index must be a dictionary.\n", + strgp->obj.name, row_no); + rc = EINVAL; + goto err_0; + } + jname = json_object_get(jidx, "name"); + if (!jname) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': index '%d': " + "index['name'] is required.\n", + strgp->obj.name, row_no, j); + rc = EINVAL; + goto err_0; + } + didx->name = strdup(json_string_value(jname)); + if (!didx->name) + goto enomem; + jidx_cols = json_object_get(jidx, "cols"); + if (!jidx_cols) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': index '%d':" + "index['cols'] is required.\n", + strgp->obj.name, row_no, j); + rc = EINVAL; + goto err_0; + } + didx->col_count = json_array_size(jidx_cols); + didx->col_idx = calloc(1, didx->col_count * sizeof(didx->col_idx[0])); + if (!didx->col_idx) + goto enomem; + cfg_row->row_sz += sizeof(struct ldmsd_row_index_s) + + (didx->col_count * sizeof(ldmsd_col_t)); + /* resolve col name to col id */ + json_array_foreach(jidx_cols, k, jcol) { + if (!json_is_string(jcol)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': index '%d': col '%d': " + "index['cols'][x] value must be a string.\n", + strgp->obj.name, row_no, j, k); + rc = EINVAL; + goto err_0; + } + key.str = json_string_value(jcol); + tbl_ent = bsearch(&key, col_id_tbl->ent, + col_id_tbl->len, + sizeof(col_id_tbl->ent[0]), + str_int_cmp); + if (!tbl_ent) { + THISLOG(reqc, ENOENT, "strgp '%s': row '%d': index '%d': " + "column '%s' not found.\n", + strgp->obj.name, row_no, j, key.str); + rc = ENOENT; + goto err_0; + } + didx->col_idx[k] = tbl_ent->i; + } + } + free(col_id_tbl); + return 0; +enomem: + rc = ENOMEM; + THISLOG(reqc, ENOMEM, "%s: Insufficent memory.\n", strgp->obj.name); +err_0: + return rc; +} + +static int handle_group( + ldmsd_strgp_t strgp, json_t *jgroup, + decomp_static_row_cfg_t cfg_row, int row_no, + ldmsd_req_ctxt_t reqc) +{ + json_t *jidxs, *jidx; + json_t *jlimit; + int col_no; + int rc; + + jlimit = json_object_get(jgroup, "limit"); + if (jlimit) { + if (json_is_integer(jlimit)) { + cfg_row->row_limit = json_integer_value(jlimit); + } else if (json_is_string(jlimit)) { + cfg_row->row_limit = strtoul(json_string_value(jlimit), NULL, 0); + } else { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['index'] is a required and must be an array.\n", + strgp->obj.name, row_no); + return EINVAL; + } + } else { + cfg_row->row_limit = 2; + } + + /* Loop through all of the entries in the + * "index" list and set the associated + * column index in the group_cols array + */ + jidxs = json_object_get(jgroup, "index"); + if (!jidxs || !json_is_array(jidxs)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['index'] is a required and must be an array.\n", + strgp->obj.name, row_no); + return EINVAL; + } + + cfg_row->group_count = json_array_size(jidxs); + cfg_row->group_cols = calloc(cfg_row->group_count, + sizeof(*cfg_row->group_cols)); + if (!cfg_row->group_cols) + goto enomem; + + json_array_foreach(jidxs, col_no, jidx) { + if (!json_is_string(jidx)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['index'] entries must be column " + "name strings.\n", + strgp->obj.name, row_no); + rc = EINVAL; + goto err_0; + } + cfg_row->group_cols[col_no] = get_col_no(cfg_row, json_string_value(jidx)); + if (cfg_row->group_cols[col_no] < 0) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['index'] the specified column '%s'" + "is not present in the column list.\n", + strgp->obj.name, row_no, json_string_value(jidx)); + rc = EINVAL; + goto err_0; + } + } + + jidxs = json_object_get(jgroup, "order"); + if (!jidxs || !json_is_array(jidxs)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['order'] is a required and must be an array.\n", + strgp->obj.name, row_no); + rc = EINVAL; goto err_0; } + cfg_row->row_order_count = json_array_size(jidxs); + cfg_row->row_order_cols = calloc(cfg_row->row_order_count, + sizeof(*cfg_row->row_order_cols)); + if (!cfg_row->row_order_cols) + goto enomem; + + json_array_foreach(jidxs, col_no, jidx) { + if (!json_is_string(jidx)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['order'] entries must be column " + "name strings.\n", + strgp->obj.name, row_no); + rc = EINVAL; + goto err_0; + } + cfg_row->row_order_cols[col_no] = + get_col_no(cfg_row, json_string_value(jidx)); + if (cfg_row->row_order_cols[col_no] < 0) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "group['order'] the specified column '%s'" + "is not present in the column list.\n", + strgp->obj.name, row_no, json_string_value(jidx)); + rc = ENOENT; + goto err_0; + } + } + + rc = init_row_cache(strgp, cfg_row->row_limit); + if (rc) + goto enomem; + return 0; +enomem: + rc = ENOMEM; + THISLOG(reqc, ENOMEM, "%s: Insufficient memory.\n", strgp->obj.name); +err_0: + return rc; +} + +static ldmsd_decomp_t +decomp_static_config(ldmsd_strgp_t strgp, json_t *jcfg, + ldmsd_req_ctxt_t reqc) +{ + json_t *jsch, *jsrc, *jdst, *jrec_member; + json_t *jrows, *jcols, *jidxs; + json_t *jrow, *jcol, *jfill, *jop; + decomp_static_row_cfg_t cfg_row; + decomp_static_col_cfg_t cfg_col; + decomp_static_cfg_t dcfg = NULL; + int row_no, col_no, rc; + + jrows = json_object_get(jcfg, "rows"); + if (!jrows || !json_is_array(jrows)) { + THISLOG(reqc, errno, + "strgp '%s': The 'rows' attribute is missing, " + "or its value is not a list.\n", + strgp->obj.name); + return NULL; + } + dcfg = calloc(1, sizeof(*dcfg) + json_array_size(jrows) * sizeof(dcfg->rows[0])); + if (!dcfg) + goto enomem; dcfg->decomp = decomp_static; /* for each row schema */ - i = 0; - TAILQ_FOREACH(jrow, &jrows->item_list, item_entry) { - if (jrow->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "The list item must be a dictionary.\n", - strgp->obj.name, i); + json_array_foreach(jrows, row_no, jrow) { + if (!json_is_object(jrow)) { + THISLOG(reqc, EINVAL, + "strgp '%s': row '%d': " + "The list item must be a dictionary.\n", + strgp->obj.name, row_no); goto err_0; } - cfg_row = &dcfg->rows[i]; + cfg_row = &dcfg->rows[row_no]; cfg_row->row_sz = sizeof(struct ldmsd_row_s); rbt_init(&cfg_row->mid_rbt, __mid_rbn_cmp); /* schema name */ - jstr = __jdict_str(jrow, "schema"); - if (!jstr) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "row['schema'] attribute is required\n", - strgp->obj.name, i); + jsch = json_object_get(jrow, "schema"); + if (!jsch || !json_is_string(jsch)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "row['schema'] attribute is required and must be a string\n", + strgp->obj.name, row_no); goto err_0; } - cfg_row->schema_name = strdup(jstr->str); + cfg_row->schema_name = strdup(json_string_value(jsch)); if (!cfg_row->schema_name) - goto err_enomem; + goto enomem; /* columns */ - jcols = __jdict_list(jrow, "cols"); - if (!jcols) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "row['cols'] list is required\n", - strgp->obj.name, i); + jcols = json_object_get(jrow, "cols"); + if (!jcols || !json_is_array(jcols)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "row['cols'] is required and must be an array.\n", + strgp->obj.name, row_no); goto err_0; } - cfg_row->col_count = jcols->item_count; + + cfg_row->col_count = json_array_size(jcols); if (!cfg_row->col_count) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "row['cols'] list is empty\n", - strgp->obj.name, i); + THISLOG(reqc, EINVAL, "strgp '%s': row '%d': " + "row['cols'] list is empty\n", + strgp->obj.name, row_no); goto err_0; } /* Add a configuration for the 'timestamp' column that goes in every row */ - cfg_row->cols = calloc(1, (cfg_row->col_count*sizeof(cfg_row->cols[0]))); + cfg_row->cols = calloc(1, (cfg_row->col_count * sizeof(cfg_row->cols[0]))); if (!cfg_row->cols) - goto err_enomem; + goto enomem; cfg_row->row_sz += cfg_row->col_count * sizeof(struct ldmsd_col_s); /* for each column */ - j = 0; - TAILQ_FOREACH(jcol, &jcols->item_list, item_entry) { - cfg_col = &cfg_row->cols[j]; - if (jcol->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col '%d': " - "a column must be a dictionary.\n", - strgp->obj.name, i, j); + json_array_foreach(jcols, col_no, jcol) { + if (!json_is_object(jcol)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d'--col '%d': " + "a column entry must be a dictionary.\n", + strgp->obj.name, row_no, col_no); goto err_0; } - jsrc = __jdict_str(jcol, "src"); - if (!jsrc) { - DECOMP_ERR(reqc, EINVAL, - "strgp '%s': row '%d': the 'src' attribute " - "is required.\n", - strgp->obj.name, i); + cfg_col = &cfg_row->cols[col_no]; + jsrc = json_object_get(jcol, "src"); + if (!jsrc || !json_is_string(jsrc)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d'--col '%d': " + "column['src'] is required and must be a string.\n", + strgp->obj.name, row_no, col_no); goto err_0; } - jfill = __jdict_ent(jcol, "fill"); - if (!jsrc) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d'--col '%d': " - "column['src'] is required\n", - strgp->obj.name, i, j); - goto err_0; - } - cfg_col->src = strdup(jsrc->str); + cfg_col->src = strdup(json_string_value(jsrc)); if (!cfg_col->src) - goto err_enomem; - jdst = __jdict_str(jcol, "dst"); - if (!jdst) { - cfg_col->dst = strdup(cfg_col->src); + goto enomem; + jdst = json_object_get(jcol, "dst"); + if (jdst && json_is_string(jdst)) { + cfg_col->dst = strdup(json_string_value(jdst)); } else { /* Inherit the destination name from the source */ - cfg_col->dst = strdup(jdst->str); + cfg_col->dst = strdup(cfg_col->src); } if (!cfg_col->dst) - goto err_enomem; - jrec_member = __jdict_str(jcol, "rec_member"); + goto enomem; + + jrec_member = json_object_get(jcol, "rec_member"); if (jrec_member) { - cfg_col->rec_member = strdup(jrec_member->str); + if (!json_is_string(jrec_member)) { + THISLOG(reqc, EINVAL, "strgp '%s': row '%d'--col '%d': " + "rec_member must be a string.\n", + strgp->obj.name, row_no, col_no); + goto err_0; + } + cfg_col->rec_member = strdup(json_string_value(jrec_member)); if (!cfg_col->rec_member) - goto err_enomem; + goto enomem; } - jop = __jdict_str(jcol, "op"); + + jop = json_object_get(jcol, "op"); if (jop) { - if (0 == strcmp(jop->str, "diff")) { - cfg_col->op_name = strdup(jop->str); + if (0 == strcmp(json_string_value(jop), "diff")) { + cfg_col->op_name = strdup(json_string_value(jop)); cfg_col->op = LDMSD_DECOMP_OP_DIFF; cfg_row->op_present = 1; /* true */ - } else if (0 == strcmp(jop->str, "mean")) { - cfg_col->op_name = strdup(jop->str); + } else if (0 == strcmp(json_string_value(jop), "mean")) { + cfg_col->op_name = strdup(json_string_value(jop)); cfg_col->op = LDMSD_DECOMP_OP_MEAN; cfg_row->op_present = 1; /* true */ - } else if (0 == strcmp(jop->str, "min")) { - cfg_col->op_name = strdup(jop->str); + } else if (0 == strcmp(json_string_value(jop), "min")) { + cfg_col->op_name = strdup(json_string_value(jop)); cfg_col->op = LDMSD_DECOMP_OP_MIN; cfg_row->op_present = 1; /* true */ - } else if (0 == strcmp(jop->str, "max")) { - cfg_col->op_name = strdup(jop->str); + } else if (0 == strcmp(json_string_value(jop), "max")) { + cfg_col->op_name = strdup(json_string_value(jop)); cfg_col->op = LDMSD_DECOMP_OP_MAX; cfg_row->op_present = 1; /* true */ } else { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d'--col %d : " - "unrecognized functinoal operator '%s'\n", - strgp->obj.name, i, j, jop->str); + THISLOG(reqc, EINVAL, "strgp '%s': row '%d'--col %d : " + "unrecognized functional operator '%s'\n", + strgp->obj.name, row_no, col_no, json_string_value(jop)); goto err_0; } } - jtype = __jdict_str(jcol, "type"); - if (jtype) { - cfg_col->type = ldms_metric_str_to_type(jtype->str); - if (cfg_col->type == LDMS_V_NONE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s'," - "column['type'] value is an unknown type: %s\n", - strgp->obj.name, i, cfg_col->dst, jtype->str); + jfill = json_object_get(jcol, "fill"); + if (jfill) { + /* The remaining code handles 'fill' */ + rc = mval_from_json(&cfg_col->fill, &cfg_col->type, + &cfg_col->fill_len, jfill); + if (rc) { + THISLOG(reqc, EINVAL, + "strgp '%s': row '%d': col[dst] '%s'," + "'fill' error %d preparing metric value.\n", + strgp->obj.name, row_no, cfg_col->dst, rc); goto err_0; } - } else { - /* Resolved in resolve_metrics */ - cfg_col->type = LDMS_V_NONE; - } - - if (!ldms_type_is_array(cfg_col->type)) - goto not_array_fill; - /* array routine */ - jarray_len = __jdict_ent(jcol, "array_len"); - if (jarray_len) { - if (jarray_len->type != JSON_INT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "column['array_len'] value must be an integer.\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - cfg_col->array_len = jarray_len->value.int_; - } else { - cfg_col->array_len = 0; - } - - /* fill */ - cfg_col->fill = calloc(cfg_col->array_len, __ldms_vsz(cfg_col->type)); - if (!cfg_col->fill) - goto err_enomem; - if (cfg_col->type == LDMS_V_CHAR_ARRAY) - goto str_fill; - /* array fill */ - if (!jfill) /* fill values are already 0 */ - goto next_col; - if (jfill->type != JSON_LIST_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "'fill' type mismatch: expecting a LIST\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - cfg_col->fill_len = jfill->value.list_->item_count; - if (cfg_col->fill_len > cfg_col->array_len) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "'fill' array length too long\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - rc = __array_fill_from_json(cfg_col->fill, cfg_col->type, jfill->value.list_); - if (rc) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "'fill' error: type mismatch\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - goto next_col; - str_fill: - /* str fill routine */ - if (!jfill) /* fill values are already 0 */ - goto next_col; - if (jfill->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "'fill' type mismatch: expecting a STRING\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; } - cfg_col->fill_len = jfill->value.str_->str_len + 1; - if (cfg_col->fill_len > cfg_col->array_len) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s': " - "'fill' array length too long\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - memcpy(cfg_col->fill, jfill->value.str_->str, cfg_col->fill_len); - goto next_col; - not_array_fill: - /* non-array fill routine */ - cfg_col->fill = &cfg_col->__fill; - if (!jfill) /* fill value is already 0 */ - goto next_col; - rc = __prim_fill_from_json(cfg_col->fill, cfg_col->type, jfill); - if (rc) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': col[dst] '%s'," - "'fill' error: type mismatch\n", - strgp->obj.name, i, cfg_col->dst); - goto err_0; - } - next_col: - j++; } /* indices */ - jidxs = __jdict_list(jrow, "indices"); - if (!jidxs) - goto next_row; - cfg_row->idx_count = jidxs->item_count; - cfg_row->idxs = calloc(1, cfg_row->idx_count*sizeof(cfg_row->idxs[0])); - if (!cfg_row->idxs) - goto err_enomem; - cfg_row->row_sz += cfg_row->idx_count * sizeof(ldmsd_row_index_t); - /* prep temporary col-id table */ - col_id_tbl = calloc(1, sizeof(*col_id_tbl) + - cfg_row->col_count*sizeof(col_id_tbl->ent[0])); - if (!col_id_tbl) - goto err_enomem; - col_id_tbl->len = cfg_row->col_count; - for (j = 0; j < cfg_row->col_count; j++) { - col_id_tbl->ent[j].str = cfg_row->cols[j].dst; - col_id_tbl->ent[j].i = j; - } - qsort(col_id_tbl->ent, col_id_tbl->len, sizeof(col_id_tbl->ent[0]), str_int_cmp); - /* foreach index */ - j = 0; - TAILQ_FOREACH(jidx, &jidxs->item_list, item_entry) { - didx = &cfg_row->idxs[j]; - if (jidx->type != JSON_DICT_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "an index must be a dictionary.\n", - strgp->obj.name, i); - goto err_0; - } - jname = __jdict_str(jidx, "name"); - if (!jname) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': index '%d': " - "index['name'] is required.\n", - strgp->obj.name, i, j); - goto err_0; - } - didx->name = strdup(jname->str); - if (!didx->name) - goto err_enomem; - jidx_cols = __jdict_list(jidx, "cols"); - if (!jidx_cols) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': index '%d':" - "index['cols'] is required.\n", - strgp->obj.name, i, j); + jidxs = json_object_get(jrow, "indices"); + if (jidxs) { + rc = handle_indices(strgp, jidxs, cfg_row, row_no, reqc); + if (rc) goto err_0; - } - didx->col_count = jidx_cols->item_count; - didx->col_idx = calloc(1, didx->col_count*sizeof(didx->col_idx[0])); - if (!didx->col_idx) - goto err_enomem; - cfg_row->row_sz += sizeof(struct ldmsd_row_index_s) + - didx->col_count * sizeof(ldmsd_col_t); - /* resolve col name to col id */ - k = 0; - TAILQ_FOREACH(jcol, &jidx_cols->item_list, item_entry) { - if (jcol->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': index '%d': col '%d': " - "index['cols'][x] value must be a string.\n", - strgp->obj.name, i, j, k); - goto err_0; - } - key.str = jcol->value.str_->str; - tbl_ent = bsearch(&key, col_id_tbl->ent, - col_id_tbl->len, - sizeof(col_id_tbl->ent[0]), - str_int_cmp); - if (!tbl_ent) { - DECOMP_ERR(reqc, ENOENT, "strgp '%s': row '%d': index '%d': " - "column '%s' not found.\n", - strgp->obj.name, i, j, key.str); - goto err_0; - } - didx->col_idx[k] = tbl_ent->i; - k++; - } - assert(k == jidx_cols->item_count); - j++; } - assert(j == jidxs->item_count); /* group clause */ - jgroup = __jdict_ent(jrow, "group"); + json_t *jgroup = json_object_get(jrow, "group"); if (jgroup) { - jstr = __jdict_str(jgroup, "limit"); - if (jstr) { - cfg_row->row_limit = strtoul(jstr->str, NULL, 0); - } else { - cfg_row->row_limit = 2; - } - /* Loop through all of the entries in the - "index" list and set the associated - column index in the group_cols array */ - jlist = __jdict_list(jgroup, "index"); - if (!jlist) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['index'] is a required entry.\n", - strgp->obj.name, i); - goto err_0; - } - - cfg_row->group_count = jlist->item_count; - cfg_row->group_cols = calloc(cfg_row->group_count, - sizeof(*cfg_row->group_cols)); - if (!cfg_row->group_cols) - goto err_enomem; - int col_no = 0; - TAILQ_FOREACH(jent, &jlist->item_list, item_entry) { - if (jent->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['index'] entries must be column " - "name strings.\n", - strgp->obj.name, i); - goto err_0; - } - cfg_row->group_cols[col_no] = get_col_no(cfg_row, jent->value.str_->str); - if (cfg_row->group_cols[col_no] < 0) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['index'] the specified column '%s'" - "is not present in the column list.\n", - strgp->obj.name, i, jent->value.str_->str); - goto err_0; - } - col_no ++; - } - - /* ========== */ - jlist = __jdict_list(jgroup, "order"); - if (!jlist) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['order'] is a required entry.\n", - strgp->obj.name, i); - goto err_0; - } - cfg_row->row_order_count = jlist->item_count; - cfg_row->row_order_cols = calloc(cfg_row->row_order_count, - sizeof(*cfg_row->row_order_cols)); - if (!cfg_row->row_order_cols) - goto err_enomem; - - col_no = 0; - TAILQ_FOREACH(jent, &jlist->item_list, item_entry) { - if (jent->type != JSON_STRING_VALUE) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['order'] entries must be column " - "name strings.\n", - strgp->obj.name, i); - goto err_0; - } - cfg_row->row_order_cols[col_no] = - get_col_no(cfg_row, jent->value.str_->str); - if (cfg_row->row_order_cols[col_no] < 0) { - DECOMP_ERR(reqc, EINVAL, "strgp '%s': row '%d': " - "group['order'] the specified column '%s'" - "is not present in the column list.\n", - strgp->obj.name, i, jent->value.str_->str); - goto err_0; - } - col_no ++; - } - - rc = init_row_cache(strgp, cfg_row->row_limit); + rc = handle_group(strgp, jgroup, cfg_row, row_no, reqc); if (rc) - goto err_enomem; + goto err_0; } - /* clean up temporary col-id table */ - free(col_id_tbl); - col_id_tbl = NULL; - next_row: dcfg->row_count++; - i++; } - assert(i == jrows->item_count); return &dcfg->decomp; - - err_enomem: - DECOMP_ERR(reqc, errno, "Not enough memory\n"); - err_0: +enomem: + THISLOG(reqc, errno, "%s: Insufficient memory.\n", strgp->obj.name); +err_0: decomp_static_cfg_free(dcfg); - free(col_id_tbl); return NULL; } @@ -898,7 +724,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, evp_ctx = EVP_MD_CTX_create(); if (!evp_ctx) { - ovis_log(mylog, OVIS_LERROR, "out of memory\n"); + ovis_log(static_log, OVIS_LERROR, "out of memory\n"); goto err; } EVP_DigestInit_ex(evp_ctx, EVP_sha256(), NULL); @@ -948,7 +774,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, if (cfg_row->cols[col_no].type) { if (ldms_type_is_array(cfg_row->cols[col_no].type)) { if (cfg_row->cols[col_no].array_len < 0) { - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': col[dst] '%s' " "array must have a len specified if it " "does not exist in the set.\n", @@ -966,7 +792,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, mid_rbn->col_mids[col_no].array_len = mlen; goto next; } - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': col[src] '%s' " "does not exist in the set and the 'type' is not " "specified.\n", @@ -991,7 +817,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, if (mtype > LDMS_V_D64_ARRAY) { /* Invalid type */ - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': col[src] '%s' " "the metric type %d is not supported.\n", strgp->obj.name, cfg_row->cols[col_no].src, @@ -1007,7 +833,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, le = ldms_list_first(set, lh, &mtype, &mlen); if (!le) { /* list empty. can't init yet */ - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': row '%d': col[dst] '%s' " "LIST is empty, skipping set metric resolution.\n", strgp->obj.name, col_no, cfg_row->cols[col_no].dst @@ -1018,7 +844,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, if (mtype == LDMS_V_LIST) { /* LIST of LIST is not supported */ /* Invalid type */ - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': row '%d': col[dst] '%s' " "LIST of LIST is not supported.\n", strgp->obj.name, col_no, cfg_row->cols[col_no].dst @@ -1048,7 +874,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, rec_array = ldms_metric_get(set, mid); rec = ldms_record_array_get_inst(rec_array, 0); if (!cfg_row->cols[col_no].rec_member) { - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': row '%d': the record array '%s' " "is emptyd.\n", strgp->obj.name, col_no, cfg_row->cols[col_no].src); @@ -1056,7 +882,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, } mid = ldms_record_metric_find(rec, cfg_row->cols[col_no].rec_member); if (mid < 0) { - ovis_log(mylog, OVIS_LERROR, + ovis_log(static_log, OVIS_LERROR, "strgp '%s': row '%d': col[dst] '%s' " "Missing record member definition.n", strgp->obj.name, col_no, cfg_row->cols[col_no].dst); @@ -1084,6 +910,7 @@ static int resolve_metrics(ldmsd_strgp_t strgp, /* Finalize row schema digest */ unsigned int len = LDMS_DIGEST_LENGTH; EVP_DigestFinal(evp_ctx, cfg_row->schema_digest.digest, &len); + EVP_MD_CTX_destroy(evp_ctx); return 0; err: EVP_MD_CTX_destroy(evp_ctx); @@ -1715,7 +1542,7 @@ static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, mval = ldms_metric_get(set, mid); mtype = ldms_metric_type_get(set, mid); if (mtype != mid_rbn->col_mids[j].mtype) { - ovis_log(mylog, OVIS_LERROR, "strgp '%s': the metric type (%s) of " + ovis_log(static_log, OVIS_LERROR, "strgp '%s': the metric type (%s) of " "row %d:col %d is different from the type (%s) of " "LDMS metric '%s'.\n", strgp->obj.name, ldms_metric_type_to_str(mid_rbn->col_mids[j].mtype), @@ -1913,19 +1740,20 @@ static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, if (cfg_row->op_present) { ldmsd_row_cache_idx_t group_idx; ldmsd_row_cache_idx_t row_idx; - ldmsd_row_cache_key_t keys; + ldmsd_row_cache_key_t *keys; ldmsd_row_t dup_row; /* Build the group key */ keys = calloc(cfg_row->group_count, sizeof(*keys)); - group_idx = ldmsd_row_cache_idx_create(cfg_row->group_count, keys); - - /* Initialize the metric types in the key values */ for (j = 0; j < cfg_row->group_count; j++) { - keys[j].type = row->cols[cfg_row->group_cols[j]].type; - keys[j].val = *row->cols[cfg_row->group_cols[j]].mval; - keys[j].count = row->cols[cfg_row->group_cols[j]].array_len; + keys[j] = ldmsd_row_cache_key_create( + row->cols[cfg_row->group_cols[j]].type, + row->cols[cfg_row->group_cols[j]].array_len); + memcpy(keys[j]->mval, + row->cols[cfg_row->group_cols[j]].mval, + keys[j]->mval_size); } + group_idx = ldmsd_row_cache_idx_create(cfg_row->group_count, keys); /* Build the row-order key */ keys = calloc(cfg_row->row_order_count, sizeof(*keys)); @@ -1933,9 +1761,12 @@ static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, /* Initialize the metric types in the key values */ for (j = 0; j < cfg_row->row_order_count; j++) { - keys[j].type = row->cols[cfg_row->row_order_cols[j]].type; - keys[j].val = *row->cols[cfg_row->row_order_cols[j]].mval; - keys[j].count = row->cols[cfg_row->row_order_cols[j]].array_len; + keys[j] = ldmsd_row_cache_key_create( + row->cols[cfg_row->row_order_cols[j]].type, + row->cols[cfg_row->row_order_cols[j]].array_len); + memcpy(keys[j]->mval, + row->cols[cfg_row->row_order_cols[j]].mval, + keys[j]->mval_size); } /* Cache the current, unmodified row */ @@ -1957,16 +1788,16 @@ static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, strgp->row_cache, group_idx); if (count != cfg_row->group_count) - ovis_log(mylog, OVIS_LWARN, + ovis_log(static_log, OVIS_LWARN, "strgp '%s': insufficent rows in " "cache to satisfy functional operator '%s' " "on column '%s'.\n", strgp->obj.name, cfg_col->op_name, cfg_col->dst); - cfg_col = &cfg_row->cols[j]; rc = op_table[cfg_col->op](&row_list, dup_row, j); } + ldmsd_row_cache_idx_free(group_idx); row = dup_row; } TAILQ_INSERT_TAIL(row_list, row, entry); @@ -1987,7 +1818,7 @@ static int decomp_static_decompose(ldmsd_strgp_t strgp, ldms_set_t set, } static void decomp_static_release_rows(ldmsd_strgp_t strgp, - ldmsd_row_list_t row_list) + ldmsd_row_list_t row_list) { ldmsd_row_t row; while ((row = TAILQ_FIRST(row_list))) { diff --git a/ldms/src/ldmsd/Makefile.am b/ldms/src/ldmsd/Makefile.am index 202294fa4..631dce29a 100644 --- a/ldms/src/ldmsd/Makefile.am +++ b/ldms/src/ldmsd/Makefile.am @@ -46,7 +46,7 @@ ldmsd_SOURCES = ldmsd.c ldmsd_config.c \ ldmsd_failover.c ldmsd_group.c ldmsd_auth.c \ ldmsd_decomp.c ldmsd_row_cache.c ldmsd_LDADD = ../core/libldms.la libldmsd_request.la libldmsd_stream.la \ - $(LZAP) $(LMMALLOC) $(LOVIS_UTIL) $(LCOLL) $(LJSON_UTIL) \ + $(LZAP) $(LMMALLOC) $(LOVIS_UTIL) $(LCOLL) $(LJSON_UTIL) $(LTLIBJANSSON) \ $(LOVIS_EVENT) $(LOVIS_EV) -lpthread $(LOVIS_CTRL) -lm -ldl \ $(LOVIS_LOG) ldmsd_CFLAGS = $(AM_CFLAGS) diff --git a/ldms/src/ldmsd/ldmsd.h b/ldms/src/ldmsd/ldmsd.h index 493dd66ac..851552f9d 100644 --- a/ldms/src/ldmsd/ldmsd.h +++ b/ldms/src/ldmsd/ldmsd.h @@ -60,6 +60,7 @@ #include #include +#include #include #include @@ -536,20 +537,23 @@ typedef struct ldmsd_row_cache_entry_s { typedef struct ldmsd_row_cache_key_s { enum ldms_value_type type; - union ldms_value val; size_t count; /* The element count if an array */ + size_t mval_size; + ldms_mval_t mval; } *ldmsd_row_cache_key_t; struct ldmsd_row_cache_idx_s { int key_count; - ldmsd_row_cache_key_t keys; /* Array of ldmsd_row_cache_key_t */ + ldmsd_row_cache_key_t *keys; /* Array of ldmsd_row_cache_key_t */ }; typedef struct ldmsd_row_s *ldmsd_row_t; typedef struct ldmsd_row_list_s *ldmsd_row_list_t; ldmsd_row_cache_t ldmsd_row_cache_create(ldmsd_strgp_t strgp, int row_count); -ldmsd_row_cache_idx_t ldmsd_row_cache_idx_create(int key_count, ldmsd_row_cache_key_t keys); +ldmsd_row_cache_key_t ldmsd_row_cache_key_create(enum ldms_value_type type, size_t len); +ldmsd_row_cache_idx_t ldmsd_row_cache_idx_create(int key_count, ldmsd_row_cache_key_t *keys); +void ldmsd_row_cache_idx_free(ldmsd_row_cache_idx_t idx); int ldmsd_row_cache(ldmsd_row_cache_t rcache, ldmsd_row_cache_idx_t group_key, ldmsd_row_cache_idx_t row_key, @@ -638,7 +642,7 @@ struct ldmsd_decomp_s { * \retval NULL If there is an error. In this case, \c errno must also * be set to describe the error. */ - ldmsd_decomp_t (*config)(ldmsd_strgp_t strgp, json_entity_t jcfg, ldmsd_req_ctxt_t reqc); + ldmsd_decomp_t (*config)(ldmsd_strgp_t strgp, json_t *jcfg, ldmsd_req_ctxt_t reqc); /** * Decompose method. diff --git a/ldms/src/ldmsd/ldmsd_decomp.c b/ldms/src/ldmsd/ldmsd_decomp.c index eef89ea2b..210138765 100644 --- a/ldms/src/ldmsd/ldmsd_decomp.c +++ b/ldms/src/ldmsd/ldmsd_decomp.c @@ -61,7 +61,7 @@ #include -#include "ovis_json/ovis_json.h" +#include #include "coll/rbt.h" #include "ldmsd.h" @@ -76,38 +76,7 @@ enum ldmsd_decomp_type_e { LDMSD_DECOMP_AS_IS, }; -/* ==== JSON helpers ==== */ - -static json_entity_t __jdict_ent(json_entity_t dict, const char *key) -{ - json_entity_t attr; - json_entity_t val; - - attr = json_attr_find(dict, key); - if (!attr) { - errno = ENOKEY; - return NULL; - } - val = json_attr_value(attr); - return val; -} - -/* Access dict[key], expecting it to be a str */ -static json_str_t __jdict_str(json_entity_t dict, const char *key) -{ - json_entity_t val; - - val = __jdict_ent(dict, key); - if (!val || val->type != JSON_STRING_VALUE) { - errno = EINVAL; - return NULL; - } - return val->value.str_; -} - - -/* ==== generic decomp ==== */ -/* convenient macro to put error message in both ldmsd log and `reqc` */ +/* Macro to put error message in both ldmsd log and `reqc` */ #define DECOMP_ERR(reqc, rc, fmt, ...) do { \ ovis_log(store_log, OVIS_LERROR, "decomposer: " fmt, ##__VA_ARGS__); \ if (reqc) { \ @@ -116,20 +85,20 @@ static json_str_t __jdict_str(json_entity_t dict, const char *key) } \ } while (0) -int __decomp_rbn_cmp(void *tree_key, const void *key) +static int decomp_rbn_cmp(void *tree_key, const void *key) { return strcmp(tree_key, key); } -static struct rbt __decomp_rbt = RBT_INITIALIZER(__decomp_rbn_cmp); -struct __decomp_rbn_s { +static struct rbt decomp_rbt = RBT_INITIALIZER(decomp_rbn_cmp); +struct decomp_rbn_s { struct rbn rbn; char name[256]; ldmsd_decomp_t decomp; }; -typedef struct __decomp_rbn_s *__decomp_rbn_t; +typedef struct decomp_rbn_s *decomp_rbn_t; -static ldmsd_decomp_t __decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) +static ldmsd_decomp_t decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) { char library_name[PATH_MAX]; char library_path[PATH_MAX]; @@ -141,10 +110,10 @@ static ldmsd_decomp_t __decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) char *dlerr; struct stat st; int rc; - __decomp_rbn_t drbn; + decomp_rbn_t drbn; ldmsd_decomp_t (*get)(), dc; - drbn = (void*)rbt_find(&__decomp_rbt, decomp); + drbn = (void*)rbt_find(&decomp_rbt, decomp); if (drbn) return drbn->decomp; @@ -212,7 +181,7 @@ static ldmsd_decomp_t __decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) rbn_init(&drbn->rbn, drbn->name); snprintf(drbn->name, sizeof(drbn->name), "%s", decomp); drbn->decomp = dc; - rbt_ins(&__decomp_rbt, &drbn->rbn); + rbt_ins(&decomp_rbt, &drbn->rbn); return dc; } @@ -220,106 +189,87 @@ static ldmsd_decomp_t __decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) /* Export so that decomp_flex can call, but don't advertise this in ldmsd.h */ ldmsd_decomp_t ldmsd_decomp_get(const char *decomp, ldmsd_req_ctxt_t reqc) { - return __decomp_get(decomp, reqc); -} + return decomp_get(decomp, reqc); +} + +#if JANSSON_VERSION_HEX >= 0x020b00 +const char *err_text[] = { + [json_error_unknown] = "unknown error", + [json_error_out_of_memory] = "out of memory", + [json_error_stack_overflow] = "stack overflow", + [json_error_cannot_open_file] = "cannot open file", + [json_error_invalid_argument] = "invalid argument", + [json_error_invalid_utf8] = "invalid UTF8", + [json_error_premature_end_of_input] = "unexpected end of file", + [json_error_end_of_input_expected] = "unexpected data at end of object", + [json_error_invalid_syntax] = "invalid syntax", + [json_error_invalid_format] = "invalid format", + [json_error_wrong_type] = "wrong type", + [json_error_null_character] = "null character", + [json_error_null_value] = "null value", + [json_error_null_byte_in_key] = "null byte in key", + [json_error_duplicate_key] = "duplicate key", + [json_error_numeric_overflow] = "numeric overflow", + [json_error_item_not_found] = "item not found", + [json_error_index_out_of_range] = "index out of range" +}; +#endif /* protected by strgp lock */ int ldmsd_decomp_config(ldmsd_strgp_t strgp, const char *json_path, ldmsd_req_ctxt_t reqc) { - json_str_t s; - json_entity_t cfg; - int fd, rc; - off_t off; - size_t sz; - json_parser_t jp = NULL; - char *buff = NULL; + int rc; + json_t *root, *type; + json_error_t jerr; ldmsd_decomp_t decomp_api; if (strgp->decomp) { /* already configured */ rc = EALREADY; - DECOMP_ERR(reqc, EALREADY, "Already configurd\n"); + DECOMP_ERR(reqc, EALREADY, "Already configured.\n"); goto err_0; } - /* Load JSON from file */ - fd = open(json_path, O_RDONLY); - if (fd < 0) { - rc = errno; - DECOMP_ERR(reqc, errno, "open error %d, file: %s\n", - errno, json_path); - goto err_0; - } - off = lseek(fd, 0, SEEK_END); - if (off == -1) { - rc = errno; - DECOMP_ERR(reqc, errno, "seek failed, errno: %d\n", errno); - goto err_1; - } - sz = off; - buff = malloc(sz + 1); - if (!buff) { - rc = errno; - DECOMP_ERR(reqc, errno, "not enough memory"); + root = json_load_file(json_path, 0, &jerr); + if (!root) { + rc = errno = EINVAL; + DECOMP_ERR(reqc, rc, + "json parser error: line %d column: %d %s: %s\n", + jerr.line, jerr.column, + #if JANSSON_VERSION_HEX >= 0x020b00 + err_text[json_error_code(&jerr)], + #else + "", + #endif + jerr.text); goto err_1; } - off = lseek(fd, 0, SEEK_SET); - if (off == -1) { - rc = errno; - DECOMP_ERR(reqc, errno, "seek failed, errno: %d\n", errno); - goto err_2; - } - jp = json_parser_new(0); - if (!jp) { - rc = errno; - DECOMP_ERR(reqc, errno, "cannot create json parser, error: %d\n", errno); - goto err_2; - } - off = read(fd, buff, sz); - if (off != sz) { - rc = errno; - DECOMP_ERR(reqc, errno, "read failed, error: %d\n", errno); - goto err_2; - } - buff[sz] = '\0'; - rc = json_parse_buffer(jp, buff, sz, &cfg); - if (rc) { - DECOMP_ERR(reqc, rc, "json parse error: %d\n", rc); - errno = rc; - goto err_3; - } /* Configure decomposer */ - s = __jdict_str(cfg, "type"); - if (!s) { + type = json_object_get(root, "type"); + if (!type) { rc = errno = EINVAL; - DECOMP_ERR(reqc, EINVAL, "decomposer: 'type' attribute is missing.\n"); - goto err_4; + DECOMP_ERR(reqc, rc, + "json parser error: JSON configuration is missing the " + "'type' attribute.\n"); + goto err_1; } - decomp_api = __decomp_get(s->str, reqc); + decomp_api = decomp_get(json_string_value(type), reqc); if (!decomp_api) { rc = errno; - goto err_4; + goto err_1; } - strgp->decomp = decomp_api->config(strgp, cfg, reqc); + strgp->decomp = decomp_api->config(strgp, root, reqc); if (!strgp->decomp) { rc = errno; - goto err_4; + goto err_1; } /* decomp config success! */ rc = 0; - - /* let-through, clean-up */ - err_4: - json_entity_free(cfg); - err_3: - json_parser_free(jp); - err_2: - free(buff); - err_1: - close(fd); - err_0: +err_1: + json_decref(root); +err_0: return rc; } diff --git a/ldms/src/ldmsd/ldmsd_row_cache.c b/ldms/src/ldmsd/ldmsd_row_cache.c index d767eb21e..8ed76924a 100644 --- a/ldms/src/ldmsd/ldmsd_row_cache.c +++ b/ldms/src/ldmsd/ldmsd_row_cache.c @@ -41,86 +41,86 @@ static int tree_comparator(void *a, const void *b) ldmsd_row_cache_idx_t key_b = (ldmsd_row_cache_idx_t)b; ldmsd_row_cache_key_t rowk_a, rowk_b; for (i = 0; i < key_a->key_count; i++) { - rowk_a = &key_a->keys[i]; - rowk_b = &key_b->keys[i]; + rowk_a = key_a->keys[i]; + rowk_b = key_b->keys[i]; switch (rowk_a->type) { case LDMS_V_TIMESTAMP: - if (rowk_a->val.v_ts.sec < rowk_b->val.v_ts.sec) + if (rowk_a->mval->v_ts.sec < rowk_b->mval->v_ts.sec) return -1; - if (rowk_a->val.v_ts.sec > rowk_b->val.v_ts.sec) + if (rowk_a->mval->v_ts.sec > rowk_b->mval->v_ts.sec) return 1; - if (rowk_a->val.v_ts.usec < rowk_b->val.v_ts.usec) + if (rowk_a->mval->v_ts.usec < rowk_b->mval->v_ts.usec) return -1; - if (rowk_a->val.v_ts.usec > rowk_b->val.v_ts.usec) + if (rowk_a->mval->v_ts.usec > rowk_b->mval->v_ts.usec) return 1; return 0; case LDMS_V_CHAR_ARRAY: - return strncmp(rowk_a->val.a_char, rowk_b->val.a_char, + return strncmp(rowk_a->mval->a_char, rowk_b->mval->a_char, rowk_a->count); case LDMS_V_CHAR: - if (rowk_a->val.v_char == rowk_b->val.v_char) + if (rowk_a->mval->v_char == rowk_b->mval->v_char) continue; - if (rowk_a->val.v_char > rowk_b->val.v_char) + if (rowk_a->mval->v_char > rowk_b->mval->v_char) return 1; return -1; case LDMS_V_U8: - if (rowk_a->val.v_u8 == rowk_b->val.v_u8) + if (rowk_a->mval->v_u8 == rowk_b->mval->v_u8) continue; - if (rowk_a->val.v_u8 > rowk_b->val.v_u8) + if (rowk_a->mval->v_u8 > rowk_b->mval->v_u8) return 1; return -1; case LDMS_V_S8: - if (rowk_a->val.v_s8 == rowk_b->val.v_s8) + if (rowk_a->mval->v_s8 == rowk_b->mval->v_s8) continue; - if (rowk_a->val.v_s8 > rowk_b->val.v_s8) + if (rowk_a->mval->v_s8 > rowk_b->mval->v_s8) return 1; return -1; case LDMS_V_U16: - if (rowk_a->val.v_u16 == rowk_b->val.v_u16) + if (rowk_a->mval->v_u16 == rowk_b->mval->v_u16) continue; - if (rowk_a->val.v_u16 > rowk_b->val.v_u16) + if (rowk_a->mval->v_u16 > rowk_b->mval->v_u16) return 1; return -1; case LDMS_V_S16: - if (rowk_a->val.v_s16 == rowk_b->val.v_s16) + if (rowk_a->mval->v_s16 == rowk_b->mval->v_s16) continue; - if (rowk_a->val.v_s16 > rowk_b->val.v_s16) + if (rowk_a->mval->v_s16 > rowk_b->mval->v_s16) return 1; return -1; case LDMS_V_U32: - if (rowk_a->val.v_u32 == rowk_b->val.v_u32) + if (rowk_a->mval->v_u32 == rowk_b->mval->v_u32) continue; - if (rowk_a->val.v_u32 > rowk_b->val.v_u32) + if (rowk_a->mval->v_u32 > rowk_b->mval->v_u32) return 1; return -1; case LDMS_V_S32: - if (rowk_a->val.v_s32 == rowk_b->val.v_s32) + if (rowk_a->mval->v_s32 == rowk_b->mval->v_s32) continue; - if (rowk_a->val.v_s32 > rowk_b->val.v_s32) + if (rowk_a->mval->v_s32 > rowk_b->mval->v_s32) return 1; return -1; case LDMS_V_U64: - if (rowk_a->val.v_u64 == rowk_b->val.v_u64) + if (rowk_a->mval->v_u64 == rowk_b->mval->v_u64) continue; - if (rowk_a->val.v_u64 > rowk_b->val.v_u64) + if (rowk_a->mval->v_u64 > rowk_b->mval->v_u64) return 1; return -1; case LDMS_V_S64: - if (rowk_a->val.v_s64 == rowk_b->val.v_s64) + if (rowk_a->mval->v_s64 == rowk_b->mval->v_s64) continue; - if (rowk_a->val.v_s64 > rowk_b->val.v_s64) + if (rowk_a->mval->v_s64 > rowk_b->mval->v_s64) return 1; return -1; case LDMS_V_F32: - if (rowk_a->val.v_f == rowk_b->val.v_f) + if (rowk_a->mval->v_f == rowk_b->mval->v_f) continue; - if (rowk_a->val.v_f > rowk_b->val.v_f) + if (rowk_a->mval->v_f > rowk_b->mval->v_f) return 1; return -1; case LDMS_V_D64: - if (rowk_a->val.v_d == rowk_b->val.v_d) + if (rowk_a->mval->v_d == rowk_b->mval->v_d) continue; - if (rowk_a->val.v_d > rowk_b->val.v_d) + if (rowk_a->mval->v_d > rowk_b->mval->v_d) return 1; return -1; default: @@ -151,6 +151,28 @@ ldmsd_row_cache_t ldmsd_row_cache_create(ldmsd_strgp_t strgp, int row_limit) return rcache; } + +/** + * @brief ldmsd_row_cache_key_create + * + * Returns an ldmsd_row_cache_key of sufficient size to contain + * the specified type and array count. + * + * @param type The ldms value type + * @param len The size of the array if type is an array + * @return ldmsd_row_cache_key_t + */ +ldmsd_row_cache_key_t ldmsd_row_cache_key_create(enum ldms_value_type type, size_t len) +{ + size_t size = ldms_metric_value_size_get(type, len); + ldmsd_row_cache_key_t key = calloc(1, sizeof(*key) + size); + key->count = len; + key->type = type; + key->mval_size = size; + key->mval = (ldms_mval_t)(key+1); + return key; +} + /** * \brief ldmsd_row_idx_create * @@ -159,7 +181,7 @@ ldmsd_row_cache_t ldmsd_row_cache_create(ldmsd_strgp_t strgp, int row_limit) * such that operators such as 'diff' make sense, e.g. ordered by * 'job_id', 'component_id', and 'timestamp'. */ -ldmsd_row_cache_idx_t ldmsd_row_cache_idx_create(int key_count, ldmsd_row_cache_key_t keys) +ldmsd_row_cache_idx_t ldmsd_row_cache_idx_create(int key_count, ldmsd_row_cache_key_t *keys) { ldmsd_row_cache_idx_t row_idx = calloc(1, sizeof(*row_idx)); if (!row_idx) @@ -170,6 +192,54 @@ ldmsd_row_cache_idx_t ldmsd_row_cache_idx_create(int key_count, ldmsd_row_cache_ return row_idx; } +ldmsd_row_cache_idx_t ldmsd_row_cache_idx_dup(ldmsd_row_cache_idx_t idx) +{ + int i; + ldmsd_row_cache_idx_t dup_idx = calloc(1, sizeof(*dup_idx)); + if (!dup_idx) + return NULL; + dup_idx->key_count = idx->key_count; + dup_idx->keys = calloc(idx->key_count, sizeof(*dup_idx->keys)); + if (!dup_idx->keys) + goto err_0; + for (i = 0; i < dup_idx->key_count; i++) { + dup_idx->keys[i] = + calloc(1, idx->keys[i]->mval_size + sizeof(*dup_idx->keys[i])); + if (!dup_idx->keys[i]) + goto err_1; + dup_idx->keys[i]->count = idx->keys[i]->count; + dup_idx->keys[i]->mval_size = idx->keys[i]->mval_size; + dup_idx->keys[i]->mval = (ldms_mval_t)(dup_idx->keys[i] + 1); + dup_idx->keys[i]->type = idx->keys[i]->type; + memcpy(dup_idx->keys[i]->mval, idx->keys[i]->mval, + idx->keys[i]->mval_size); + } + return dup_idx; +err_1: + while (i >= 0) { + free(dup_idx->keys[i]); + i -= 1; + } + free(dup_idx->keys); +err_0: + free(dup_idx); + return NULL; +} + +void ldmsd_row_cache_key_free(ldmsd_row_cache_key_t key) +{ + free(key); +} + +void ldmsd_row_cache_idx_free(ldmsd_row_cache_idx_t idx) +{ + int i; + for (i = 0; i < idx->key_count; i++) + ldmsd_row_cache_key_free(idx->keys[i]); + free(idx->keys); + free(idx); +} + int ldmsd_row_cache(ldmsd_row_cache_t rcache, ldmsd_row_cache_idx_t group_key, ldmsd_row_cache_idx_t row_key, @@ -190,24 +260,24 @@ int ldmsd_row_cache(ldmsd_row_cache_t rcache, if (!group_rbn) { /* Create a new group and add it to the tree */ group = calloc(1, sizeof(*group)); + group->row_key_count = row_key->key_count; rbt_init(&group->row_tree, tree_comparator); + group_key = ldmsd_row_cache_idx_dup(group_key); rbn_init(&group->rbn, group_key); rbt_ins(&rcache->group_tree, &group->rbn); group_rbn = &group->rbn; } + group = container_of(group_rbn, struct ldmsd_row_group_s, rbn); if (rbt_card(&group->row_tree) == rcache->row_limit) { ldmsd_row_cache_entry_t cent; struct rbn *rbn; rbn = rbt_min(&group->row_tree); - assert(rbn); cent = container_of(rbn, struct ldmsd_row_cache_entry_s, rbn); - assert(rbn); - free(cent->idx->keys); - free(cent->idx); - free(cent->row); rbt_del(&group->row_tree, rbn); + ldmsd_row_cache_idx_free(cent->idx); + free(cent->row); free(cent); }