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); }