* [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion @ 2020-06-12 10:53 Ciara Power 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power ` (9 more replies) 0 siblings, 10 replies; 44+ messages in thread From: Ciara Power @ 2020-06-12 10:53 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: keith.wiles, dev, Ciara Power This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. Currently, a dictionary can support a uint64_t array value, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support some recursive data objects ethdev: add basic stats for telemetry lib/librte_ethdev/rte_ethdev.c | 54 +++++++++++++++++++ lib/librte_telemetry/rte_telemetry.h | 27 ++++++++++ .../rte_telemetry_version.map | 2 + lib/librte_telemetry/telemetry.c | 34 ++++++++++++ lib/librte_telemetry/telemetry_data.c | 18 +++++++ lib/librte_telemetry/telemetry_data.h | 3 ++ lib/librte_telemetry/telemetry_json.h | 17 ++++++ 7 files changed, 155 insertions(+) -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-06-12 10:53 ` Ciara Power 2020-06-12 12:58 ` Bruce Richardson 2020-06-12 13:07 ` Bruce Richardson 2020-06-12 10:53 ` [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry Ciara Power ` (8 subsequent siblings) 9 siblings, 2 replies; 44+ messages in thread From: Ciara Power @ 2020-06-12 10:53 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: keith.wiles, dev, Ciara Power Dict data objects now support uint64_t array data object values. Only one level of recursion supported. Signed-off-by: Ciara Power <ciara.power@intel.com> --- lib/librte_telemetry/rte_telemetry.h | 27 +++++++++++++++ .../rte_telemetry_version.map | 2 ++ lib/librte_telemetry/telemetry.c | 34 +++++++++++++++++++ lib/librte_telemetry/telemetry_data.c | 18 ++++++++++ lib/librte_telemetry/telemetry_data.h | 3 ++ lib/librte_telemetry/telemetry_json.h | 17 ++++++++++ 6 files changed, 101 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index 2c3c96cf7..dc18c34d0 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_DATA_VAL, /** a rte_tel_data pointer value */ }; /** @@ -188,6 +189,22 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a data object pointer to a dictionary. + * The dict must have been started by rte_tel_data_start_dict(). + * + * @param d + * The data structure passed to the callback + * @param x + * The data pointer to be returned in the dictionary + * @return + * 0 on success, negative errno on error + */ +__rte_experimental +int +rte_tel_data_add_dict_data(struct rte_tel_data *d, const char *name, + struct rte_tel_data *val); + /** * This telemetry callback is used when registering a telemetry command. * It handles getting and formatting information to be returned to telemetry @@ -253,4 +270,14 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get the size of the rte_tel_data struct. + * + * @return + * size_t of the struct + */ +__rte_experimental +size_t +rte_tel_get_data_size(void); + #endif diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map index 86433c21d..c5425eff6 100644 --- a/lib/librte_telemetry/rte_telemetry_version.map +++ b/lib/librte_telemetry/rte_telemetry_version.map @@ -4,6 +4,7 @@ EXPERIMENTAL { rte_tel_data_add_array_int; rte_tel_data_add_array_string; rte_tel_data_add_array_u64; + rte_tel_data_add_dict_data; rte_tel_data_add_dict_int; rte_tel_data_add_dict_string; rte_tel_data_add_dict_u64; @@ -13,6 +14,7 @@ EXPERIMENTAL { rte_telemetry_init; rte_telemetry_legacy_register; rte_telemetry_register_cmd; + rte_tel_get_data_size; local: *; }; diff --git a/lib/librte_telemetry/telemetry.c b/lib/librte_telemetry/telemetry.c index 7b6f8a79e..b3a5f4296 100644 --- a/lib/librte_telemetry/telemetry.c +++ b/lib/librte_telemetry/telemetry.c @@ -47,6 +47,12 @@ static int num_callbacks; /* How many commands are registered */ /* Used when accessing or modifying list of command callbacks */ static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; +size_t +rte_tel_get_data_size(void) +{ + return DATA_STRUCT_SIZE; +} + int rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help) { @@ -120,6 +126,23 @@ 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) +{ + size_t used = 0; + unsigned int i; + + if (d->type != RTE_TEL_ARRAY_U64) + return snprintf(out_buf, buf_len, "null"); + + used = rte_tel_json_empty_array(out_buf, buf_len, 0); + 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); + return used; +} + static void output_json(const char *cmd, const struct rte_tel_data *d, int s) { @@ -166,6 +189,17 @@ 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_DATA_VAL: + { + char temp[buf_len]; + if (recursive_data_json(v->value.dataval, + temp, buf_len) != 0) + used = rte_tel_json_add_obj_json( + cb_data_buf, + buf_len, used, + v->name, temp); + free(v->value.dataval); + } } } used += prefix_used; diff --git a/lib/librte_telemetry/telemetry_data.c b/lib/librte_telemetry/telemetry_data.c index f424bbd48..46ce7f9b0 100644 --- a/lib/librte_telemetry/telemetry_data.c +++ b/lib/librte_telemetry/telemetry_data.c @@ -128,3 +128,21 @@ 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_data(struct rte_tel_data *d, const char *name, + struct rte_tel_data *val) +{ + struct tel_dict_entry *e = &d->data.dict[d->data_len]; + + if (d->type != RTE_TEL_DICT) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) + return -ENOSPC; + + d->data_len++; + e->type = RTE_TEL_DATA_VAL; + e->value.dataval = val; + const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; +} diff --git a/lib/librte_telemetry/telemetry_data.h b/lib/librte_telemetry/telemetry_data.h index ff3a371a3..226d961e0 100644 --- a/lib/librte_telemetry/telemetry_data.h +++ b/lib/librte_telemetry/telemetry_data.h @@ -8,6 +8,8 @@ #include <inttypes.h> #include "rte_telemetry.h" +#define DATA_STRUCT_SIZE sizeof(struct rte_tel_data) + enum tel_container_types { RTE_TEL_NULL, /** null, used as error value */ RTE_TEL_STRING, /** basic string type, no included data */ @@ -25,6 +27,7 @@ union tel_value { char sval[RTE_TEL_MAX_STRING_LEN]; int ival; uint64_t u64val; + struct rte_tel_data *dataval; }; struct tel_dict_entry { diff --git a/lib/librte_telemetry/telemetry_json.h b/lib/librte_telemetry/telemetry_json.h index a2ce4899e..415cfe7c6 100644 --- a/lib/librte_telemetry/telemetry_json.h +++ b/lib/librte_telemetry/telemetry_json.h @@ -155,4 +155,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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power @ 2020-06-12 12:58 ` Bruce Richardson 2020-06-12 13:07 ` Bruce Richardson 1 sibling, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-12 12:58 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, keith.wiles, dev On Fri, Jun 12, 2020 at 11:53:43AM +0100, Ciara Power wrote: > Dict data objects now support uint64_t array data object values. > Only one level of recursion supported. > > Signed-off-by: Ciara Power <ciara.power@intel.com> > --- > lib/librte_telemetry/rte_telemetry.h | 27 +++++++++++++++ > .../rte_telemetry_version.map | 2 ++ > lib/librte_telemetry/telemetry.c | 34 +++++++++++++++++++ > lib/librte_telemetry/telemetry_data.c | 18 ++++++++++ > lib/librte_telemetry/telemetry_data.h | 3 ++ > lib/librte_telemetry/telemetry_json.h | 17 ++++++++++ > 6 files changed, 101 insertions(+) > > diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h > index 2c3c96cf7..dc18c34d0 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_DATA_VAL, /** a rte_tel_data pointer value */ > }; > > /** > @@ -188,6 +189,22 @@ int > rte_tel_data_add_dict_u64(struct rte_tel_data *d, > const char *name, uint64_t val); > > +/** > + * Add a data object pointer to a dictionary. > + * The dict must have been started by rte_tel_data_start_dict(). > + * > + * @param d > + * The data structure passed to the callback > + * @param x > + * The data pointer to be returned in the dictionary > + * @return > + * 0 on success, negative errno on error > + */ > +__rte_experimental > +int > +rte_tel_data_add_dict_data(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val); > + > /** > * This telemetry callback is used when registering a telemetry command. > * It handles getting and formatting information to be returned to telemetry > @@ -253,4 +270,14 @@ int > rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, > const char **err_str); > > +/** > + * Get the size of the rte_tel_data struct. > + * > + * @return > + * size_t of the struct > + */ > +__rte_experimental > +size_t > +rte_tel_get_data_size(void); > + Thanks for this work. Thinking about this, the biggest issue I believe is always going to be the memory management aspect of it. It seems here that you are providing an API to allow the memory allocation of the additional object to be done by the callback itself, but that leaves open a problem of freeing the memory again - how does the telemetry library know what allocation function was used, so it knows to free it appropriately? This could be done by function pointer passed in by when adding the data element, but I think that is clunky. Perhaps a better approach here might be to instead add APIs for data alloc and free, so that there is only one way to manage the memory. That would remove the need for this size function. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power 2020-06-12 12:58 ` Bruce Richardson @ 2020-06-12 13:07 ` Bruce Richardson 2020-06-12 13:14 ` Bruce Richardson 1 sibling, 1 reply; 44+ messages in thread From: Bruce Richardson @ 2020-06-12 13:07 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, keith.wiles, dev On Fri, Jun 12, 2020 at 11:53:43AM +0100, Ciara Power wrote: > Dict data objects now support uint64_t array data object values. > Only one level of recursion supported. > > Signed-off-by: Ciara Power <ciara.power@intel.com> > --- > lib/librte_telemetry/rte_telemetry.h | 27 +++++++++++++++ > .../rte_telemetry_version.map | 2 ++ > lib/librte_telemetry/telemetry.c | 34 +++++++++++++++++++ > lib/librte_telemetry/telemetry_data.c | 18 ++++++++++ > lib/librte_telemetry/telemetry_data.h | 3 ++ > lib/librte_telemetry/telemetry_json.h | 17 ++++++++++ > 6 files changed, 101 insertions(+) > > diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h > index 2c3c96cf7..dc18c34d0 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_DATA_VAL, /** a rte_tel_data pointer value */ Are there plans to allow arrays of this type since it's added to the enum. Might be worth adding in v2. > }; > > /** > @@ -188,6 +189,22 @@ int > rte_tel_data_add_dict_u64(struct rte_tel_data *d, > const char *name, uint64_t val); > > +/** > + * Add a data object pointer to a dictionary. > + * The dict must have been started by rte_tel_data_start_dict(). > + * > + * @param d > + * The data structure passed to the callback > + * @param x > + * The data pointer to be returned in the dictionary > + * @return > + * 0 on success, negative errno on error > + */ > +__rte_experimental > +int > +rte_tel_data_add_dict_data(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val); > + > /** > * This telemetry callback is used when registering a telemetry command. > * It handles getting and formatting information to be returned to telemetry > @@ -253,4 +270,14 @@ int > rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, > const char **err_str); > > +/** > + * Get the size of the rte_tel_data struct. > + * > + * @return > + * size_t of the struct > + */ > +__rte_experimental > +size_t > +rte_tel_get_data_size(void); > + > #endif > diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map > index 86433c21d..c5425eff6 100644 > --- a/lib/librte_telemetry/rte_telemetry_version.map > +++ b/lib/librte_telemetry/rte_telemetry_version.map > @@ -4,6 +4,7 @@ EXPERIMENTAL { > rte_tel_data_add_array_int; > rte_tel_data_add_array_string; > rte_tel_data_add_array_u64; > + rte_tel_data_add_dict_data; > rte_tel_data_add_dict_int; > rte_tel_data_add_dict_string; > rte_tel_data_add_dict_u64; > @@ -13,6 +14,7 @@ EXPERIMENTAL { > rte_telemetry_init; > rte_telemetry_legacy_register; > rte_telemetry_register_cmd; > + rte_tel_get_data_size; > > local: *; > }; > diff --git a/lib/librte_telemetry/telemetry.c b/lib/librte_telemetry/telemetry.c > index 7b6f8a79e..b3a5f4296 100644 > --- a/lib/librte_telemetry/telemetry.c > +++ b/lib/librte_telemetry/telemetry.c > @@ -47,6 +47,12 @@ static int num_callbacks; /* How many commands are registered */ > /* Used when accessing or modifying list of command callbacks */ > static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; > > +size_t > +rte_tel_get_data_size(void) > +{ > + return DATA_STRUCT_SIZE; > +} > + > int > rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help) > { > @@ -120,6 +126,23 @@ 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) > +{ > + size_t used = 0; > + unsigned int i; > + > + if (d->type != RTE_TEL_ARRAY_U64) > + return snprintf(out_buf, buf_len, "null"); > + > + used = rte_tel_json_empty_array(out_buf, buf_len, 0); > + 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); > + return used; > +} > + > static void > output_json(const char *cmd, const struct rte_tel_data *d, int s) > { > @@ -166,6 +189,17 @@ 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_DATA_VAL: > + { > + char temp[buf_len]; > + if (recursive_data_json(v->value.dataval, > + temp, buf_len) != 0) > + used = rte_tel_json_add_obj_json( > + cb_data_buf, > + buf_len, used, > + v->name, temp); > + free(v->value.dataval); > + } > } > } > used += prefix_used; > diff --git a/lib/librte_telemetry/telemetry_data.c b/lib/librte_telemetry/telemetry_data.c > index f424bbd48..46ce7f9b0 100644 > --- a/lib/librte_telemetry/telemetry_data.c > +++ b/lib/librte_telemetry/telemetry_data.c > @@ -128,3 +128,21 @@ 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_data(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val) > +{ > + struct tel_dict_entry *e = &d->data.dict[d->data_len]; > + > + if (d->type != RTE_TEL_DICT) > + return -EINVAL; > + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) > + return -ENOSPC; > + > + d->data_len++; > + e->type = RTE_TEL_DATA_VAL; > + e->value.dataval = val; I think we need some restrictions here and return error if not met. 1) I believe we only plan to support one level of recursion, so therefore we need to check that the added element does not have any other data values already hanging off it. 2) Right now this only supports arrays of numbers in the added data, so that needs to be checked. > + const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); > + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; > +} > diff --git a/lib/librte_telemetry/telemetry_data.h b/lib/librte_telemetry/telemetry_data.h > index ff3a371a3..226d961e0 100644 > --- a/lib/librte_telemetry/telemetry_data.h > +++ b/lib/librte_telemetry/telemetry_data.h > @@ -8,6 +8,8 @@ > #include <inttypes.h> > #include "rte_telemetry.h" > > +#define DATA_STRUCT_SIZE sizeof(struct rte_tel_data) > + > enum tel_container_types { > RTE_TEL_NULL, /** null, used as error value */ > RTE_TEL_STRING, /** basic string type, no included data */ > @@ -25,6 +27,7 @@ union tel_value { > char sval[RTE_TEL_MAX_STRING_LEN]; > int ival; > uint64_t u64val; > + struct rte_tel_data *dataval; > }; > > struct tel_dict_entry { > diff --git a/lib/librte_telemetry/telemetry_json.h b/lib/librte_telemetry/telemetry_json.h > index a2ce4899e..415cfe7c6 100644 > --- a/lib/librte_telemetry/telemetry_json.h > +++ b/lib/librte_telemetry/telemetry_json.h > @@ -155,4 +155,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 > ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects 2020-06-12 13:07 ` Bruce Richardson @ 2020-06-12 13:14 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-12 13:14 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, keith.wiles, dev On Fri, Jun 12, 2020 at 02:07:06PM +0100, Bruce Richardson wrote: > On Fri, Jun 12, 2020 at 11:53:43AM +0100, Ciara Power wrote: > > Dict data objects now support uint64_t array data object values. > > Only one level of recursion supported. > > > > Signed-off-by: Ciara Power <ciara.power@intel.com> > > --- <snip> > > static void > > output_json(const char *cmd, const struct rte_tel_data *d, int s) > > { > > @@ -166,6 +189,17 @@ 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_DATA_VAL: > > + { > > + char temp[buf_len]; > > + if (recursive_data_json(v->value.dataval, > > + temp, buf_len) != 0) > > + used = rte_tel_json_add_obj_json( > > + cb_data_buf, > > + buf_len, used, > > + v->name, temp); > > + free(v->value.dataval); Are there cases where we want to preserve the structure across calls rather than doing an alloc and free each time? ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power @ 2020-06-12 10:53 ` Ciara Power 2020-06-12 13:10 ` Bruce Richardson 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion Ciara Power ` (7 subsequent siblings) 9 siblings, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-06-12 10:53 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: keith.wiles, dev, Ciara Power The ethdev library now registers a telemetry command for basic ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.05.0-rc3", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> --- lib/librte_ethdev/rte_ethdev.c | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 8e10a6fc3..1a33e41d3 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5215,6 +5215,58 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = malloc(rte_tel_get_data_size()); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_data(d, stat_name, q_data); +} + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + +#define ADD_DICT_STAT(s) rte_tel_data_add_dict_u64(d, #s, stats.s) + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(ipackets); + ADD_DICT_STAT(opackets); + ADD_DICT_STAT(ibytes); + ADD_DICT_STAT(obytes); + ADD_DICT_STAT(imissed); + ADD_DICT_STAT(ierrors); + ADD_DICT_STAT(oerrors); + ADD_DICT_STAT(rx_nombuf); + + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5302,6 +5354,8 @@ RTE_INIT(ethdev_init_log) rte_log_set_level(rte_eth_dev_logtype, RTE_LOG_INFO); rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the basic stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry 2020-06-12 10:53 ` [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry Ciara Power @ 2020-06-12 13:10 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-12 13:10 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, keith.wiles, dev On Fri, Jun 12, 2020 at 11:53:44AM +0100, Ciara Power wrote: > The ethdev library now registers a telemetry command for basic ethdev > statistics. > > An example usage is shown below: > > Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 > {"version": "DPDK 20.05.0-rc3", "pid": 14119, "max_output_len": 16384} > --> /ethdev/stats,0 > {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ > 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ > "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} > > Signed-off-by: Ciara Power <ciara.power@intel.com> > --- > lib/librte_ethdev/rte_ethdev.c | 54 ++++++++++++++++++++++++++++++++++ > 1 file changed, 54 insertions(+) > > diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c > index 8e10a6fc3..1a33e41d3 100644 > --- a/lib/librte_ethdev/rte_ethdev.c > +++ b/lib/librte_ethdev/rte_ethdev.c > @@ -5215,6 +5215,58 @@ handle_port_list(const char *cmd __rte_unused, > return 0; > } > > +static void > +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, > + const char *stat_name) > +{ > + int q; > + struct rte_tel_data *q_data = malloc(rte_tel_get_data_size()); > + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); > + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) > + rte_tel_data_add_array_u64(q_data, q_stats[q]); > + rte_tel_data_add_dict_data(d, stat_name, q_data); > +} It might be worthwhile adding an function to create an array based off existing numbers. Save a function call per element. > + > +static int > +handle_port_stats(const char *cmd __rte_unused, > + const char *params, > + struct rte_tel_data *d) > +{ > + struct rte_eth_stats stats; > + int port_id, ret; > + > +#define ADD_DICT_STAT(s) rte_tel_data_add_dict_u64(d, #s, stats.s) I think the macro would be better defined just above the function, rather than inside it. Perhaps just after the opening brace might also work, but I think it looks wrong being in the middle of the code itself. > + > + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) > + return -1; > + > + port_id = atoi(params); > + if (!rte_eth_dev_is_valid_port(port_id)) > + return -1; > + > + ret = rte_eth_stats_get(port_id, &stats); > + if (ret < 0) > + return -1; > + > + rte_tel_data_start_dict(d); > + ADD_DICT_STAT(ipackets); > + ADD_DICT_STAT(opackets); > + ADD_DICT_STAT(ibytes); > + ADD_DICT_STAT(obytes); > + ADD_DICT_STAT(imissed); > + ADD_DICT_STAT(ierrors); > + ADD_DICT_STAT(oerrors); > + ADD_DICT_STAT(rx_nombuf); > + > + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); > + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); > + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); > + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); > + add_port_queue_stats(d, stats.q_errors, "q_errors"); > + > + return 0; > +} > + > static int > handle_port_xstats(const char *cmd __rte_unused, > const char *params, > @@ -5302,6 +5354,8 @@ RTE_INIT(ethdev_init_log) > rte_log_set_level(rte_eth_dev_logtype, RTE_LOG_INFO); > rte_telemetry_register_cmd("/ethdev/list", handle_port_list, > "Returns list of available ethdev ports. Takes no parameters"); > + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, > + "Returns the basic stats for a port. Parameters: int port_id"); > rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, > "Returns the extended stats for a port. Parameters: int port_id"); > rte_telemetry_register_cmd("/ethdev/link_status", > -- > 2.17.1 > ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power 2020-06-12 10:53 ` [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry Ciara Power @ 2020-06-24 13:48 ` Ciara Power 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects Ciara Power 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry Ciara Power 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion Ciara Power ` (6 subsequent siblings) 9 siblings, 2 replies; 44+ messages in thread From: Ciara Power @ 2020-06-24 13:48 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array types, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add basic stats for telemetry lib/librte_ethdev/rte_ethdev.c | 53 +++++++++++++++ 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 +++++++++ 7 files changed, 271 insertions(+) -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-06-24 13:48 ` Ciara Power 2020-06-24 15:09 ` Bruce Richardson 2020-06-24 15:18 ` Bruce Richardson 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry Ciara Power 1 sibling, 2 replies; 44+ messages in thread From: Ciara Power @ 2020-06-24 13:48 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power Arrays and Dicts now support uint64_t, int and string array values. Only one level of recursion supported. Signed-off-by: Ciara Power <ciara.power@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. + * 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. + * @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 + */ +__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 + */ +__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. + * + * @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) +{ + 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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects Ciara Power @ 2020-06-24 15:09 ` Bruce Richardson 2020-06-24 15:18 ` Bruce Richardson 1 sibling, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-24 15:09 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles 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.power@intel.com> > > --- Approach seems good to me, but I think you need more detail in the commit log for this. I think the first sentence needs to make it clearer that its arrays of uint64_t and arrays of int which can be supported as well as arrays of strings. I also think some discussion of why this is necessary is needed in the commit log as well as an explanation of how the memory alloc and free for the array objects work. /Bruce ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects Ciara Power 2020-06-24 15:09 ` Bruce Richardson @ 2020-06-24 15:18 ` Bruce Richardson 1 sibling, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-24 15:18 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles 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.power@intel.com> > A number of comments inline below. Once fixed you can include my ack on V3. Acked-by: Bruce Richardson <bruce.richardson@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 > ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects Ciara Power @ 2020-06-24 13:48 ` Ciara Power 2020-06-24 15:19 ` Bruce Richardson 1 sibling, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-06-24 13:48 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power The ethdev library now registers a telemetry command for basic ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc0", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> --- v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 8e10a6fc3..23de93b50 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5215,6 +5215,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5302,6 +5353,8 @@ RTE_INIT(ethdev_init_log) rte_log_set_level(rte_eth_dev_logtype, RTE_LOG_INFO); rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the basic stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry Ciara Power @ 2020-06-24 15:19 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-06-24 15:19 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles On Wed, Jun 24, 2020 at 02:48:24PM +0100, Ciara Power wrote: > The ethdev library now registers a telemetry command for basic ethdev > statistics. > > An example usage is shown below: > > Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 > {"version": "DPDK 20.08.0-rc0", "pid": 14119, "max_output_len": 16384} > --> /ethdev/stats,0 > {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ > 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ > "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ > "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} > > Signed-off-by: Ciara Power <ciara.power@intel.com> > One comment inline below, otherwise: Acked-by: Bruce Richardson <bruce.richardson@intel.com> > --- > v2: > - Updated to use memory management APIs. > --- > lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ > 1 file changed, 53 insertions(+) > > diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c > index 8e10a6fc3..23de93b50 100644 > --- a/lib/librte_ethdev/rte_ethdev.c > +++ b/lib/librte_ethdev/rte_ethdev.c > @@ -5215,6 +5215,57 @@ handle_port_list(const char *cmd __rte_unused, > return 0; > } > > +static void > +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, > + const char *stat_name) > +{ > + int q; > + struct rte_tel_data *q_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); > + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) > + rte_tel_data_add_array_u64(q_data, q_stats[q]); > + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); > +} > + > +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) > + > +static int > +handle_port_stats(const char *cmd __rte_unused, > + const char *params, > + struct rte_tel_data *d) > +{ > + struct rte_eth_stats stats; > + int port_id, ret; > + > + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) > + return -1; > + > + port_id = atoi(params); > + if (!rte_eth_dev_is_valid_port(port_id)) > + return -1; > + > + ret = rte_eth_stats_get(port_id, &stats); > + if (ret < 0) > + return -1; > + > + rte_tel_data_start_dict(d); > + ADD_DICT_STAT(stats, ipackets); > + ADD_DICT_STAT(stats, opackets); > + ADD_DICT_STAT(stats, ibytes); > + ADD_DICT_STAT(stats, obytes); > + ADD_DICT_STAT(stats, imissed); > + ADD_DICT_STAT(stats, ierrors); > + ADD_DICT_STAT(stats, oerrors); > + ADD_DICT_STAT(stats, rx_nombuf); > + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); > + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); > + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); > + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); > + add_port_queue_stats(d, stats.q_errors, "q_errors"); > + > + return 0; > +} > + > static int > handle_port_xstats(const char *cmd __rte_unused, > const char *params, > @@ -5302,6 +5353,8 @@ RTE_INIT(ethdev_init_log) > rte_log_set_level(rte_eth_dev_logtype, RTE_LOG_INFO); > rte_telemetry_register_cmd("/ethdev/list", handle_port_list, > "Returns list of available ethdev ports. Takes no parameters"); > + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, > + "Returns the basic stats for a port. Parameters: int port_id"); I think "common stats" or "standard stats" may be better than calling them "basic stats" since the main advantage of them is that they are common/standard across all drivers and are not NIC-specific. > rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, > "Returns the extended stats for a port. Parameters: int port_id"); > rte_telemetry_register_cmd("/ethdev/link_status", > -- > 2.17.1 > ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (2 preceding siblings ...) 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-02 10:19 ` Ciara Power 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects Ciara Power 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 2/2] ethdev: add common stats for telemetry Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion Ciara Power ` (5 subsequent siblings) 9 siblings, 2 replies; 44+ messages in thread From: Ciara Power @ 2020-07-02 10:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry lib/librte_ethdev/rte_ethdev.c | 53 +++++++++++++++ lib/librte_telemetry/rte_telemetry.h | 68 +++++++++++++++++++ .../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 +++++++++ 7 files changed, 272 insertions(+) -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-02 10:19 ` Ciara Power 2020-07-07 7:15 ` Thomas Monjalon 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 2/2] ethdev: add common stats for telemetry Ciara Power 1 sibling, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-07-02 10:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Modified commit log. - Changed function name to reference container. - Modified Doxygen comments to reference container. 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 | 68 +++++++++++++++++++ .../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, 219 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index eb7f2c917..99e7a7c00 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. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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,28 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 +307,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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..691b52144 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 +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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 (container_to_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 (container_to_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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects Ciara Power @ 2020-07-07 7:15 ` Thomas Monjalon 0 siblings, 0 replies; 44+ messages in thread From: Thomas Monjalon @ 2020-07-07 7:15 UTC (permalink / raw) To: Ciara Power; +Cc: kevin.laatz, ferruh.yigit, arybchenko, dev, keith.wiles 02/07/2020 12:19, Ciara Power: > +/** > + * Add a container to a dictionary. A container is an existing telemetry data > + * array. The dict the container is to be added to must have been started by > + * rte_tel_data_start_dict(). The container must be an array of type > + * uint64_t/int/string. > + * > + * @param d > + * The data structure passed to the callback > + * @param val > + * The pointer to the container to be stored in the dict. > + * @param keep > + * 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_dict_container(struct rte_tel_data *d, const char *name, > + struct rte_tel_data *val, int keep); Please fix this miss: rte_telemetry.h:233: warning: The following parameter of rte_tel_data_add_dict_container(struct rte_tel_data *d, const char *name, struct rte_tel_data *val, int keep) is not documented: + parameter 'name' ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v3 2/2] ethdev: add common stats for telemetry 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects Ciara Power @ 2020-07-02 10:19 ` Ciara Power 1 sibling, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-02 10:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc0", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Updated help text and commit subject line. v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 8e10a6fc3..63086864f 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5215,6 +5215,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5302,6 +5353,8 @@ RTE_INIT(ethdev_init_log) rte_log_set_level(rte_eth_dev_logtype, RTE_LOG_INFO); rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (3 preceding siblings ...) 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-13 14:23 ` Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 1/2] telemetry: support array values in data objects Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 2/2] ethdev: add common stats for telemetry Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power ` (4 subsequent siblings) 9 siblings, 2 replies; 44+ messages in thread From: Ciara Power @ 2020-07-13 14:23 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++ lib/librte_telemetry/rte_telemetry.h | 70 +++++++++++++++++++ .../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 +++++++++ 7 files changed, 274 insertions(+) -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 1/2] telemetry: support array values in data objects 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-13 14:23 ` Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 2/2] ethdev: add common stats for telemetry Ciara Power 1 sibling, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-13 14:23 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v4: Added missing param description to Doxygen comment. v3: - Modified commit log. - Changed function name to reference container. - Modified Doxygen comments to reference container. 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 | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index d13010b8f..c16541226 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -46,6 +46,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 */ }; /** @@ -136,6 +137,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -190,6 +213,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -264,4 +311,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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..691b52144 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 +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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 (container_to_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 (container_to_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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v4 2/2] ethdev: add common stats for telemetry 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 1/2] telemetry: support array values in data objects Ciara Power @ 2020-07-13 14:23 ` Ciara Power 1 sibling, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-13 14:23 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Updated help text and commit subject line. v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 7858ad5f1..8ee14b1b5 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5275,6 +5275,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5361,6 +5412,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (4 preceding siblings ...) 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-15 12:29 ` Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 1/3] telemetry: support array values in data objects Ciara Power ` (2 more replies) 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power ` (3 subsequent siblings) 9 siblings, 3 replies; 44+ messages in thread From: Ciara Power @ 2020-07-15 12:29 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power v5: Added unit tests for telemetry data to JSON conversion. v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry Louise Kilheeney (1): test/test_telemetry_data: add unit tests for data to JSON app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 359 ++++++++++++++++++ lib/librte_ethdev/rte_ethdev.c | 53 +++ lib/librte_telemetry/rte_telemetry.h | 70 ++++ .../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 ++ 10 files changed, 637 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 1/3] telemetry: support array values in data objects 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-15 12:29 ` Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-15 12:29 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v4: Added missing param description to Doxygen comment. v3: - Modified commit log. - Changed function name to reference container. - Modified Doxygen comments to reference container. 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 | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index d13010b8f..c16541226 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -46,6 +46,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 */ }; /** @@ -136,6 +137,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -190,6 +213,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -264,4 +311,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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..691b52144 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 +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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 (container_to_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 (container_to_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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 1/3] telemetry: support array values in data objects Ciara Power @ 2020-07-15 12:29 ` Ciara Power 2020-07-17 11:53 ` Bruce Richardson 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-07-15 12:29 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Louise Kilheeney, Ciara Power From: Louise Kilheeney <louise.kilheeney@intel.com> This patch adds tests for verifying telemetry data structures are converted to JSON as expected. Both flat and recursive data structures are tested, for all possible value types. Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> Signed-off-by: Ciara Power <ciara.power@intel.com> --- app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 359 +++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c diff --git a/app/test/Makefile b/app/test/Makefile index f4065271e..1cb64089c 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c SRCS-y += test_debug.c SRCS-y += test_errno.c diff --git a/app/test/meson.build b/app/test/meson.build index 786a21397..4a72fe5b6 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -170,6 +170,7 @@ test_deps = ['acl', 'ring', 'security', 'stack', + 'telemetry', 'timer' ] @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') test_deps += 'pmd_skeleton_event' endif if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') - test_sources += 'test_telemetry_json.c' - fast_tests += [['telemetry_json_autotest', true]] + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] endif # The following linkages of drivers are required because diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c new file mode 100644 index 000000000..670af9134 --- /dev/null +++ b/app/test/test_telemetry_data.c @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 Intel Corporation + */ + +#include <glob.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <limits.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_telemetry.h> +#include <rte_string_fns.h> + +#include "test.h" +#include "telemetry_data.h" + +#define TELEMETRY_VERSION "v2" +#define REQUEST_CMD "/test" +#define BUF_SIZE 1024 +#define TEST_OUTPUT(exp) test_output(__func__, exp) + +static struct rte_tel_data response_data; +static int sock; + +static int +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + *d = response_data; + return 0; +} + +static int +test_output(const char *func_name, const char *expected) +{ + int bytes; + char buf[BUF_SIZE * 16]; + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { + printf("%s: Error with socket write - %s\n", __func__, + strerror(errno)); + return -1; + } + bytes = read(sock, buf, sizeof(buf)); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); + return strncmp(expected, buf, sizeof(buf)); +} + +static int +test_dict_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4],\"dict_1\":[0,1,2,3,4]}}"); +} + +static int +test_array_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +test_case_array_int(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_int(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_int(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_int(&response_data, name_of_value, i); + } + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_case_array_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); + rte_tel_data_add_array_string(&response_data, "aaaa"); + rte_tel_data_add_array_string(&response_data, "bbbb"); + rte_tel_data_add_array_string(&response_data, "cccc"); + rte_tel_data_add_array_string(&response_data, "dddd"); + rte_tel_data_add_array_string(&response_data, "eeee"); + + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\",\"eeee\"]}"); +} + +static int +test_case_add_dict_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); +} + + +static int +test_dict_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":[\"bbbb\"]}}"); +} + +static int +test_array_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); +} + +static int +test_case_array_u64(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_u64(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_u64(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); + } + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_dict_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 10; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9],\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); +} + +static int +test_array_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +connect_to_socket(void) +{ + char file_pattern[PATH_MAX]; + char buf[BUF_SIZE]; + glob_t globbuf; + int sock, bytes; + struct sockaddr_un telem_addr; + + snprintf(file_pattern, sizeof(file_pattern), + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), + TELEMETRY_VERSION); + if (glob(file_pattern, 0, NULL, &globbuf) != 0) { + printf("\n%s: Error finding socket file path: %s\n", __func__, + strerror(errno)); + globfree(&globbuf); + return -1; + } + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + printf("\n%s: Error creating socket: %s\n", __func__, + strerror(errno)); + return -1; + } + telem_addr.sun_family = AF_UNIX; + strlcpy(telem_addr.sun_path, globbuf.gl_pathv[0], + sizeof(telem_addr.sun_path)); + if (connect(sock, &telem_addr, sizeof(telem_addr)) < 0) + printf("\n%s: Error connecting to socket: %s\n", __func__, + strerror(errno)); + + bytes = read(sock, buf, sizeof(buf)); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("\n%s: %s\n", __func__, buf); + globfree(&globbuf); + return sock; +} + +static int +test_telemetry_data(void) +{ + sock = connect_to_socket(); + if (sock <= 0) + return -1; + + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); + if (test_case_array_string() != 0 + || test_case_array_int() != 0 + || test_case_array_u64() != 0 + || test_case_add_dict_int() != 0 + || test_case_add_dict_u64() != 0 + || test_case_add_dict_string() != 0 + || test_dict_with_array_int_values() != 0 + || test_dict_with_array_u64_values() != 0 + || test_dict_with_array_string_values() != 0 + || test_array_with_array_int_values() != 0 + || test_array_with_array_u64_values() != 0 + || test_array_with_array_string_values() != 0) { + close(sock); + return -1; + } + + close(sock); + return 0; +} + +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-17 11:53 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-07-17 11:53 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles, Louise Kilheeney On Wed, Jul 15, 2020 at 01:29:34PM +0100, Ciara Power wrote: > From: Louise Kilheeney <louise.kilheeney@intel.com> > > This patch adds tests for verifying telemetry data structures are > converted to JSON as expected. Both flat and recursive data structures > are tested, for all possible value types. > > Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> > Signed-off-by: Ciara Power <ciara.power@intel.com> Great to see some unit tests included. However, I think the documenting of what is happening is missing in comments and in the commit log. I think you need to explain in the commit log the general pattern used for the tests, which seems to be having a different response copied to the user each time the callback is called, and verifing that the output got is what was built-up internally. This should also be explained via comments in the code too, as I call out below. > --- > app/test/Makefile | 1 + > app/test/meson.build | 5 +- > app/test/test_telemetry_data.c | 359 +++++++++++++++++++++++++++++++++ > 3 files changed, 363 insertions(+), 2 deletions(-) > create mode 100644 app/test/test_telemetry_data.c > > diff --git a/app/test/Makefile b/app/test/Makefile > index f4065271e..1cb64089c 100644 > --- a/app/test/Makefile > +++ b/app/test/Makefile > @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c > SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c > > SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c > +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c > > SRCS-y += test_debug.c > SRCS-y += test_errno.c > diff --git a/app/test/meson.build b/app/test/meson.build > index 786a21397..4a72fe5b6 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -170,6 +170,7 @@ test_deps = ['acl', > 'ring', > 'security', > 'stack', > + 'telemetry', > 'timer' > ] > > @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') > test_deps += 'pmd_skeleton_event' > endif > if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') > - test_sources += 'test_telemetry_json.c' > - fast_tests += [['telemetry_json_autotest', true]] > + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] > + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] > endif > > # The following linkages of drivers are required because > diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c > new file mode 100644 > index 000000000..670af9134 > --- /dev/null > +++ b/app/test/test_telemetry_data.c > @@ -0,0 +1,359 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2020 Intel Corporation > + */ > + > +#include <glob.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <sys/un.h> > +#include <unistd.h> > +#include <limits.h> > + > +#include <rte_eal.h> > +#include <rte_common.h> > +#include <rte_telemetry.h> > +#include <rte_string_fns.h> > + > +#include "test.h" > +#include "telemetry_data.h" > + > +#define TELEMETRY_VERSION "v2" > +#define REQUEST_CMD "/test" > +#define BUF_SIZE 1024 > +#define TEST_OUTPUT(exp) test_output(__func__, exp) > + > +static struct rte_tel_data response_data; > +static int sock; > + > +static int > +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, > + struct rte_tel_data *d) > +{ > + *d = response_data; > + return 0; > +} This function needs a comment explaining what it does and why. > + > +static int > +test_output(const char *func_name, const char *expected) > +{ > + int bytes; > + char buf[BUF_SIZE * 16]; > + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { > + printf("%s: Error with socket write - %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + bytes = read(sock, buf, sizeof(buf)); > + if (bytes < 0) { > + printf("%s: Error with socket read - %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + buf[bytes] = '\0'; > + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); > + return strncmp(expected, buf, sizeof(buf)); > +} > + This one too, needs comment explaining how it works etc. <snip> > +static int > +test_telemetry_data(void) > +{ > + sock = connect_to_socket(); > + if (sock <= 0) > + return -1; > + > + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); > + if (test_case_array_string() != 0 > + || test_case_array_int() != 0 > + || test_case_array_u64() != 0 > + || test_case_add_dict_int() != 0 > + || test_case_add_dict_u64() != 0 > + || test_case_add_dict_string() != 0 > + || test_dict_with_array_int_values() != 0 > + || test_dict_with_array_u64_values() != 0 > + || test_dict_with_array_string_values() != 0 > + || test_array_with_array_int_values() != 0 > + || test_array_with_array_u64_values() != 0 > + || test_array_with_array_string_values() != 0) { > + close(sock); > + return -1; > + } I'm not sure I like this way of calling the test cases, and the indentation of the lines is definitely wrong. The simplest option here is to have a single if statement for each test and a goto for the "error" leg: if (test_case_array_string() != 0) goto error; if (test_case_array_int() != 0) goto error; ... However, if you want to avoid the multiple if statements, a better option than merging as above would be to put the functions in an array and loop over them as (rough code, completely untested!): typedef int (*test_case)(void); test_case test_cases[] = {test_case_array_string, test_case_array_int, ... }; ... for (i = 0; i < RTE_DIM(test_cases); i++) if (test_cases[i]() != 0) { ... } > + > + close(sock); > + return 0; > +} > + > +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); > -- Regards, /Bruce ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v5 3/3] ethdev: add common stats for telemetry 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 1/3] telemetry: support array values in data objects Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-15 12:29 ` Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-15 12:29 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, Ciara Power The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Updated help text and commit subject line. v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 7858ad5f1..8ee14b1b5 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5275,6 +5275,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5361,6 +5412,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (5 preceding siblings ...) 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-20 11:19 ` Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 1/3] telemetry: support array values in data objects Ciara Power ` (2 more replies) 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power ` (2 subsequent siblings) 9 siblings, 3 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 11:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power v6: - Fixed FreeBSD build failure for unit tests. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. v5: Added unit tests for telemetry data to JSON conversion. v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry Louise Kilheeney (1): test/test_telemetry_data: add unit tests for data to JSON app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 375 ++++++++++++++++++ lib/librte_ethdev/rte_ethdev.c | 53 +++ lib/librte_telemetry/rte_telemetry.h | 70 ++++ .../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 ++ 10 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v6 1/3] telemetry: support array values in data objects 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-20 11:19 ` Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 11:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v4: Added missing param description to Doxygen comment. v3: - Modified commit log. - Changed function name to reference container. - Modified Doxygen comments to reference container. 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 | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index d13010b8f..c16541226 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -46,6 +46,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 */ }; /** @@ -136,6 +137,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -190,6 +213,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -264,4 +311,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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 025228273..4469885c3 100644 --- a/lib/librte_telemetry/telemetry.c +++ b/lib/librte_telemetry/telemetry.c @@ -123,6 +123,35 @@ command_help(const char *cmd __rte_unused, const char *params, return 0; } +static int +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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) { @@ -169,6 +198,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 (container_to_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; @@ -177,6 +220,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]; @@ -197,6 +241,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 (container_to_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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 1/3] telemetry: support array values in data objects Ciara Power @ 2020-07-20 11:19 ` Ciara Power 2020-07-20 13:08 ` Bruce Richardson 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-07-20 11:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Louise Kilheeney, Ciara Power From: Louise Kilheeney <louise.kilheeney@intel.com> This patch adds tests for verifying telemetry data structures are converted to JSON as expected. Both flat and recursive data structures are tested, for all possible value types. The app connects to the telemetry socket as a client, and registers one command with a corresponding callback function. Each time the callback function is called, it copies a global data variable to the data pointer passed in by telemetry. When a test case is run, the test case function builds up the global data variable with the relevant data types, and the expected json string output which should be generated from that. The 'test_output()' function is used to trigger the callback and ensure the actual output matches that expected. Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> Signed-off-by: Ciara Power <ciara.power@intel.com> --- v6: - Fixed FreeBSD build error. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. --- app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 375 +++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c diff --git a/app/test/Makefile b/app/test/Makefile index f4065271e..1cb64089c 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c SRCS-y += test_debug.c SRCS-y += test_errno.c diff --git a/app/test/meson.build b/app/test/meson.build index 786a21397..4a72fe5b6 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -170,6 +170,7 @@ test_deps = ['acl', 'ring', 'security', 'stack', + 'telemetry', 'timer' ] @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') test_deps += 'pmd_skeleton_event' endif if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') - test_sources += 'test_telemetry_json.c' - fast_tests += [['telemetry_json_autotest', true]] + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] endif # The following linkages of drivers are required because diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c new file mode 100644 index 000000000..f95054433 --- /dev/null +++ b/app/test/test_telemetry_data.c @@ -0,0 +1,375 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 Intel Corporation + */ + +#include <glob.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <limits.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_telemetry.h> +#include <rte_string_fns.h> + +#include "test.h" +#include "telemetry_data.h" + +#define TELEMETRY_VERSION "v2" +#define REQUEST_CMD "/test" +#define BUF_SIZE 1024 +#define TEST_OUTPUT(exp) test_output(__func__, exp) + +static struct rte_tel_data response_data; +static int sock; + +/* + * This function is the callback registered with Telemetry to be used when + * the /test command is requested. This callback returns the global data built + * up by the individual test cases. + */ +static int +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + *d = response_data; + return 0; +} + +/* + * This function is called by each test case function. It communicates with + * the telemetry socket by requesting the /test command, and reading the + * response. The expected response is passed in by the test case function, + * and is compared to the actual response received from Telemetry. + */ +static int +test_output(const char *func_name, const char *expected) +{ + int bytes; + char buf[BUF_SIZE * 16]; + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { + printf("%s: Error with socket write - %s\n", __func__, + strerror(errno)); + return -1; + } + bytes = read(sock, buf, sizeof(buf)); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); + return strncmp(expected, buf, sizeof(buf)); +} + +static int +test_dict_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4],\"dict_1\":[0,1,2,3,4]}}"); +} + +static int +test_array_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +test_case_array_int(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_int(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_int(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_int(&response_data, name_of_value, i); + } + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_case_array_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); + rte_tel_data_add_array_string(&response_data, "aaaa"); + rte_tel_data_add_array_string(&response_data, "bbbb"); + rte_tel_data_add_array_string(&response_data, "cccc"); + rte_tel_data_add_array_string(&response_data, "dddd"); + rte_tel_data_add_array_string(&response_data, "eeee"); + + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\",\"eeee\"]}"); +} + +static int +test_case_add_dict_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); +} + + +static int +test_dict_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":[\"bbbb\"]}}"); +} + +static int +test_array_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); +} + +static int +test_case_array_u64(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_u64(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_u64(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); + } + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_dict_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 10; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9],\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); +} + +static int +test_array_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +connect_to_socket(void) +{ + char file_pattern[PATH_MAX]; + char buf[BUF_SIZE]; + glob_t globbuf; + int sock, bytes; + struct sockaddr_un telem_addr; + + snprintf(file_pattern, sizeof(file_pattern), + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), + TELEMETRY_VERSION); + if (glob(file_pattern, 0, NULL, &globbuf) != 0) { + printf("\n%s: Error finding socket file path: %s\n", __func__, + strerror(errno)); + globfree(&globbuf); + return -1; + } + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + printf("\n%s: Error creating socket: %s\n", __func__, + strerror(errno)); + return -1; + } + telem_addr.sun_family = AF_UNIX; + strlcpy(telem_addr.sun_path, globbuf.gl_pathv[0], + sizeof(telem_addr.sun_path)); + if (connect(sock, (struct sockaddr *) &telem_addr, + sizeof(telem_addr)) < 0) + printf("\n%s: Error connecting to socket: %s\n", __func__, + strerror(errno)); + + bytes = read(sock, buf, sizeof(buf)); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("\n%s: %s\n", __func__, buf); + globfree(&globbuf); + return sock; +} + +static int +test_telemetry_data(void) +{ + typedef int (*test_case)(void); + unsigned int i = 0; + + sock = connect_to_socket(); + if (sock <= 0) + return -1; + + test_case test_cases[] = {test_case_array_string, + test_case_array_int, test_case_array_u64, + test_case_add_dict_int, test_case_add_dict_u64, + test_case_add_dict_string, + test_dict_with_array_int_values, + test_dict_with_array_u64_values, + test_dict_with_array_string_values, + test_array_with_array_int_values, + test_array_with_array_u64_values, + test_array_with_array_string_values }; + + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); + for (i = 0; i < RTE_DIM(test_cases); i++) { + if (test_cases[i]() != 0) { + close(sock); + return -1; + } + } + close(sock); + return 0; +} + +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-20 13:08 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-07-20 13:08 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles, Louise Kilheeney On Mon, Jul 20, 2020 at 12:19:06PM +0100, Ciara Power wrote: > From: Louise Kilheeney <louise.kilheeney@intel.com> > > This patch adds tests for verifying telemetry data structures are > converted to JSON as expected. Both flat and recursive data structures > are tested, for all possible value types. > > The app connects to the telemetry socket as a client, and registers one > command with a corresponding callback function. Each time the callback > function is called, it copies a global data variable to the data pointer > passed in by telemetry. > When a test case is run, the test case function builds up the global > data variable with the relevant data types, and the expected json string > output which should be generated from that. The 'test_output()' function > is used to trigger the callback and ensure the actual output matches > that expected. > > Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> > Signed-off-by: Ciara Power <ciara.power@intel.com> > > --- > v6: > - Fixed FreeBSD build error. > - Added comments and expanded commit log. > - Add loop to call test cases stored in a list. > --- Thanks, this is a good improvement. Apologies but a few more comments below that I missed in the previous version. /Bruce > app/test/Makefile | 1 + > app/test/meson.build | 5 +- > app/test/test_telemetry_data.c | 375 +++++++++++++++++++++++++++++++++ > 3 files changed, 379 insertions(+), 2 deletions(-) > create mode 100644 app/test/test_telemetry_data.c > > diff --git a/app/test/Makefile b/app/test/Makefile > index f4065271e..1cb64089c 100644 > --- a/app/test/Makefile > +++ b/app/test/Makefile > @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c > SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c > > SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c > +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c > > SRCS-y += test_debug.c > SRCS-y += test_errno.c > diff --git a/app/test/meson.build b/app/test/meson.build > index 786a21397..4a72fe5b6 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -170,6 +170,7 @@ test_deps = ['acl', > 'ring', > 'security', > 'stack', > + 'telemetry', > 'timer' > ] > > @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') > test_deps += 'pmd_skeleton_event' > endif > if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') > - test_sources += 'test_telemetry_json.c' > - fast_tests += [['telemetry_json_autotest', true]] > + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] > + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] > endif > > # The following linkages of drivers are required because > diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c > new file mode 100644 > index 000000000..f95054433 > --- /dev/null > +++ b/app/test/test_telemetry_data.c > @@ -0,0 +1,375 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2020 Intel Corporation > + */ > + > +#include <glob.h> While the python script for testing telemetry does use glob to find the socket, in this case I think it's unnecessary since we know we are in the process that created the listening socket, so we can just connect straight to it. > +#include <string.h> > +#include <sys/socket.h> > +#include <sys/un.h> > +#include <unistd.h> > +#include <limits.h> > + > +#include <rte_eal.h> > +#include <rte_common.h> > +#include <rte_telemetry.h> > +#include <rte_string_fns.h> > + > +#include "test.h" > +#include "telemetry_data.h" > + > +#define TELEMETRY_VERSION "v2" > +#define REQUEST_CMD "/test" > +#define BUF_SIZE 1024 > +#define TEST_OUTPUT(exp) test_output(__func__, exp) > + > +static struct rte_tel_data response_data; > +static int sock; > + > +/* > + * This function is the callback registered with Telemetry to be used when > + * the /test command is requested. This callback returns the global data built > + * up by the individual test cases. > + */ > +static int > +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, > + struct rte_tel_data *d) > +{ > + *d = response_data; > + return 0; > +} > + > +/* > + * This function is called by each test case function. It communicates with > + * the telemetry socket by requesting the /test command, and reading the > + * response. The expected response is passed in by the test case function, > + * and is compared to the actual response received from Telemetry. > + */ > +static int > +test_output(const char *func_name, const char *expected) > +{ > + int bytes; > + char buf[BUF_SIZE * 16]; > + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { > + printf("%s: Error with socket write - %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + bytes = read(sock, buf, sizeof(buf)); > + if (bytes < 0) { > + printf("%s: Error with socket read - %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + buf[bytes] = '\0'; Need to check here for overflow. If bytes == BUF_SIZE * 16, you'll overflow. > + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); > + return strncmp(expected, buf, sizeof(buf)); > +} > + > +static int > +test_dict_with_array_int_values(void) > +{ > + int i; > + > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + for (i = 0; i < 5; i++) { > + rte_tel_data_add_array_int(child_data, i); > + rte_tel_data_add_array_int(child_data2, i); > + } > + > + rte_tel_data_add_dict_container(&response_data, "dict_0", > + child_data, 0); > + rte_tel_data_add_dict_container(&response_data, "dict_1", > + child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4],\"dict_1\":[0,1,2,3,4]}}"); > +} > + > +static int > +test_array_with_array_int_values(void) > +{ > + int i; > + > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); > + > + for (i = 0; i < 5; i++) { > + rte_tel_data_add_array_int(child_data, i); > + rte_tel_data_add_array_int(child_data2, i); > + } > + rte_tel_data_add_array_container(&response_data, child_data, 0); > + rte_tel_data_add_array_container(&response_data, child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); > +} > + > +static int > +test_case_array_int(void) > +{ > + int i; > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); > + for (i = 0; i < 5; i++) > + rte_tel_data_add_array_int(&response_data, i); > + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); > +} > + > +static int > +test_case_add_dict_int(void) > +{ > + int i = 0; > + char name_of_value[8]; > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + for (i = 0; i < 5; i++) { > + sprintf(name_of_value, "dict_%d", i); > + rte_tel_data_add_dict_int(&response_data, name_of_value, i); > + } > + > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); > +} > + > +static int > +test_case_array_string(void) > +{ > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); > + rte_tel_data_add_array_string(&response_data, "aaaa"); > + rte_tel_data_add_array_string(&response_data, "bbbb"); > + rte_tel_data_add_array_string(&response_data, "cccc"); > + rte_tel_data_add_array_string(&response_data, "dddd"); > + rte_tel_data_add_array_string(&response_data, "eeee"); > + > + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\",\"eeee\"]}"); > +} > + > +static int > +test_case_add_dict_string(void) > +{ > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); > + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); > + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); > + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); > + > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); > +} Although these are single strings, they are not user output, and so you can split them across lines to limit line length. > + > + > +static int > +test_dict_with_array_string_values(void) > +{ > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + rte_tel_data_add_array_string(child_data, "aaaa"); > + rte_tel_data_add_array_string(child_data2, "bbbb"); > + > + rte_tel_data_add_dict_container(&response_data, "dict_0", > + child_data, 0); > + rte_tel_data_add_dict_container(&response_data, "dict_1", > + child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":[\"bbbb\"]}}"); > +} > + > +static int > +test_array_with_array_string_values(void) > +{ > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); > + > + rte_tel_data_add_array_string(child_data, "aaaa"); > + rte_tel_data_add_array_string(child_data2, "bbbb"); > + > + rte_tel_data_add_array_container(&response_data, child_data, 0); > + rte_tel_data_add_array_container(&response_data, child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); > +} > + > +static int > +test_case_array_u64(void) > +{ > + int i; > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); > + for (i = 0; i < 5; i++) > + rte_tel_data_add_array_u64(&response_data, i); > + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); > +} > + > +static int > +test_case_add_dict_u64(void) > +{ > + int i = 0; > + char name_of_value[8]; > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + for (i = 0; i < 5; i++) { > + sprintf(name_of_value, "dict_%d", i); > + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); > + } > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2,\"dict_3\":3,\"dict_4\":4}}"); > +} > + > +static int > +test_dict_with_array_u64_values(void) > +{ > + int i; > + > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_dict(&response_data); > + > + for (i = 0; i < 10; i++) { > + rte_tel_data_add_array_u64(child_data, i); > + rte_tel_data_add_array_u64(child_data2, i); > + } > + > + rte_tel_data_add_dict_container(&response_data, "dict_0", > + child_data, 0); > + rte_tel_data_add_dict_container(&response_data, "dict_1", > + child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9],\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); > +} > + > +static int > +test_array_with_array_u64_values(void) > +{ > + int i; > + > + struct rte_tel_data *child_data = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); > + > + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); > + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); > + > + memset(&response_data, 0, sizeof(response_data)); > + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); > + > + for (i = 0; i < 5; i++) { > + rte_tel_data_add_array_u64(child_data, i); > + rte_tel_data_add_array_u64(child_data2, i); > + } > + rte_tel_data_add_array_container(&response_data, child_data, 0); > + rte_tel_data_add_array_container(&response_data, child_data2, 0); > + > + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); > +} > + > +static int > +connect_to_socket(void) > +{ > + char file_pattern[PATH_MAX]; > + char buf[BUF_SIZE]; > + glob_t globbuf; > + int sock, bytes; > + struct sockaddr_un telem_addr; > + > + snprintf(file_pattern, sizeof(file_pattern), > + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), > + TELEMETRY_VERSION); > + if (glob(file_pattern, 0, NULL, &globbuf) != 0) { > + printf("\n%s: Error finding socket file path: %s\n", __func__, > + strerror(errno)); > + globfree(&globbuf); > + return -1; > + } > + The path you create here should be the exact path that you need, no globbing should be necessary, which I think should simplify the code a bit. > + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); > + if (sock < 0) { > + printf("\n%s: Error creating socket: %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + telem_addr.sun_family = AF_UNIX; > + strlcpy(telem_addr.sun_path, globbuf.gl_pathv[0], > + sizeof(telem_addr.sun_path)); > + if (connect(sock, (struct sockaddr *) &telem_addr, > + sizeof(telem_addr)) < 0) > + printf("\n%s: Error connecting to socket: %s\n", __func__, > + strerror(errno)); > + > + bytes = read(sock, buf, sizeof(buf)); > + if (bytes < 0) { > + printf("%s: Error with socket read - %s\n", __func__, > + strerror(errno)); > + return -1; > + } > + buf[bytes] = '\0'; Again, watch for overflow when you hit the exact output limit. > + printf("\n%s: %s\n", __func__, buf); > + globfree(&globbuf); > + return sock; > +} > + > +static int > +test_telemetry_data(void) > +{ > + typedef int (*test_case)(void); > + unsigned int i = 0; > + > + sock = connect_to_socket(); > + if (sock <= 0) > + return -1; > + > + test_case test_cases[] = {test_case_array_string, > + test_case_array_int, test_case_array_u64, > + test_case_add_dict_int, test_case_add_dict_u64, > + test_case_add_dict_string, > + test_dict_with_array_int_values, > + test_dict_with_array_u64_values, > + test_dict_with_array_string_values, > + test_array_with_array_int_values, > + test_array_with_array_u64_values, > + test_array_with_array_string_values }; > + > + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); > + for (i = 0; i < RTE_DIM(test_cases); i++) { > + if (test_cases[i]() != 0) { > + close(sock); > + return -1; > + } > + } Thanks, this is much more readable than the previous version. > + close(sock); > + return 0; > +} > + > +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); > -- > 2.17.1 > ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v6 3/3] ethdev: add common stats for telemetry 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 1/3] telemetry: support array values in data objects Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-20 11:19 ` Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 11:19 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Updated help text and commit subject line. v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 7858ad5f1..8ee14b1b5 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5275,6 +5275,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5361,6 +5412,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (6 preceding siblings ...) 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-20 14:04 ` Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 1/3] telemetry: support array values in data objects Ciara Power ` (2 more replies) 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power 9 siblings, 3 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 14:04 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power v7: - Simplified connecting to socket by removing use of glob. - Fixed buffer overflow issue when reading from socket. - Split expected response strings over multiple lines. v6: - Fixed FreeBSD build failure for unit tests. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. v5: Added unit tests for telemetry data to JSON conversion. v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry Louise Kilheeney (1): test/test_telemetry_data: add unit tests for data to JSON app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 ++++++++++++++++++ lib/librte_ethdev/rte_ethdev.c | 53 +++ lib/librte_telemetry/rte_telemetry.h | 70 ++++ .../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 ++ 10 files changed, 647 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v7 1/3] telemetry: support array values in data objects 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-07-20 14:04 ` Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 14:04 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v4: Added missing param description to Doxygen comment. v3: - Modified commit log. - Changed function name to reference container. - Modified Doxygen comments to reference container. 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 | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index d13010b8f..c16541226 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -46,6 +46,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 */ }; /** @@ -136,6 +137,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -190,6 +213,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -264,4 +311,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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 025228273..4469885c3 100644 --- a/lib/librte_telemetry/telemetry.c +++ b/lib/librte_telemetry/telemetry.c @@ -123,6 +123,35 @@ command_help(const char *cmd __rte_unused, const char *params, return 0; } +static int +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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) { @@ -169,6 +198,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 (container_to_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; @@ -177,6 +220,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]; @@ -197,6 +241,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 (container_to_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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 1/3] telemetry: support array values in data objects Ciara Power @ 2020-07-20 14:04 ` Ciara Power 2020-07-20 14:32 ` Bruce Richardson 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 1 reply; 44+ messages in thread From: Ciara Power @ 2020-07-20 14:04 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Louise Kilheeney, Ciara Power From: Louise Kilheeney <louise.kilheeney@intel.com> This patch adds tests for verifying telemetry data structures are converted to JSON as expected. Both flat and recursive data structures are tested, for all possible value types. The app connects to the telemetry socket as a client, and registers one command with a corresponding callback function. Each time the callback function is called, it copies a global data variable to the data pointer passed in by telemetry. When a test case is run, the test case function builds up the global data variable with the relevant data types, and the expected json string output which should be generated from that. The 'test_output()' function is used to trigger the callback and ensure the actual output matches that expected. Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> Signed-off-by: Ciara Power <ciara.power@intel.com> --- v7: - Simplified connecting to socket by removing use of glob. - Fixed buffer overflow issue when reading from socket. - Split expected response strings over multiple lines. v6: - Fixed FreeBSD build error. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. --- app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 +++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c diff --git a/app/test/Makefile b/app/test/Makefile index f4065271e..1cb64089c 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c SRCS-y += test_debug.c SRCS-y += test_errno.c diff --git a/app/test/meson.build b/app/test/meson.build index 786a21397..4a72fe5b6 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -170,6 +170,7 @@ test_deps = ['acl', 'ring', 'security', 'stack', + 'telemetry', 'timer' ] @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') test_deps += 'pmd_skeleton_event' endif if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') - test_sources += 'test_telemetry_json.c' - fast_tests += [['telemetry_json_autotest', true]] + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] endif # The following linkages of drivers are required because diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c new file mode 100644 index 000000000..7a31e68a7 --- /dev/null +++ b/app/test/test_telemetry_data.c @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 Intel Corporation + */ + +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <limits.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_telemetry.h> +#include <rte_string_fns.h> + +#include "test.h" +#include "telemetry_data.h" + +#define TELEMETRY_VERSION "v2" +#define REQUEST_CMD "/test" +#define BUF_SIZE 1024 +#define TEST_OUTPUT(exp) test_output(__func__, exp) + +static struct rte_tel_data response_data; +static int sock; + +/* + * This function is the callback registered with Telemetry to be used when + * the /test command is requested. This callback returns the global data built + * up by the individual test cases. + */ +static int +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + *d = response_data; + return 0; +} + +/* + * This function is called by each test case function. It communicates with + * the telemetry socket by requesting the /test command, and reading the + * response. The expected response is passed in by the test case function, + * and is compared to the actual response received from Telemetry. + */ +static int +test_output(const char *func_name, const char *expected) +{ + int bytes; + char buf[BUF_SIZE * 16]; + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { + printf("%s: Error with socket write - %s\n", __func__, + strerror(errno)); + return -1; + } + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); + return strncmp(expected, buf, sizeof(buf)); +} + +static int +test_dict_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4]," + "\"dict_1\":[0,1,2,3,4]}}"); +} + +static int +test_array_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +test_case_array_int(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_int(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_int(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_int(&response_data, name_of_value, i); + } + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_case_array_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); + rte_tel_data_add_array_string(&response_data, "aaaa"); + rte_tel_data_add_array_string(&response_data, "bbbb"); + rte_tel_data_add_array_string(&response_data, "cccc"); + rte_tel_data_add_array_string(&response_data, "dddd"); + rte_tel_data_add_array_string(&response_data, "eeee"); + + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\"," + "\"eeee\"]}"); +} + +static int +test_case_add_dict_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":" + "\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); +} + + +static int +test_dict_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":" + "[\"bbbb\"]}}"); +} + +static int +test_array_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); +} + +static int +test_case_array_u64(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_u64(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_u64(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); + } + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_dict_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 10; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9]," + "\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); +} + +static int +test_array_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +connect_to_socket(void) +{ + char buf[BUF_SIZE]; + int sock, bytes; + struct sockaddr_un telem_addr; + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + printf("\n%s: Error creating socket: %s\n", __func__, + strerror(errno)); + return -1; + } + telem_addr.sun_family = AF_UNIX; + snprintf(telem_addr.sun_path, sizeof(telem_addr.sun_path), + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), + TELEMETRY_VERSION); + if (connect(sock, (struct sockaddr *) &telem_addr, + sizeof(telem_addr)) < 0) + printf("\n%s: Error connecting to socket: %s\n", __func__, + strerror(errno)); + + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("\n%s: %s\n", __func__, buf); + return sock; +} + +static int +test_telemetry_data(void) +{ + typedef int (*test_case)(void); + unsigned int i = 0; + + sock = connect_to_socket(); + if (sock <= 0) + return -1; + + test_case test_cases[] = {test_case_array_string, + test_case_array_int, test_case_array_u64, + test_case_add_dict_int, test_case_add_dict_u64, + test_case_add_dict_string, + test_dict_with_array_int_values, + test_dict_with_array_u64_values, + test_dict_with_array_string_values, + test_array_with_array_int_values, + test_array_with_array_u64_values, + test_array_with_array_string_values }; + + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); + for (i = 0; i < RTE_DIM(test_cases); i++) { + if (test_cases[i]() != 0) { + close(sock); + return -1; + } + } + close(sock); + return 0; +} + +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-20 14:32 ` Bruce Richardson 0 siblings, 0 replies; 44+ messages in thread From: Bruce Richardson @ 2020-07-20 14:32 UTC (permalink / raw) To: Ciara Power Cc: kevin.laatz, thomas, ferruh.yigit, arybchenko, dev, keith.wiles, Louise Kilheeney On Mon, Jul 20, 2020 at 03:04:02PM +0100, Ciara Power wrote: > From: Louise Kilheeney <louise.kilheeney@intel.com> > > This patch adds tests for verifying telemetry data structures are > converted to JSON as expected. Both flat and recursive data structures > are tested, for all possible value types. > > The app connects to the telemetry socket as a client, and registers one > command with a corresponding callback function. Each time the callback > function is called, it copies a global data variable to the data pointer > passed in by telemetry. > When a test case is run, the test case function builds up the global > data variable with the relevant data types, and the expected json string > output which should be generated from that. The 'test_output()' function > is used to trigger the callback and ensure the actual output matches > that expected. > > Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> > Signed-off-by: Ciara Power <ciara.power@intel.com> > Acked-by: Bruce Richardson <bruce.richardson@intel.com> ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v7 3/3] ethdev: add common stats for telemetry 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 1/3] telemetry: support array values in data objects Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-07-20 14:04 ` Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-07-20 14:04 UTC (permalink / raw) To: kevin.laatz, thomas, ferruh.yigit, arybchenko Cc: dev, keith.wiles, bruce.richardson, Ciara Power The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v3: - Updated help text and commit subject line. v2: - Updated to use memory management APIs. --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 7858ad5f1..8ee14b1b5 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5275,6 +5275,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5361,6 +5412,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (7 preceding siblings ...) 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-08-21 12:51 ` Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 1/3] telemetry: support array values in data objects Ciara Power ` (2 more replies) 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power 9 siblings, 3 replies; 44+ messages in thread From: Ciara Power @ 2020-08-21 12:51 UTC (permalink / raw) To: dev; +Cc: keith.wiles, bruce.richardson, Ciara Power v8: Rebased onto main. v7: - Simplified connecting to socket by removing use of glob. - Fixed buffer overflow issue when reading from socket. - Split expected response strings over multiple lines. v6: - Fixed FreeBSD build failure for unit tests. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. v5: Added unit tests for telemetry data to JSON conversion. v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry Louise Kilheeney (1): test/test_telemetry_data: add unit tests for data to JSON app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 ++++++++++++++++++ lib/librte_ethdev/rte_ethdev.c | 53 +++ lib/librte_telemetry/rte_telemetry.h | 70 ++++ .../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 ++ 10 files changed, 647 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v8 1/3] telemetry: support array values in data objects 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-08-21 12:51 ` Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-08-21 12:51 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Ciara Power, Kevin Laatz, Ray Kinsella, Neil Horman Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- lib/librte_telemetry/rte_telemetry.h | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index d13010b8fb..c165412260 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -46,6 +46,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 */ }; /** @@ -136,6 +137,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -190,6 +213,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -264,4 +311,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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 86433c21d0..d1dbf8d586 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 0252282735..4469885c34 100644 --- a/lib/librte_telemetry/telemetry.c +++ b/lib/librte_telemetry/telemetry.c @@ -123,6 +123,35 @@ command_help(const char *cmd __rte_unused, const char *params, return 0; } +static int +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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) { @@ -169,6 +198,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 (container_to_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; @@ -177,6 +220,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]; @@ -197,6 +241,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 (container_to_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 f424bbd48f..77b0fe09a5 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 ff3a371a33..adb84a09f1 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 a2ce4899e0..ad270b9b30 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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v8 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 1/3] telemetry: support array values in data objects Ciara Power @ 2020-08-21 12:51 ` Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 3/3] ethdev: add common stats for telemetry Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-08-21 12:51 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Louise Kilheeney, Ciara Power, Kevin Laatz From: Louise Kilheeney <louise.kilheeney@intel.com> This patch adds tests for verifying telemetry data structures are converted to JSON as expected. Both flat and recursive data structures are tested, for all possible value types. The app connects to the telemetry socket as a client, and registers one command with a corresponding callback function. Each time the callback function is called, it copies a global data variable to the data pointer passed in by telemetry. When a test case is run, the test case function builds up the global data variable with the relevant data types, and the expected json string output which should be generated from that. The 'test_output()' function is used to trigger the callback and ensure the actual output matches that expected. Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- app/test/Makefile | 1 + app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 +++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c diff --git a/app/test/Makefile b/app/test/Makefile index f4065271e4..1cb64089c1 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -145,6 +145,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_json.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += test_telemetry_data.c SRCS-y += test_debug.c SRCS-y += test_errno.c diff --git a/app/test/meson.build b/app/test/meson.build index 786a213972..4a72fe5b61 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -170,6 +170,7 @@ test_deps = ['acl', 'ring', 'security', 'stack', + 'telemetry', 'timer' ] @@ -345,8 +346,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') test_deps += 'pmd_skeleton_event' endif if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') - test_sources += 'test_telemetry_json.c' - fast_tests += [['telemetry_json_autotest', true]] + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] endif # The following linkages of drivers are required because diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c new file mode 100644 index 0000000000..7a31e68a78 --- /dev/null +++ b/app/test/test_telemetry_data.c @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 Intel Corporation + */ + +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <limits.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_telemetry.h> +#include <rte_string_fns.h> + +#include "test.h" +#include "telemetry_data.h" + +#define TELEMETRY_VERSION "v2" +#define REQUEST_CMD "/test" +#define BUF_SIZE 1024 +#define TEST_OUTPUT(exp) test_output(__func__, exp) + +static struct rte_tel_data response_data; +static int sock; + +/* + * This function is the callback registered with Telemetry to be used when + * the /test command is requested. This callback returns the global data built + * up by the individual test cases. + */ +static int +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + *d = response_data; + return 0; +} + +/* + * This function is called by each test case function. It communicates with + * the telemetry socket by requesting the /test command, and reading the + * response. The expected response is passed in by the test case function, + * and is compared to the actual response received from Telemetry. + */ +static int +test_output(const char *func_name, const char *expected) +{ + int bytes; + char buf[BUF_SIZE * 16]; + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { + printf("%s: Error with socket write - %s\n", __func__, + strerror(errno)); + return -1; + } + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); + return strncmp(expected, buf, sizeof(buf)); +} + +static int +test_dict_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4]," + "\"dict_1\":[0,1,2,3,4]}}"); +} + +static int +test_array_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +test_case_array_int(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_int(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_int(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_int(&response_data, name_of_value, i); + } + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_case_array_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); + rte_tel_data_add_array_string(&response_data, "aaaa"); + rte_tel_data_add_array_string(&response_data, "bbbb"); + rte_tel_data_add_array_string(&response_data, "cccc"); + rte_tel_data_add_array_string(&response_data, "dddd"); + rte_tel_data_add_array_string(&response_data, "eeee"); + + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\"," + "\"eeee\"]}"); +} + +static int +test_case_add_dict_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":" + "\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); +} + + +static int +test_dict_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":" + "[\"bbbb\"]}}"); +} + +static int +test_array_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); +} + +static int +test_case_array_u64(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_u64(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_u64(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); + } + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_dict_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 10; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9]," + "\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); +} + +static int +test_array_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +connect_to_socket(void) +{ + char buf[BUF_SIZE]; + int sock, bytes; + struct sockaddr_un telem_addr; + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + printf("\n%s: Error creating socket: %s\n", __func__, + strerror(errno)); + return -1; + } + telem_addr.sun_family = AF_UNIX; + snprintf(telem_addr.sun_path, sizeof(telem_addr.sun_path), + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), + TELEMETRY_VERSION); + if (connect(sock, (struct sockaddr *) &telem_addr, + sizeof(telem_addr)) < 0) + printf("\n%s: Error connecting to socket: %s\n", __func__, + strerror(errno)); + + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("\n%s: %s\n", __func__, buf); + return sock; +} + +static int +test_telemetry_data(void) +{ + typedef int (*test_case)(void); + unsigned int i = 0; + + sock = connect_to_socket(); + if (sock <= 0) + return -1; + + test_case test_cases[] = {test_case_array_string, + test_case_array_int, test_case_array_u64, + test_case_add_dict_int, test_case_add_dict_u64, + test_case_add_dict_string, + test_dict_with_array_int_values, + test_dict_with_array_u64_values, + test_dict_with_array_string_values, + test_array_with_array_int_values, + test_array_with_array_u64_values, + test_array_with_array_string_values }; + + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); + for (i = 0; i < RTE_DIM(test_cases); i++) { + if (test_cases[i]() != 0) { + close(sock); + return -1; + } + } + close(sock); + return 0; +} + +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v8 3/3] ethdev: add common stats for telemetry 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 1/3] telemetry: support array values in data objects Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-08-21 12:51 ` Ciara Power 2 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-08-21 12:51 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Ciara Power, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index 7858ad5f11..8ee14b1b54 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5275,6 +5275,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5361,6 +5412,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power ` (8 preceding siblings ...) 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-09-23 11:12 ` Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 1/3] telemetry: support array values in data objects Ciara Power ` (3 more replies) 9 siblings, 4 replies; 44+ messages in thread From: Ciara Power @ 2020-09-23 11:12 UTC (permalink / raw) To: dev; +Cc: keith.wiles, bruce.richardson, Ciara Power v9: Rebased onto main to remove conflict. v8: Rebased onto main. v7: - Simplified connecting to socket by removing use of glob. - Fixed buffer overflow issue when reading from socket. - Split expected response strings over multiple lines. v6: - Fixed FreeBSD build failure for unit tests. - Added comments and expanded commit log. - Add loop to call test cases stored in a list. v5: Added unit tests for telemetry data to JSON conversion. v4: Added missing param description to Doxygen comment. v3: - Modified commit logs. - Changed function names to reference container. - Modified Doxygen comments to reference container. 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. This patchset adds support for basic ethdev statistics in Telemetry. To do this, recursive data object support is needed to report the queue statistics in a list. With this patch, an array or dictionary supports uint64_t, int or string array type values, which is used for the ethdev queue stats. Ciara Power (2): telemetry: support array values in data objects ethdev: add common stats for telemetry Louise Kilheeney (1): test/test_telemetry_data: add unit tests for data to JSON app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 ++++++++++++++++++ lib/librte_ethdev/rte_ethdev.c | 53 +++ lib/librte_telemetry/rte_telemetry.h | 70 ++++ .../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 ++ 9 files changed, 646 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v9 1/3] telemetry: support array values in data objects 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power @ 2020-09-23 11:12 ` Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power ` (2 subsequent siblings) 3 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-09-23 11:12 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Ciara Power, Kevin Laatz, Ray Kinsella, Neil Horman Arrays of type uint64_t/int/string can now be included within an array or dict. One level of embedded containers is supported. This is necessary to allow for instances such as the ethdev queue stats to be reported as a list of uint64_t values, rather than having multiple dict entries with one uint64_t value for each queue stat. The memory management APIs provided by telemetry simplify the memory allocation/free aspect of the embedded container. The rte_tel_data_alloc function is called in the library/app callback to return a pointer to a container that has been allocated memory. When adding this container to an array/dict, a parameter is passed to indicate if the memory should be freed by telemetry after use. This will allow reuse of the allocated memory if the library/app wishes to do so. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- lib/librte_telemetry/rte_telemetry.h | 70 +++++++++++++++++++ .../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, 221 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index deac2e71ca..4693275c24 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -47,6 +47,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 */ }; /** @@ -137,6 +138,28 @@ __rte_experimental int rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); +/** + * Add a container to an array. A container is an existing telemetry data + * array. The array the container is to be added to must have been started by + * rte_tel_data_start_array() with RTE_TEL_CONTAINER as the type parameter. + * The container type must be an array of type uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param val + * The pointer to the container to be stored in the array. + * @param keep + * 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(). @@ -191,6 +214,30 @@ int rte_tel_data_add_dict_u64(struct rte_tel_data *d, const char *name, uint64_t val); +/** + * Add a container to a dictionary. A container is an existing telemetry data + * array. The dict the container is to be added to must have been started by + * rte_tel_data_start_dict(). The container must be an array of type + * uint64_t/int/string. + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict. + * @param val + * The pointer to the container to be stored in the dict. + * @param keep + * 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_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 @@ -265,4 +312,27 @@ int rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, const char **err_str); +/** + * Get a pointer to a container with memory allocated. The container is to be + * used embedded within an existing telemetry dict/array. + * + * @return + * Pointer to a container. + */ +__rte_experimental +struct rte_tel_data * +rte_tel_data_alloc(void); + +/** + * @internal + * Free a container that has memory allocated. + * + * @param data + * Pointer to container. + *. + */ +__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 86433c21d0..d1dbf8d586 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 51e7ceeb1b..caa2c4a535 100644 --- a/lib/librte_telemetry/telemetry.c +++ b/lib/librte_telemetry/telemetry.c @@ -133,6 +133,35 @@ command_help(const char *cmd __rte_unused, const char *params, return 0; } +static int +container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) +{ + 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) { @@ -179,6 +208,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 (container_to_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; @@ -187,6 +230,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]; @@ -207,6 +251,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 (container_to_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 f424bbd48f..77b0fe09a5 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 ff3a371a33..adb84a09f1 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 a2ce4899e0..ad270b9b30 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 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v9 2/3] test/test_telemetry_data: add unit tests for data to JSON 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 1/3] telemetry: support array values in data objects Ciara Power @ 2020-09-23 11:12 ` Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 3/3] ethdev: add common stats for telemetry Ciara Power 2020-10-06 20:56 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Thomas Monjalon 3 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-09-23 11:12 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Louise Kilheeney, Ciara Power, Kevin Laatz From: Louise Kilheeney <louise.kilheeney@intel.com> This patch adds tests for verifying telemetry data structures are converted to JSON as expected. Both flat and recursive data structures are tested, for all possible value types. The app connects to the telemetry socket as a client, and registers one command with a corresponding callback function. Each time the callback function is called, it copies a global data variable to the data pointer passed in by telemetry. When a test case is run, the test case function builds up the global data variable with the relevant data types, and the expected json string output which should be generated from that. The 'test_output()' function is used to trigger the callback and ensure the actual output matches that expected. Signed-off-by: Louise Kilheeney <louise.kilheeney@intel.com> Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v9: Removed Makefile entry as this is removed on main branch. --- app/test/meson.build | 5 +- app/test/test_telemetry_data.c | 369 +++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 app/test/test_telemetry_data.c diff --git a/app/test/meson.build b/app/test/meson.build index 181e870290..dedf29dd7f 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -171,6 +171,7 @@ test_deps = ['acl', 'ring', 'security', 'stack', + 'telemetry', 'timer' ] @@ -347,8 +348,8 @@ if dpdk_conf.has('RTE_LIBRTE_SKELETON_EVENTDEV_PMD') test_deps += 'pmd_skeleton_event' endif if dpdk_conf.has('RTE_LIBRTE_TELEMETRY') - test_sources += 'test_telemetry_json.c' - fast_tests += [['telemetry_json_autotest', true]] + test_sources += ['test_telemetry_json.c', 'test_telemetry_data.c'] + fast_tests += [['telemetry_json_autotest', true], ['telemetry_data_autotest', true]] endif # The following linkages of drivers are required because diff --git a/app/test/test_telemetry_data.c b/app/test/test_telemetry_data.c new file mode 100644 index 0000000000..7a31e68a78 --- /dev/null +++ b/app/test/test_telemetry_data.c @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 Intel Corporation + */ + +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <limits.h> + +#include <rte_eal.h> +#include <rte_common.h> +#include <rte_telemetry.h> +#include <rte_string_fns.h> + +#include "test.h" +#include "telemetry_data.h" + +#define TELEMETRY_VERSION "v2" +#define REQUEST_CMD "/test" +#define BUF_SIZE 1024 +#define TEST_OUTPUT(exp) test_output(__func__, exp) + +static struct rte_tel_data response_data; +static int sock; + +/* + * This function is the callback registered with Telemetry to be used when + * the /test command is requested. This callback returns the global data built + * up by the individual test cases. + */ +static int +test_cb(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + *d = response_data; + return 0; +} + +/* + * This function is called by each test case function. It communicates with + * the telemetry socket by requesting the /test command, and reading the + * response. The expected response is passed in by the test case function, + * and is compared to the actual response received from Telemetry. + */ +static int +test_output(const char *func_name, const char *expected) +{ + int bytes; + char buf[BUF_SIZE * 16]; + if (write(sock, REQUEST_CMD, strlen(REQUEST_CMD)) < 0) { + printf("%s: Error with socket write - %s\n", __func__, + strerror(errno)); + return -1; + } + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("%s: buf = '%s', expected = '%s'\n", func_name, buf, expected); + return strncmp(expected, buf, sizeof(buf)); +} + +static int +test_dict_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4]," + "\"dict_1\":[0,1,2,3,4]}}"); +} + +static int +test_array_with_array_int_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_INT_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_INT_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_int(child_data, i); + rte_tel_data_add_array_int(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +test_case_array_int(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_INT_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_int(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_int(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_int(&response_data, name_of_value, i); + } + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_case_array_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_STRING_VAL); + rte_tel_data_add_array_string(&response_data, "aaaa"); + rte_tel_data_add_array_string(&response_data, "bbbb"); + rte_tel_data_add_array_string(&response_data, "cccc"); + rte_tel_data_add_array_string(&response_data, "dddd"); + rte_tel_data_add_array_string(&response_data, "eeee"); + + return TEST_OUTPUT("{\"/test\":[\"aaaa\",\"bbbb\",\"cccc\",\"dddd\"," + "\"eeee\"]}"); +} + +static int +test_case_add_dict_string(void) +{ + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_dict_string(&response_data, "dict_0", "aaaa"); + rte_tel_data_add_dict_string(&response_data, "dict_1", "bbbb"); + rte_tel_data_add_dict_string(&response_data, "dict_2", "cccc"); + rte_tel_data_add_dict_string(&response_data, "dict_3", "dddd"); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":\"aaaa\",\"dict_1\":" + "\"bbbb\",\"dict_2\":\"cccc\",\"dict_3\":\"dddd\"}}"); +} + + +static int +test_dict_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[\"aaaa\"],\"dict_1\":" + "[\"bbbb\"]}}"); +} + +static int +test_array_with_array_string_values(void) +{ + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_STRING_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_STRING_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + rte_tel_data_add_array_string(child_data, "aaaa"); + rte_tel_data_add_array_string(child_data2, "bbbb"); + + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[\"aaaa\"],[\"bbbb\"]]}"); +} + +static int +test_case_array_u64(void) +{ + int i; + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_U64_VAL); + for (i = 0; i < 5; i++) + rte_tel_data_add_array_u64(&response_data, i); + return TEST_OUTPUT("{\"/test\":[0,1,2,3,4]}"); +} + +static int +test_case_add_dict_u64(void) +{ + int i = 0; + char name_of_value[8]; + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 5; i++) { + sprintf(name_of_value, "dict_%d", i); + rte_tel_data_add_dict_u64(&response_data, name_of_value, i); + } + return TEST_OUTPUT("{\"/test\":{\"dict_0\":0,\"dict_1\":1,\"dict_2\":2," + "\"dict_3\":3,\"dict_4\":4}}"); +} + +static int +test_dict_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_dict(&response_data); + + for (i = 0; i < 10; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + + rte_tel_data_add_dict_container(&response_data, "dict_0", + child_data, 0); + rte_tel_data_add_dict_container(&response_data, "dict_1", + child_data2, 0); + + return TEST_OUTPUT("{\"/test\":{\"dict_0\":[0,1,2,3,4,5,6,7,8,9]," + "\"dict_1\":[0,1,2,3,4,5,6,7,8,9]}}"); +} + +static int +test_array_with_array_u64_values(void) +{ + int i; + + struct rte_tel_data *child_data = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data, RTE_TEL_U64_VAL); + + struct rte_tel_data *child_data2 = rte_tel_data_alloc(); + rte_tel_data_start_array(child_data2, RTE_TEL_U64_VAL); + + memset(&response_data, 0, sizeof(response_data)); + rte_tel_data_start_array(&response_data, RTE_TEL_CONTAINER); + + for (i = 0; i < 5; i++) { + rte_tel_data_add_array_u64(child_data, i); + rte_tel_data_add_array_u64(child_data2, i); + } + rte_tel_data_add_array_container(&response_data, child_data, 0); + rte_tel_data_add_array_container(&response_data, child_data2, 0); + + return TEST_OUTPUT("{\"/test\":[[0,1,2,3,4],[0,1,2,3,4]]}"); +} + +static int +connect_to_socket(void) +{ + char buf[BUF_SIZE]; + int sock, bytes; + struct sockaddr_un telem_addr; + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + printf("\n%s: Error creating socket: %s\n", __func__, + strerror(errno)); + return -1; + } + telem_addr.sun_family = AF_UNIX; + snprintf(telem_addr.sun_path, sizeof(telem_addr.sun_path), + "%s/dpdk_telemetry.%s", rte_eal_get_runtime_dir(), + TELEMETRY_VERSION); + if (connect(sock, (struct sockaddr *) &telem_addr, + sizeof(telem_addr)) < 0) + printf("\n%s: Error connecting to socket: %s\n", __func__, + strerror(errno)); + + bytes = read(sock, buf, sizeof(buf) - 1); + if (bytes < 0) { + printf("%s: Error with socket read - %s\n", __func__, + strerror(errno)); + return -1; + } + buf[bytes] = '\0'; + printf("\n%s: %s\n", __func__, buf); + return sock; +} + +static int +test_telemetry_data(void) +{ + typedef int (*test_case)(void); + unsigned int i = 0; + + sock = connect_to_socket(); + if (sock <= 0) + return -1; + + test_case test_cases[] = {test_case_array_string, + test_case_array_int, test_case_array_u64, + test_case_add_dict_int, test_case_add_dict_u64, + test_case_add_dict_string, + test_dict_with_array_int_values, + test_dict_with_array_u64_values, + test_dict_with_array_string_values, + test_array_with_array_int_values, + test_array_with_array_u64_values, + test_array_with_array_string_values }; + + rte_telemetry_register_cmd(REQUEST_CMD, test_cb, "Test"); + for (i = 0; i < RTE_DIM(test_cases); i++) { + if (test_cases[i]() != 0) { + close(sock); + return -1; + } + } + close(sock); + return 0; +} + +REGISTER_TEST_COMMAND(telemetry_data_autotest, test_telemetry_data); -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* [dpdk-dev] [PATCH v9 3/3] ethdev: add common stats for telemetry 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 1/3] telemetry: support array values in data objects Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power @ 2020-09-23 11:12 ` Ciara Power 2020-10-06 20:56 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Thomas Monjalon 3 siblings, 0 replies; 44+ messages in thread From: Ciara Power @ 2020-09-23 11:12 UTC (permalink / raw) To: dev Cc: keith.wiles, bruce.richardson, Ciara Power, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko The ethdev library now registers a telemetry command for common ethdev statistics. An example usage is shown below: Connecting to /var/run/dpdk/rte/dpdk_telemetry.v2 {"version": "DPDK 20.08.0-rc1", "pid": 14119, "max_output_len": 16384} --> /ethdev/stats,0 {"/ethdev/stats": {"ipackets": 0, "opackets": 0, "ibytes": 0, "obytes": \ 0, "imissed": 0, "ierrors": 0, "oerrors": 0, "rx_nombuf": 0, \ "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}} Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- lib/librte_ethdev/rte_ethdev.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c index dfe5c1b488..81bb86c433 100644 --- a/lib/librte_ethdev/rte_ethdev.c +++ b/lib/librte_ethdev/rte_ethdev.c @@ -5328,6 +5328,57 @@ handle_port_list(const char *cmd __rte_unused, return 0; } +static void +add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, + const char *stat_name) +{ + int q; + struct rte_tel_data *q_data = rte_tel_data_alloc(); + rte_tel_data_start_array(q_data, RTE_TEL_U64_VAL); + for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) + rte_tel_data_add_array_u64(q_data, q_stats[q]); + rte_tel_data_add_dict_container(d, stat_name, q_data, 0); +} + +#define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_u64(d, #s, stats.s) + +static int +handle_port_stats(const char *cmd __rte_unused, + const char *params, + struct rte_tel_data *d) +{ + struct rte_eth_stats stats; + int port_id, ret; + + if (params == NULL || strlen(params) == 0 || !isdigit(*params)) + return -1; + + port_id = atoi(params); + if (!rte_eth_dev_is_valid_port(port_id)) + return -1; + + ret = rte_eth_stats_get(port_id, &stats); + if (ret < 0) + return -1; + + rte_tel_data_start_dict(d); + ADD_DICT_STAT(stats, ipackets); + ADD_DICT_STAT(stats, opackets); + ADD_DICT_STAT(stats, ibytes); + ADD_DICT_STAT(stats, obytes); + ADD_DICT_STAT(stats, imissed); + ADD_DICT_STAT(stats, ierrors); + ADD_DICT_STAT(stats, oerrors); + ADD_DICT_STAT(stats, rx_nombuf); + add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); + add_port_queue_stats(d, stats.q_opackets, "q_opackets"); + add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); + add_port_queue_stats(d, stats.q_obytes, "q_obytes"); + add_port_queue_stats(d, stats.q_errors, "q_errors"); + + return 0; +} + static int handle_port_xstats(const char *cmd __rte_unused, const char *params, @@ -5414,6 +5465,8 @@ RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd("/ethdev/list", handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); + rte_telemetry_register_cmd("/ethdev/stats", handle_port_stats, + "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/xstats", handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd("/ethdev/link_status", -- 2.17.1 ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power ` (2 preceding siblings ...) 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 3/3] ethdev: add common stats for telemetry Ciara Power @ 2020-10-06 20:56 ` Thomas Monjalon 3 siblings, 0 replies; 44+ messages in thread From: Thomas Monjalon @ 2020-10-06 20:56 UTC (permalink / raw) To: Ciara Power; +Cc: dev, keith.wiles, bruce.richardson, Kilheeney, Louise > This patchset adds support for basic ethdev statistics in Telemetry. > To do this, recursive data object support is needed to report the queue > statistics in a list. With this patch, an array or dictionary supports > uint64_t, int or string array type values, which is used for the ethdev > queue stats. Applied, thanks ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2020-10-06 20:56 UTC | newest] Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-06-12 10:53 [dpdk-dev] [RFC 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-06-12 10:53 ` [dpdk-dev] [RFC 1/2] telemetry: support some recursive data objects Ciara Power 2020-06-12 12:58 ` Bruce Richardson 2020-06-12 13:07 ` Bruce Richardson 2020-06-12 13:14 ` Bruce Richardson 2020-06-12 10:53 ` [dpdk-dev] [RFC 2/2] ethdev: add basic stats for telemetry Ciara Power 2020-06-12 13:10 ` Bruce Richardson 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 1/2] telemetry: support array values in data objects Ciara Power 2020-06-24 15:09 ` Bruce Richardson 2020-06-24 15:18 ` Bruce Richardson 2020-06-24 13:48 ` [dpdk-dev] [PATCH v2 2/2] ethdev: add basic stats for telemetry Ciara Power 2020-06-24 15:19 ` Bruce Richardson 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 1/2] telemetry: support array values in data objects Ciara Power 2020-07-07 7:15 ` Thomas Monjalon 2020-07-02 10:19 ` [dpdk-dev] [PATCH v3 2/2] ethdev: add common stats for telemetry Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 0/2] add basic ethdev stats with data object recursion Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 1/2] telemetry: support array values in data objects Ciara Power 2020-07-13 14:23 ` [dpdk-dev] [PATCH v4 2/2] ethdev: add common stats for telemetry Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 1/3] telemetry: support array values in data objects Ciara Power 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-17 11:53 ` Bruce Richardson 2020-07-15 12:29 ` [dpdk-dev] [PATCH v5 3/3] ethdev: add common stats for telemetry Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 1/3] telemetry: support array values in data objects Ciara Power 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-20 13:08 ` Bruce Richardson 2020-07-20 11:19 ` [dpdk-dev] [PATCH v6 3/3] ethdev: add common stats for telemetry Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 1/3] telemetry: support array values in data objects Ciara Power 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-07-20 14:32 ` Bruce Richardson 2020-07-20 14:04 ` [dpdk-dev] [PATCH v7 3/3] ethdev: add common stats for telemetry Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 1/3] telemetry: support array values in data objects Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-08-21 12:51 ` [dpdk-dev] [PATCH v8 3/3] ethdev: add common stats for telemetry Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 1/3] telemetry: support array values in data objects Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 2/3] test/test_telemetry_data: add unit tests for data to JSON Ciara Power 2020-09-23 11:12 ` [dpdk-dev] [PATCH v9 3/3] ethdev: add common stats for telemetry Ciara Power 2020-10-06 20:56 ` [dpdk-dev] [PATCH v9 0/3] add basic ethdev stats with data object recursion Thomas Monjalon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).