On Wed, Jun 24, 2020 at 02:48:23PM +0100, Ciara Power wrote: > Arrays and Dicts now support uint64_t, int and string > array values. Only one level of recursion supported. > > Signed-off-by: Ciara Power <ciara.po...@intel.com> >
A number of comments inline below. Once fixed you can include my ack on V3. Acked-by: Bruce Richardson <bruce.richard...@intel.com> > --- > v2: > - Added support for arrays to have container values. > - Added support for int and string arrays within dict/array. > - Added APIs for internal container memory management. > --- > lib/librte_telemetry/rte_telemetry.h | 67 +++++++++++++++++++ > .../rte_telemetry_version.map | 4 ++ > lib/librte_telemetry/telemetry.c | 56 ++++++++++++++++ > lib/librte_telemetry/telemetry_data.c | 51 ++++++++++++++ > lib/librte_telemetry/telemetry_data.h | 7 ++ > lib/librte_telemetry/telemetry_json.h | 33 +++++++++ > 6 files changed, 218 insertions(+) > > diff --git a/lib/librte_telemetry/rte_telemetry.h > b/lib/librte_telemetry/rte_telemetry.h > index eb7f2c917..89aff3429 100644 > --- a/lib/librte_telemetry/rte_telemetry.h > +++ b/lib/librte_telemetry/rte_telemetry.h > @@ -44,6 +44,7 @@ enum rte_tel_value_type { > RTE_TEL_STRING_VAL, /** a string value */ > RTE_TEL_INT_VAL, /** a signed 32-bit int value */ > RTE_TEL_U64_VAL, /** an unsigned 64-bit int value */ > + RTE_TEL_CONTAINER, /** a container struct */ > }; > > /** > @@ -134,6 +135,28 @@ __rte_experimental > int > rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); > > +/** > + * Add a container to an array. I think you need to explain the term container a bit, as in "an existing telemetry data array", or similar. > + * The array must have been started by rte_tel_data_start_array() with > + * RTE_TEL_CONTAINER as the type parameter. The rte_tel_data type > + * must be an array of type u64/int/string. > + * > + * @param d > + * The data structure passed to the callback > + * @param val > + * The rte_tel_data pointer to be returned in the container. "The pointer to the container to be stored in the array" > + * @param keep > + * Integer value to represent if rte_tel_data memory should be freed > + * after use. This would be clearer as a boolean, or call it a flag. How about: "Flag to indicate that the container memory should not be automatically freed by the telemetry library once it has finished with the data" > + * 1 = keep, 0 = free. > + * @return > + * 0 on success, negative errno on error > + */ > +__rte_experimental > +int > +rte_tel_data_add_array_container(struct rte_tel_data *d, > + struct rte_tel_data *val, int keep); > + > /** > * Add a string value to a dictionary. > * The dict must have been started by rte_tel_data_start_dict(). > @@ -188,6 +211,27 @@ int > rte_tel_data_add_dict_u64(struct rte_tel_data *d, > const char *name, uint64_t val); > > +/** > + * Add a container to a dictionary. > + * The dict must have been started by rte_tel_data_start_dict(). > + * The rte_tel_data type must be an array of type u64/int/string. > + * > + * @param d > + * The data structure passed to the callback > + * @param val > + * The rte_tel_data pointer to be returned in the container. > + * @param keep > + * Integer value to represent if rte_tel_data memory should be freed > + * after use. > + * 1 = keep, 0 = free. > + * @return > + * 0 on success, negative errno on error > + */ Similar comments to the above here. > +__rte_experimental > +int > +rte_tel_data_add_dict_container(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val, int keep); > + > /** > * This telemetry callback is used when registering a telemetry command. > * It handles getting and formatting information to be returned to telemetry > @@ -262,4 +306,27 @@ int > rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, > const char **err_str); > > +/** > + * @internal > + * Get a data object with memory allocated. > + * Do we want to use the term container here also? > + * @return > + * Pointer to rte_tel_data. > + */ > +__rte_experimental > +struct rte_tel_data * > +rte_tel_data_alloc(void); > + > +/** > + * @internal > + * Free a data object that has memory allocated. > + * > + * @param data > + * Pointer to rte_tel_data. > + *. > + */ > +__rte_experimental > +void > +rte_tel_data_free(struct rte_tel_data *data); > + > #endif > diff --git a/lib/librte_telemetry/rte_telemetry_version.map > b/lib/librte_telemetry/rte_telemetry_version.map > index 86433c21d..d1dbf8d58 100644 > --- a/lib/librte_telemetry/rte_telemetry_version.map > +++ b/lib/librte_telemetry/rte_telemetry_version.map > @@ -1,12 +1,16 @@ > EXPERIMENTAL { > global: > > + rte_tel_data_add_array_container; > rte_tel_data_add_array_int; > rte_tel_data_add_array_string; > rte_tel_data_add_array_u64; > + rte_tel_data_add_dict_container; > rte_tel_data_add_dict_int; > rte_tel_data_add_dict_string; > rte_tel_data_add_dict_u64; > + rte_tel_data_alloc; > + rte_tel_data_free; > rte_tel_data_start_array; > rte_tel_data_start_dict; > rte_tel_data_string; > diff --git a/lib/librte_telemetry/telemetry.c > b/lib/librte_telemetry/telemetry.c > index e7e3d861d..a36f60a6c 100644 > --- a/lib/librte_telemetry/telemetry.c > +++ b/lib/librte_telemetry/telemetry.c > @@ -120,6 +120,35 @@ command_help(const char *cmd __rte_unused, const char > *params, > return 0; > } > > +static int > +recursive_data_json(const struct rte_tel_data *d, char *out_buf, size_t > buf_len) Not sure recursive in the name is great. Do we want to call this "container_to_json" or "array_to_json" perhaps? > +{ > + size_t used = 0; > + unsigned int i; > + > + if (d->type != RTE_TEL_ARRAY_U64 && d->type != RTE_TEL_ARRAY_INT > + && d->type != RTE_TEL_ARRAY_STRING) > + return snprintf(out_buf, buf_len, "null"); > + > + used = rte_tel_json_empty_array(out_buf, buf_len, 0); > + if (d->type == RTE_TEL_ARRAY_U64) > + for (i = 0; i < d->data_len; i++) > + used = rte_tel_json_add_array_u64(out_buf, > + buf_len, used, > + d->data.array[i].u64val); > + if (d->type == RTE_TEL_ARRAY_INT) > + for (i = 0; i < d->data_len; i++) > + used = rte_tel_json_add_array_int(out_buf, > + buf_len, used, > + d->data.array[i].ival); > + if (d->type == RTE_TEL_ARRAY_STRING) > + for (i = 0; i < d->data_len; i++) > + used = rte_tel_json_add_array_string(out_buf, > + buf_len, used, > + d->data.array[i].sval); > + return used; > +} > + > static void > output_json(const char *cmd, const struct rte_tel_data *d, int s) > { > @@ -166,6 +195,20 @@ output_json(const char *cmd, const struct rte_tel_data > *d, int s) > buf_len, used, > v->name, v->value.u64val); > break; > + case RTE_TEL_CONTAINER: > + { > + char temp[buf_len]; > + const struct container *cont = > + &v->value.container; > + if (recursive_data_json(cont->data, > + temp, buf_len) != 0) > + used = rte_tel_json_add_obj_json( > + cb_data_buf, > + buf_len, used, > + v->name, temp); > + if (!cont->keep) > + rte_tel_data_free(cont->data); > + } > } > } > used += prefix_used; > @@ -174,6 +217,7 @@ output_json(const char *cmd, const struct rte_tel_data > *d, int s) > case RTE_TEL_ARRAY_STRING: > case RTE_TEL_ARRAY_INT: > case RTE_TEL_ARRAY_U64: > + case RTE_TEL_ARRAY_CONTAINER: > prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", > MAX_CMD_LEN, cmd); > cb_data_buf = &out_buf[prefix_used]; > @@ -194,6 +238,18 @@ output_json(const char *cmd, const struct rte_tel_data > *d, int s) > used = rte_tel_json_add_array_u64(cb_data_buf, > buf_len, used, > d->data.array[i].u64val); > + else if (d->type == RTE_TEL_ARRAY_CONTAINER) { > + char temp[buf_len]; > + const struct container *rec_data = > + &d->data.array[i].container; > + if (recursive_data_json(rec_data->data, > + temp, buf_len) != 0) > + used = rte_tel_json_add_array_json( > + cb_data_buf, > + buf_len, used, temp); > + if (!rec_data->keep) > + rte_tel_data_free(rec_data->data); > + } > used += prefix_used; > used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); > break; > diff --git a/lib/librte_telemetry/telemetry_data.c > b/lib/librte_telemetry/telemetry_data.c > index f424bbd48..77b0fe09a 100644 > --- a/lib/librte_telemetry/telemetry_data.c > +++ b/lib/librte_telemetry/telemetry_data.c > @@ -14,6 +14,7 @@ rte_tel_data_start_array(struct rte_tel_data *d, enum > rte_tel_value_type type) > RTE_TEL_ARRAY_STRING, /* RTE_TEL_STRING_VAL = 0 */ > RTE_TEL_ARRAY_INT, /* RTE_TEL_INT_VAL = 1 */ > RTE_TEL_ARRAY_U64, /* RTE_TEL_u64_VAL = 2 */ > + RTE_TEL_ARRAY_CONTAINER, /* RTE_TEL_CONTAINER = 3 */ > }; > d->type = array_types[type]; > d->data_len = 0; > @@ -74,6 +75,23 @@ rte_tel_data_add_array_u64(struct rte_tel_data *d, > uint64_t x) > return 0; > } > > +int > +rte_tel_data_add_array_container(struct rte_tel_data *d, > + struct rte_tel_data *val, int keep) > +{ > + if (d->type != RTE_TEL_ARRAY_CONTAINER || > + (val->type != RTE_TEL_ARRAY_U64 > + && val->type != RTE_TEL_ARRAY_INT > + && val->type != RTE_TEL_ARRAY_STRING)) > + return -EINVAL; > + if (d->data_len >= RTE_TEL_MAX_ARRAY_ENTRIES) > + return -ENOSPC; > + > + d->data.array[d->data_len].container.data = val; > + d->data.array[d->data_len++].container.keep = !!keep; > + return 0; > +} > + > int > rte_tel_data_add_dict_string(struct rte_tel_data *d, const char *name, > const char *val) > @@ -128,3 +146,36 @@ rte_tel_data_add_dict_u64(struct rte_tel_data *d, > const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); > return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; > } > + > +int > +rte_tel_data_add_dict_container(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val, int keep) > +{ > + struct tel_dict_entry *e = &d->data.dict[d->data_len]; > + > + if (d->type != RTE_TEL_DICT || (val->type != RTE_TEL_ARRAY_U64 > + && val->type != RTE_TEL_ARRAY_INT > + && val->type != RTE_TEL_ARRAY_STRING)) > + return -EINVAL; > + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) > + return -ENOSPC; > + > + d->data_len++; > + e->type = RTE_TEL_CONTAINER; > + e->value.container.data = val; > + e->value.container.keep = !!keep; > + const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); > + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; > +} > + > +struct rte_tel_data * > +rte_tel_data_alloc(void) > +{ > + return malloc(sizeof(struct rte_tel_data)); > +} > + > +void > +rte_tel_data_free(struct rte_tel_data *data) > +{ > + free(data); > +} > diff --git a/lib/librte_telemetry/telemetry_data.h > b/lib/librte_telemetry/telemetry_data.h > index ff3a371a3..adb84a09f 100644 > --- a/lib/librte_telemetry/telemetry_data.h > +++ b/lib/librte_telemetry/telemetry_data.h > @@ -15,6 +15,12 @@ enum tel_container_types { > RTE_TEL_ARRAY_STRING, /** array of string values only */ > RTE_TEL_ARRAY_INT, /** array of signed, 32-bit int values */ > RTE_TEL_ARRAY_U64, /** array of unsigned 64-bit int values */ > + RTE_TEL_ARRAY_CONTAINER, /** array of container structs */ > +}; > + > +struct container { > + struct rte_tel_data *data; > + int keep; > }; > > /* each type here must have an equivalent enum in the value types enum in > @@ -25,6 +31,7 @@ union tel_value { > char sval[RTE_TEL_MAX_STRING_LEN]; > int ival; > uint64_t u64val; > + struct container container; > }; > > struct tel_dict_entry { > diff --git a/lib/librte_telemetry/telemetry_json.h > b/lib/librte_telemetry/telemetry_json.h > index a2ce4899e..ad270b9b3 100644 > --- a/lib/librte_telemetry/telemetry_json.h > +++ b/lib/librte_telemetry/telemetry_json.h > @@ -102,6 +102,22 @@ rte_tel_json_add_array_u64(char *buf, const int len, > const int used, > return ret == 0 ? used : end + ret; > } > > +/* > + * Add a new element with raw JSON value to the JSON array stored in the > + * provided buffer. > + */ > +static inline int > +rte_tel_json_add_array_json(char *buf, const int len, const int used, > + const char *str) > +{ > + int ret, end = used - 1; /* strip off final delimiter */ > + if (used <= 2) /* assume empty, since minimum is '[]' */ > + return __json_snprintf(buf, len, "[%s]", str); > + > + ret = __json_snprintf(buf + end, len - end, ",%s]", str); > + return ret == 0 ? used : end + ret; > +} > + > /** > * Add a new element with uint64_t value to the JSON object stored in the > * provided buffer. > @@ -155,4 +171,21 @@ rte_tel_json_add_obj_str(char *buf, const int len, const > int used, > return ret == 0 ? used : end + ret; > } > > +/** > + * Add a new element with raw JSON value to the JSON object stored in the > + * provided buffer. > + */ > +static inline int > +rte_tel_json_add_obj_json(char *buf, const int len, const int used, > + const char *name, const char *val) > +{ > + int ret, end = used - 1; > + if (used <= 2) /* assume empty, since minimum is '{}' */ > + return __json_snprintf(buf, len, "{\"%s\":%s}", name, val); > + > + ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}", > + name, val); > + return ret == 0 ? used : end + ret; > +} > + > #endif /*_RTE_TELEMETRY_JSON_H_*/ > -- > 2.17.1 >