From 36df7e3cd04935eaf0220673ebbe0a179d4dd6af Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Wed, 6 May 2026 22:39:06 -0600 Subject: [PATCH] lib: cmetrics: upgrade to v2.1.3 Signed-off-by: Eduardo Silva --- lib/cmetrics/.github/workflows/build.yaml | 2 +- lib/cmetrics/.github/workflows/packages.yaml | 4 +- lib/cmetrics/CMakeLists.txt | 2 +- lib/cmetrics/src/cmt_decode_msgpack.c | 167 ++++- lib/cmetrics/src/cmt_decode_opentelemetry.c | 190 +++++- .../src/cmt_decode_prometheus_remote_write.c | 261 +++++++- lib/cmetrics/src/cmt_encode_cloudwatch_emf.c | 17 +- lib/cmetrics/src/cmt_encode_influx.c | 42 +- lib/cmetrics/src/cmt_encode_msgpack.c | 7 +- lib/cmetrics/src/cmt_encode_opentelemetry.c | 55 +- lib/cmetrics/src/cmt_encode_prometheus.c | 54 +- .../src/cmt_encode_prometheus_remote_write.c | 126 +++- lib/cmetrics/src/cmt_encode_splunk_hec.c | 47 +- lib/cmetrics/src/cmt_encode_text.c | 40 +- lib/cmetrics/src/cmt_map.c | 15 +- .../tests/data/otlp_null_label_histogram.bin | Bin 0 -> 183 bytes lib/cmetrics/tests/decoding.c | 391 ++++++++++- lib/cmetrics/tests/encoding.c | 382 +++++++++++ lib/cmetrics/tests/histogram.c | 56 +- lib/cmetrics/tests/null_label.c | 176 +++++ lib/cmetrics/tests/opentelemetry.c | 626 ++++++++++++++++++ lib/cmetrics/tests/prometheus_parser.c | 36 +- 22 files changed, 2540 insertions(+), 156 deletions(-) create mode 100644 lib/cmetrics/tests/data/otlp_null_label_histogram.bin diff --git a/lib/cmetrics/.github/workflows/build.yaml b/lib/cmetrics/.github/workflows/build.yaml index 40f8432a8fc..fc46d3670f8 100644 --- a/lib/cmetrics/.github/workflows/build.yaml +++ b/lib/cmetrics/.github/workflows/build.yaml @@ -144,7 +144,7 @@ jobs: submodules: true - name: Build on ${{ matrix.os }} with ${{ matrix.compiler }} - uses: uraimo/run-on-arch-action@v3.0.1 + uses: uraimo/run-on-arch-action@v3.1.0 with: arch: aarch64 distro: ubuntu_latest diff --git a/lib/cmetrics/.github/workflows/packages.yaml b/lib/cmetrics/.github/workflows/packages.yaml index e646b5544f3..621defe2638 100644 --- a/lib/cmetrics/.github/workflows/packages.yaml +++ b/lib/cmetrics/.github/workflows/packages.yaml @@ -22,7 +22,7 @@ jobs: with: submodules: true - - uses: uraimo/run-on-arch-action@v3.0.1 + - uses: uraimo/run-on-arch-action@v3.1.0 name: Build the ${{matrix.format}} packages with: arch: aarch64 @@ -129,7 +129,7 @@ jobs: artifacts/**/* - name: Release on tag - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: generate_release_notes: true diff --git a/lib/cmetrics/CMakeLists.txt b/lib/cmetrics/CMakeLists.txt index 57db65718a5..14a3e3f5bfd 100644 --- a/lib/cmetrics/CMakeLists.txt +++ b/lib/cmetrics/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # CMetrics Version set(CMT_VERSION_MAJOR 2) set(CMT_VERSION_MINOR 1) -set(CMT_VERSION_PATCH 2) +set(CMT_VERSION_PATCH 3) set(CMT_VERSION_STR "${CMT_VERSION_MAJOR}.${CMT_VERSION_MINOR}.${CMT_VERSION_PATCH}") # Include helpers diff --git a/lib/cmetrics/src/cmt_decode_msgpack.c b/lib/cmetrics/src/cmt_decode_msgpack.c index c3a0098789f..80256c20060 100644 --- a/lib/cmetrics/src/cmt_decode_msgpack.c +++ b/lib/cmetrics/src/cmt_decode_msgpack.c @@ -33,6 +33,7 @@ #include #include +#include static int create_counter_instance(struct cmt_map *map) { @@ -330,6 +331,7 @@ static int unpack_label(mpack_reader_t *reader, size_t index, struct cfl_list *target_label_list) { + mpack_tag_t tag; struct cmt_map_label *new_label; int result; @@ -344,7 +346,27 @@ static int unpack_label(mpack_reader_t *reader, return CMT_DECODE_MSGPACK_ALLOCATION_ERROR; } - result = cmt_mpack_consume_string_tag(reader, &new_label->name); + tag = mpack_peek_tag(reader); + if (mpack_ok != mpack_reader_error(reader)) { + free(new_label); + + return CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR; + } + + if (mpack_tag_type(&tag) == mpack_type_nil) { + mpack_expect_nil(reader); + if (mpack_ok != mpack_reader_error(reader)) { + free(new_label); + + return CMT_DECODE_MSGPACK_CORRUPT_INPUT_DATA_ERROR; + } + + new_label->name = NULL; + result = CMT_DECODE_MSGPACK_SUCCESS; + } + else { + result = cmt_mpack_consume_string_tag(reader, &new_label->name); + } if (result != CMT_DECODE_MSGPACK_SUCCESS) { free(new_label); @@ -520,6 +542,11 @@ static int unpack_metric_value_type(mpack_reader_t *reader, size_t index, void * int result; struct cmt_msgpack_decode_context *decode_context; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_uint_tag(reader, &value); @@ -529,6 +556,9 @@ static int unpack_metric_value_type(mpack_reader_t *reader, size_t index, void * value == CMT_METRIC_VALUE_DOUBLE) { cmt_atomic_store(&decode_context->metric->value_type, value); } + else { + result = CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } } return result; @@ -540,6 +570,11 @@ static int unpack_metric_value_int64(mpack_reader_t *reader, size_t index, void int result; struct cmt_msgpack_decode_context *decode_context; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_int_tag(reader, &value); if (result == CMT_DECODE_MSGPACK_SUCCESS) { @@ -558,6 +593,11 @@ static int unpack_metric_value_uint64(mpack_reader_t *reader, size_t index, void int result; struct cmt_msgpack_decode_context *decode_context; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_uint_tag(reader, &value); if (result == CMT_DECODE_MSGPACK_SUCCESS) { @@ -863,9 +903,18 @@ static int unpack_exp_histogram_scale(mpack_reader_t *reader, size_t index, void int64_t value; int result; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_int_tag(reader, &value); if (result == CMT_DECODE_MSGPACK_SUCCESS) { + if (value < INT_MIN || value > INT_MAX) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context->metric->exp_hist_scale = (int32_t) value; } return result; @@ -874,6 +923,12 @@ static int unpack_exp_histogram_scale(mpack_reader_t *reader, size_t index, void static int unpack_exp_histogram_zero_count(mpack_reader_t *reader, size_t index, void *context) { struct cmt_msgpack_decode_context *decode_context; + + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->exp_hist_zero_count); } @@ -881,6 +936,12 @@ static int unpack_exp_histogram_zero_count(mpack_reader_t *reader, size_t index, static int unpack_exp_histogram_zero_threshold(mpack_reader_t *reader, size_t index, void *context) { struct cmt_msgpack_decode_context *decode_context; + + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; return cmt_mpack_consume_double_tag(reader, &decode_context->metric->exp_hist_zero_threshold); } @@ -891,9 +952,18 @@ static int unpack_exp_histogram_positive_offset(mpack_reader_t *reader, size_t i int64_t value; int result; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_int_tag(reader, &value); if (result == CMT_DECODE_MSGPACK_SUCCESS) { + if (value < INT_MIN || value > INT_MAX) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context->metric->exp_hist_positive_offset = (int32_t) value; } return result; @@ -905,9 +975,18 @@ static int unpack_exp_histogram_negative_offset(mpack_reader_t *reader, size_t i int64_t value; int result; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_int_tag(reader, &value); if (result == CMT_DECODE_MSGPACK_SUCCESS) { + if (value < INT_MIN || value > INT_MAX) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context->metric->exp_hist_negative_offset = (int32_t) value; } return result; @@ -916,14 +995,36 @@ static int unpack_exp_histogram_negative_offset(mpack_reader_t *reader, size_t i static int unpack_exp_histogram_positive_bucket(mpack_reader_t *reader, size_t index, void *context) { struct cmt_msgpack_decode_context *decode_context; + + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; + if (decode_context->metric->exp_hist_positive_buckets == NULL || + index >= decode_context->metric->exp_hist_positive_count) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->exp_hist_positive_buckets[index]); } static int unpack_exp_histogram_negative_bucket(mpack_reader_t *reader, size_t index, void *context) { struct cmt_msgpack_decode_context *decode_context; + + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; + if (decode_context->metric->exp_hist_negative_buckets == NULL || + index >= decode_context->metric->exp_hist_negative_count) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + return cmt_mpack_consume_uint_tag(reader, &decode_context->metric->exp_hist_negative_buckets[index]); } @@ -932,6 +1033,11 @@ static int unpack_exp_histogram_positive_buckets(mpack_reader_t *reader, size_t struct cmt_msgpack_decode_context *decode_context; size_t count; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; count = cmt_mpack_peek_array_length(reader); @@ -957,6 +1063,11 @@ static int unpack_exp_histogram_negative_buckets(mpack_reader_t *reader, size_t struct cmt_msgpack_decode_context *decode_context; size_t count; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; count = cmt_mpack_peek_array_length(reader); @@ -983,6 +1094,11 @@ static int unpack_exp_histogram_count(mpack_reader_t *reader, size_t index, void uint64_t value; struct cmt_msgpack_decode_context *decode_context; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_uint_tag(reader, &value); @@ -999,6 +1115,11 @@ static int unpack_exp_histogram_sum_set(mpack_reader_t *reader, size_t index, vo uint64_t value; int result; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_uint_tag(reader, &value); @@ -1016,6 +1137,11 @@ static int unpack_exp_histogram_sum(mpack_reader_t *reader, size_t index, void * uint64_t value; struct cmt_msgpack_decode_context *decode_context; + if (NULL == reader || + NULL == context) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context = (struct cmt_msgpack_decode_context *) context; result = cmt_mpack_consume_uint_tag(reader, &value); @@ -1313,6 +1439,19 @@ static int unpack_meta_type(mpack_reader_t *reader, size_t index, void *context) result = cmt_mpack_consume_uint_tag(reader, &value); if (CMT_DECODE_MSGPACK_SUCCESS == result) { + if (decode_context->map->parent != NULL) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + if (value != CMT_COUNTER && + value != CMT_GAUGE && + value != CMT_SUMMARY && + value != CMT_HISTOGRAM && + value != CMT_EXP_HISTOGRAM && + value != CMT_UNTYPED) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context->map->type = value; result = create_metric_instance(decode_context->map); @@ -1337,6 +1476,12 @@ static int unpack_meta_aggregation_type(mpack_reader_t *reader, size_t index, vo result = cmt_mpack_consume_uint_tag(reader, &value); if (CMT_DECODE_MSGPACK_SUCCESS == result) { + if (value != CMT_AGGREGATION_TYPE_UNSPECIFIED && + value != CMT_AGGREGATION_TYPE_DELTA && + value != CMT_AGGREGATION_TYPE_CUMULATIVE) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + decode_context->aggregation_type = value; } @@ -1346,6 +1491,7 @@ static int unpack_meta_aggregation_type(mpack_reader_t *reader, size_t index, vo static int unpack_meta_opts(mpack_reader_t *reader, size_t index, void *context) { struct cmt_msgpack_decode_context *decode_context; + struct cmt_opts *opts; if (NULL == reader || NULL == context) { @@ -1353,6 +1499,15 @@ static int unpack_meta_opts(mpack_reader_t *reader, size_t index, void *context) } decode_context = (struct cmt_msgpack_decode_context *) context; + opts = decode_context->map->opts; + if (opts == NULL || + opts->ns != NULL || + opts->subsystem != NULL || + opts->name != NULL || + opts->description != NULL || + decode_context->map->unit != NULL) { + return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } return unpack_opts(reader, decode_context->map); } @@ -1505,9 +1660,13 @@ static int unpack_basic_type_meta(mpack_reader_t *reader, size_t index, void *co result = cmt_mpack_unpack_map(reader, callbacks, context); if (CMT_DECODE_MSGPACK_SUCCESS == result) { - if (decode_context->map == NULL || decode_context->map->parent == NULL) { + if (decode_context->map == NULL || + decode_context->map->parent == NULL || + decode_context->map->opts == NULL || + decode_context->map->opts->name == NULL || + decode_context->map->opts->description == NULL) { return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; - } + } decode_context->map->label_count = cfl_list_size(&decode_context->map->label_keys); if (decode_context->map->type == CMT_HISTOGRAM) { @@ -1995,6 +2154,8 @@ int cmt_decode_msgpack_create(struct cmt **out_cmt, char *in_buf, size_t in_size return CMT_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; } + *out_cmt = NULL; + if (0 == in_size || 0 == (in_size - *offset) ) { return CMT_DECODE_MSGPACK_INSUFFICIENT_DATA; diff --git a/lib/cmetrics/src/cmt_decode_opentelemetry.c b/lib/cmetrics/src/cmt_decode_opentelemetry.c index 299d0a421f0..421a1a1ce7c 100644 --- a/lib/cmetrics/src/cmt_decode_opentelemetry.c +++ b/lib/cmetrics/src/cmt_decode_opentelemetry.c @@ -32,6 +32,8 @@ #include #include +#include +#include static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source); @@ -267,7 +269,7 @@ static struct cmt_map_label *create_label(char *caption, size_t length) if (instance != NULL) { if (caption != NULL) { - if (length == 0) { + if (length == (size_t) -1) { length = strlen(caption); } @@ -315,7 +317,8 @@ static uint64_t compute_metric_hash(struct cmt_map *map, struct cmt_metric *samp struct cmt_map_label *label_value; cfl_hash_state_t state; - if (sample == NULL || map == NULL) { + if (sample == NULL || map == NULL || + map->opts == NULL || map->opts->fqname == NULL) { return 0; } @@ -328,6 +331,9 @@ static uint64_t compute_metric_hash(struct cmt_map *map, struct cmt_metric *samp cfl_list_foreach(head, &sample->labels) { label_value = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_value->name == NULL) { + continue; + } cfl_hash_64bits_update(&state, label_value->name, cfl_sds_len(label_value->name)); } @@ -532,7 +538,7 @@ static int append_new_map_label_key(struct cmt_map *map, char *name) { struct cmt_map_label *label; - label = create_label(name, 0); + label = create_label(name, (size_t) -1); if (label == NULL) { return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -567,6 +573,7 @@ static int decode_data_point_labels(struct cmt *cmt, { char dummy_label_value[32]; void **value_index_list; + size_t alloc_count; size_t attribute_index; size_t map_label_index; size_t map_label_count; @@ -583,11 +590,17 @@ static int decode_data_point_labels(struct cmt *cmt, return result; } - if (attribute_count > 127) { + if (attribute_list == NULL) { return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } - value_index_list = calloc(128, sizeof(void *)); + map_label_count = cfl_list_size(&map->label_keys); + if (attribute_count > SIZE_MAX - map_label_count) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + alloc_count = map_label_count + attribute_count; + value_index_list = calloc(alloc_count, sizeof(void *)); if (value_index_list == NULL) { return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -599,6 +612,10 @@ static int decode_data_point_labels(struct cmt *cmt, attribute_index++) { attribute = attribute_list[attribute_index]; + if (attribute == NULL || attribute->key == NULL || attribute->key[0] == '\0') { + result = CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + break; + } label_found = CMT_FALSE; label_index = 0; @@ -606,7 +623,8 @@ static int decode_data_point_labels(struct cmt *cmt, cfl_list_foreach(label_iterator, &map->label_keys) { current_label = cfl_list_entry(label_iterator, struct cmt_map_label, _head); - if (strcmp(current_label->name, attribute->key) == 0) { + if (current_label->name != NULL && + strcmp(current_label->name, attribute->key) == 0) { label_found = CMT_TRUE; break; @@ -620,6 +638,11 @@ static int decode_data_point_labels(struct cmt *cmt, } if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) { + if (label_index >= alloc_count) { + result = CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + break; + } + value_index_list[label_index] = (void *) attribute; } } @@ -631,45 +654,68 @@ static int decode_data_point_labels(struct cmt *cmt, map_label_index < map_label_count ; map_label_index++) { + if (map_label_index >= alloc_count) { + result = CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + break; + } + if (value_index_list[map_label_index] != NULL) { attribute = (Opentelemetry__Proto__Common__V1__KeyValue *) value_index_list[map_label_index]; if (attribute->value == NULL) { + result = append_new_metric_label_value(metric, NULL, 0); continue; } if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { - result = append_new_metric_label_value(metric, attribute->value->string_value, 0); + if (attribute->value->string_value == NULL) { + result = append_new_metric_label_value(metric, NULL, 0); + } + else { + result = append_new_metric_label_value(metric, + attribute->value->string_value, + (size_t) -1); + } } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX) { - result = append_new_metric_label_value(metric, "", 0); + result = append_new_metric_label_value(metric, NULL, 0); } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) { - result = append_new_metric_label_value(metric, - (char *) attribute->value->bytes_value.data, - attribute->value->bytes_value.len); + if (attribute->value->bytes_value.data == NULL && + attribute->value->bytes_value.len > 0) { + result = CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + else { + result = append_new_metric_label_value(metric, + attribute->value->bytes_value.data != NULL ? + (char *) attribute->value->bytes_value.data : "", + attribute->value->bytes_value.len); + } } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) { snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%d", attribute->value->bool_value); - result = append_new_metric_label_value(metric, dummy_label_value, 0); + result = append_new_metric_label_value(metric, dummy_label_value, (size_t) -1); } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE) { snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%" PRIi64, attribute->value->int_value); - result = append_new_metric_label_value(metric, dummy_label_value, 0); + result = append_new_metric_label_value(metric, dummy_label_value, (size_t) -1); } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE) { snprintf(dummy_label_value, sizeof(dummy_label_value) - 1, "%.17g", attribute->value->double_value); - result = append_new_metric_label_value(metric, dummy_label_value, 0); + result = append_new_metric_label_value(metric, dummy_label_value, (size_t) -1); } else { result = append_new_metric_label_value(metric, NULL, 0); } } + else { + result = append_new_metric_label_value(metric, NULL, 0); + } } free(value_index_list); @@ -785,9 +831,17 @@ static int decode_numerical_data_point_list(struct cmt *cmt, result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (data_point_count > 0 && data_point_list == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == 0 && index < data_point_count ; index++) { + if (data_point_list[index] == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = decode_numerical_data_point(cmt, map, data_point_list[index]); } @@ -808,6 +862,10 @@ static int decode_summary_data_point(struct cmt *cmt, summary = (struct cmt_summary *) map->parent; + if (data_point->n_quantile_values > 0 && data_point->quantile_values == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + if (summary->quantiles == NULL) { summary->quantiles = calloc(data_point->n_quantile_values, sizeof(double)); @@ -821,6 +879,10 @@ static int decode_summary_data_point(struct cmt *cmt, for (index = 0 ; index < data_point->n_quantile_values ; index++) { + if (data_point->quantile_values[index] == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + summary->quantiles[index] = data_point->quantile_values[index]->quantile; } } @@ -918,9 +980,17 @@ static int decode_summary_data_point_list(struct cmt *cmt, result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (data_point_count > 0 && data_point_list == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == CMT_DECODE_OPENTELEMETRY_SUCCESS && index < data_point_count ; index++) { + if (data_point_list[index] == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = decode_summary_data_point(cmt, map, data_point_list[index]); } @@ -941,6 +1011,18 @@ static int decode_histogram_data_point(struct cmt *cmt, histogram = (struct cmt_histogram *) map->parent; + if (data_point->n_explicit_bounds > 0 && data_point->explicit_bounds == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (data_point->n_bucket_counts > 0 && data_point->bucket_counts == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (data_point->n_bucket_counts > INT_MAX) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + if (data_point->n_bucket_counts > data_point->n_explicit_bounds + 1) { return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } @@ -998,6 +1080,10 @@ static int decode_histogram_data_point(struct cmt *cmt, struct cfl_kvlist *point_metadata; if (sample->hist_buckets == NULL) { + if (data_point->n_bucket_counts == SIZE_MAX) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + sample->hist_buckets = calloc(data_point->n_bucket_counts + 1, sizeof(uint64_t)); @@ -1055,9 +1141,17 @@ static int decode_histogram_data_point_list(struct cmt *cmt, result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (data_point_count > 0 && data_point_list == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == 0 && index < data_point_count ; index++) { + if (data_point_list[index] == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = decode_histogram_data_point(cmt, map, data_point_list[index]); } @@ -1196,6 +1290,18 @@ static int decode_exponential_histogram_data_point(struct cmt *cmt, new_positive_buckets = NULL; new_negative_buckets = NULL; + if (positive != NULL && + positive->n_bucket_counts > 0 && + positive->bucket_counts == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (negative != NULL && + negative->n_bucket_counts > 0 && + negative->bucket_counts == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + static_metric_detected = CMT_FALSE; if (data_point->n_attributes == 0) { @@ -1329,10 +1435,18 @@ static int decode_exponential_histogram_data_point_list( result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (data_point_count > 0 && data_point_list == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == CMT_DECODE_OPENTELEMETRY_SUCCESS && index < data_point_count ; index++) { + if (data_point_list[index] == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = decode_exponential_histogram_data_point(cmt, map, data_point_list[index]); } @@ -1384,6 +1498,10 @@ static int decode_metrics_entry(struct cmt *cmt, result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (metric == NULL || metric->name == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + metric_name = metric->name; metric_unit = metric->unit; metric_namespace = ""; @@ -1398,6 +1516,10 @@ static int decode_metrics_entry(struct cmt *cmt, } if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUM) { + if (metric->sum == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + instance = cmt_counter_create(cmt, metric_namespace, metric_subsystem, @@ -1438,6 +1560,10 @@ static int decode_metrics_entry(struct cmt *cmt, } } else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) { + if (metric->gauge == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + instance = cmt_gauge_create(cmt, metric_namespace, metric_subsystem, @@ -1478,6 +1604,10 @@ static int decode_metrics_entry(struct cmt *cmt, } } else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_SUMMARY) { + if (metric->summary == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + instance = cmt_summary_create(cmt, metric_namespace, metric_subsystem, @@ -1523,6 +1653,10 @@ static int decode_metrics_entry(struct cmt *cmt, } } else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_HISTOGRAM) { + if (metric->histogram == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + instance = cmt_histogram_create(cmt, metric_namespace, metric_subsystem, @@ -1564,6 +1698,10 @@ static int decode_metrics_entry(struct cmt *cmt, } } else if (metric->data_case == OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_EXPONENTIAL_HISTOGRAM) { + if (metric->exponential_histogram == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + instance = cmt_exp_histogram_create(cmt, metric_namespace, metric_subsystem, @@ -1605,6 +1743,9 @@ static int decode_metrics_entry(struct cmt *cmt, } } } + else { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } return result; } @@ -1718,6 +1859,10 @@ static int decode_scope_metrics_entry(struct cfl_list *context_list, int result; size_t index; + if (metrics == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + context = cmt_create(); if (context == NULL) { @@ -1756,6 +1901,10 @@ static int decode_scope_metrics_entry(struct cfl_list *context_list, return result; } + if (metrics->n_metrics > 0 && metrics->metrics == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == CMT_DECODE_OPENTELEMETRY_SUCCESS && index < metrics->n_metrics ; @@ -1864,6 +2013,15 @@ static int decode_resource_metrics_entry( result = CMT_DECODE_OPENTELEMETRY_SUCCESS; + if (resource_metrics == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (resource_metrics->n_scope_metrics > 0 && + resource_metrics->scope_metrics == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == CMT_DECODE_OPENTELEMETRY_SUCCESS && index < resource_metrics->n_scope_metrics ; @@ -1923,6 +2081,10 @@ static int decode_service_request(struct cfl_list *context_list, result = CMT_DECODE_OPENTELEMETRY_SUCCESS; if (service_request->n_resource_metrics > 0) { + if (service_request->resource_metrics == NULL) { + return CMT_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + for (index = 0 ; result == CMT_DECODE_OPENTELEMETRY_SUCCESS && index < service_request->n_resource_metrics ; diff --git a/lib/cmetrics/src/cmt_decode_prometheus_remote_write.c b/lib/cmetrics/src/cmt_decode_prometheus_remote_write.c index 8c6a98679a8..80645721d1d 100644 --- a/lib/cmetrics/src/cmt_decode_prometheus_remote_write.c +++ b/lib/cmetrics/src/cmt_decode_prometheus_remote_write.c @@ -26,6 +26,9 @@ #include #include +#include +#include + static void *__cmt_allocator_alloc(void *data, size_t size) { return malloc(size); } @@ -46,9 +49,23 @@ static char *cmt_metric_name_from_labels(Prometheus__TimeSeries *ts) int i; int count; + if (ts == NULL || ts->labels == NULL) { + return NULL; + } + count = ts->n_labels; for (i = 0; i < count; i++) { + if (ts->labels[i] == NULL || + ts->labels[i]->name == NULL || + ts->labels[i]->name[0] == '\0') { + continue; + } + if (strncmp("__name__", ts->labels[i]->name, 8) == 0) { + if (ts->labels[i]->value == NULL) { + return NULL; + } + return strdup(ts->labels[i]->value); } } @@ -60,26 +77,28 @@ static struct cmt_map_label *create_map_label(char *caption, size_t length) { struct cmt_map_label *map_label; + if (caption == NULL) { + return NULL; + } + map_label = calloc(1, sizeof(struct cmt_map_label)); if (!map_label) { return NULL; } if (map_label != NULL) { - if (caption != NULL) { - if (length == 0) { - length = strlen(caption); - } + if (length == 0) { + length = strlen(caption); + } - map_label->name = cfl_sds_create_len(caption, length); + map_label->name = cfl_sds_create_len(caption, length); - if (map_label->name == NULL) { - cmt_errno(); + if (map_label->name == NULL) { + cmt_errno(); - free(map_label); + free(map_label); - map_label = NULL; - } + map_label = NULL; } } @@ -124,6 +143,7 @@ static int decode_labels(struct cmt *cmt, Prometheus__Label **labels) { void **value_index_list; + size_t alloc_count; size_t prom_label_index; size_t map_label_index; size_t map_label_count; @@ -140,11 +160,17 @@ static int decode_labels(struct cmt *cmt, return result; } - if (n_labels > 127) { + if (labels == NULL) { return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; } - value_index_list = calloc(128, sizeof(void *)); + map_label_count = cfl_list_size(&map->label_keys); + if (n_labels > SIZE_MAX - map_label_count) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + alloc_count = map_label_count + n_labels; + value_index_list = calloc(alloc_count, sizeof(void *)); if (value_index_list == NULL) { return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; @@ -156,6 +182,10 @@ static int decode_labels(struct cmt *cmt, prom_label_index++) { label = labels[prom_label_index]; + if (label == NULL || label->name == NULL || label->name[0] == '\0') { + result = CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + break; + } label_found = CMT_FALSE; label_index = 0; @@ -163,7 +193,8 @@ static int decode_labels(struct cmt *cmt, cfl_list_foreach(label_iterator, &map->label_keys) { current_label = cfl_list_entry(label_iterator, struct cmt_map_label, _head); - if (strcmp(current_label->name, label->name) == 0) { + if (current_label->name != NULL && + strcmp(current_label->name, label->name) == 0) { label_found = CMT_TRUE; break; @@ -177,6 +208,11 @@ static int decode_labels(struct cmt *cmt, } if (result == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + if (label_index >= alloc_count) { + result = CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + break; + } + value_index_list[label_index] = (void *) label; } } @@ -188,9 +224,19 @@ static int decode_labels(struct cmt *cmt, map_label_index < map_label_count ; map_label_index++) { + if (map_label_index >= alloc_count) { + result = CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + break; + } + if (value_index_list[map_label_index] != NULL) { label = (Prometheus__Label *) value_index_list[map_label_index]; - result = append_new_metric_label_value(metric, label->value, 0); + result = append_new_metric_label_value(metric, + label->value != NULL ? label->value : "", + 0); + } + else { + result = append_new_metric_label_value(metric, "", 0); } } @@ -349,6 +395,10 @@ static int decode_histogram_points(struct cmt *cmt, Prometheus__Label **labels) { int i; + int bucket_index; + size_t count_index; + size_t count_size; + size_t span_index; int static_metric_detected; struct cmt_histogram *histogram; struct cmt_metric *metric; @@ -361,25 +411,90 @@ static int decode_histogram_points(struct cmt *cmt, histogram = (struct cmt_histogram *) map->parent; + if (hist == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (hist->n_negative_spans > 0 && hist->negative_spans == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (hist->n_positive_spans > 0 && hist->positive_spans == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (hist->n_negative_counts > 0 && hist->negative_counts == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (hist->n_positive_counts > 0 && hist->positive_counts == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (hist->n_negative_counts > INT_MAX || + hist->n_positive_counts > INT_MAX) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + if (histogram->buckets == NULL) { if (hist->n_negative_spans > 0) { - spans = calloc(1, sizeof(double) * hist->n_negative_spans); + count_size = hist->n_negative_counts; + spans = calloc(count_size, sizeof(double)); + if (spans == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; + } - for (i = 0; i < hist->n_negative_spans; i++) { - spans[i] = (double) hist->negative_spans[i]->offset; + bucket_index = 0; + count_index = 0; + for (span_index = 0; span_index < hist->n_negative_spans; span_index++) { + if (hist->negative_spans[span_index] == NULL) { + free(spans); + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + bucket_index += hist->negative_spans[span_index]->offset; + + for (i = 0; i < hist->negative_spans[span_index]->length; i++) { + if (count_index >= count_size) { + break; + } + + spans[count_index++] = (double) bucket_index++; + } } + histogram->buckets = cmt_histogram_buckets_create_size(spans, - hist->n_negative_spans); + count_index); free(spans); } else if (hist->n_positive_spans > 0) { - spans = calloc(1, sizeof(double) * hist->n_positive_spans); + count_size = hist->n_positive_counts; + spans = calloc(count_size, sizeof(double)); + if (spans == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; + } + + bucket_index = 0; + count_index = 0; + for (span_index = 0; span_index < hist->n_positive_spans; span_index++) { + if (hist->positive_spans[span_index] == NULL) { + free(spans); + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + bucket_index += hist->positive_spans[span_index]->offset; - for (i = 0; i < hist->n_positive_spans; i++) { - spans[i] = (double) hist->positive_spans[i]->offset; + for (i = 0; i < hist->positive_spans[span_index]->length; i++) { + if (count_index >= count_size) { + break; + } + + spans[count_index++] = (double) bucket_index++; + } } + histogram->buckets = cmt_histogram_buckets_create_size(spans, - hist->n_positive_spans); + count_index); free(spans); } @@ -426,6 +541,38 @@ static int decode_histogram_points(struct cmt *cmt, map->metric_static_set = CMT_TRUE; } + if (metric->hist_buckets == NULL) { + if (histogram->buckets->count >= INT_MAX) { + if (static_metric_detected == CMT_FALSE) { + destroy_label_list(&metric->labels); + + cfl_list_del(&metric->_head); + + free(metric); + } + + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + metric->hist_buckets = calloc(histogram->buckets->count + 1, + sizeof(uint64_t)); + if (metric->hist_buckets == NULL) { + if (static_metric_detected == CMT_FALSE) { + if (metric->hist_buckets != NULL) { + free(metric->hist_buckets); + } + + destroy_label_list(&metric->labels); + + cfl_list_del(&metric->_head); + + free(metric); + } + + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; + } + } + if (result == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { if (hist->n_negative_spans > 0) { for (i = 0; i < hist->n_negative_counts; i++) { @@ -441,6 +588,10 @@ static int decode_histogram_points(struct cmt *cmt, } else { if (static_metric_detected == CMT_FALSE) { + if (metric->hist_buckets != NULL) { + free(metric->hist_buckets); + } + destroy_label_list(&metric->labels); cfl_list_del(&metric->_head); @@ -463,6 +614,10 @@ static int decode_histogram_points(struct cmt *cmt, } else { if (static_metric_detected == CMT_FALSE) { + if (metric->hist_buckets != NULL) { + free(metric->hist_buckets); + } + destroy_label_list(&metric->labels); cfl_list_del(&metric->_head); @@ -487,6 +642,10 @@ static int decode_histogram_time_series(struct cmt *cmt, result = CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS; + if (hist_count > 0 && ts->histograms == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + for (index = 0; result == 0 && index < hist_count; index++) { histogram = ts->histograms[index]; result = decode_histogram_points(cmt, map, @@ -524,6 +683,7 @@ static int decode_metrics_entry(struct cmt *cmt, Prometheus__WriteRequest *write) { int i; + int j; char *metric_name = NULL; char *metric_subsystem = NULL; char *metric_namespace = NULL; @@ -540,34 +700,60 @@ static int decode_metrics_entry(struct cmt *cmt, result = CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS; ts_count = write->n_timeseries; + if (ts_count > 0 && write->timeseries == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + + if (write->n_metadata > 0 && write->metadata == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + for (i = 0; i < ts_count; i++) { ts = write->timeseries[i]; + if (ts == NULL) { + return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_DECODE_ERROR; + } + meta_count = write->n_metadata; hist_count = ts->n_histograms; - if (meta_count > 0) { - metadata = write->metadata[i]; + metadata = NULL; + + metric_name = cmt_metric_name_from_labels(ts); + if (metric_name == NULL) { + continue; } - if (metadata == NULL) { - type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE; - metric_description = "-"; + + for (j = 0; j < meta_count; j++) { + if (write->metadata[j] == NULL || + write->metadata[j]->metric_family_name == NULL) { + continue; + } + + if (strcmp(write->metadata[j]->metric_family_name, metric_name) == 0) { + metadata = write->metadata[j]; + break; + } } - else if (hist_count > 0) { + + if (hist_count > 0) { type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__HISTOGRAM; + metric_description = metadata != NULL ? metadata->help : "-"; + if (metric_description == NULL) { + metric_description = "-"; + } + } + else if (metadata == NULL) { + type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE; metric_description = "-"; } else { - type = write->metadata[i]->type; - metric_description = write->metadata[i]->help; + type = metadata->type; + metric_description = metadata->help; if (metric_description == NULL) { metric_description = "-"; } } - metric_name = cmt_metric_name_from_labels(ts); - if (metric_name == NULL) { - continue; - } - metric_namespace = ""; metric_subsystem = ""; @@ -635,7 +821,7 @@ static int decode_metrics_entry(struct cmt *cmt, metric_subsystem, metric_name, metric_description, - (struct cmt_histogram_buckets *) cmt, + NULL, 0, NULL); if (instance == NULL) { @@ -643,6 +829,9 @@ static int decode_metrics_entry(struct cmt *cmt, return CMT_DECODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; } + cmt_histogram_buckets_destroy(((struct cmt_histogram *) instance)->buckets); + ((struct cmt_histogram *) instance)->buckets = NULL; + result = decode_histogram_entry(cmt, instance, ts); if (result) { diff --git a/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c b/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c index 1918aa98ef8..266beb7adf6 100644 --- a/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c +++ b/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c @@ -72,7 +72,7 @@ static void pack_basic_header(mpack_writer_t *writer, struct cmt *cmt, mpack_start_array(writer, labels); cfl_list_foreach(head, &map->label_keys) { label_k = cfl_list_entry(head, struct cmt_map_label, _head); - mpack_write_cstr(writer, label_k->name); + mpack_write_cstr(writer, label_k->name != NULL ? label_k->name : ""); } cfl_list_foreach(head, &cmt->static_labels->list) { @@ -246,6 +246,8 @@ static int pack_metric(mpack_writer_t *writer, struct cmt *cmt, double val = 0.0; int c_labels = 0; int static_labels = 0; + int label_key_count; + int label_index; struct cfl_list *head; struct cmt_map_label *label_k; struct cmt_map_label *label_v; @@ -253,6 +255,7 @@ static int pack_metric(mpack_writer_t *writer, struct cmt *cmt, struct cmt_opts *opts = map->opts; c_labels = cfl_list_size(&metric->labels); + label_key_count = map->label_count; s = 3; if (c_labels > 0) { @@ -294,14 +297,20 @@ static int pack_metric(mpack_writer_t *writer, struct cmt *cmt, pack_basic_header_finish(writer); /* dimensions */ - if (c_labels > 0) { + if (c_labels > 0 && label_key_count > 0) { label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + label_index = 0; cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + label_v = cfl_list_entry(head, struct cmt_map_label, _head); - mpack_write_cstr(writer, label_k->name); - mpack_write_cstr(writer, label_v->name); + mpack_write_cstr(writer, label_k->name != NULL ? label_k->name : ""); + mpack_write_cstr(writer, label_v->name != NULL ? label_v->name : ""); + label_index++; label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, _head, &map->label_keys); } diff --git a/lib/cmetrics/src/cmt_encode_influx.c b/lib/cmetrics/src/cmt_encode_influx.c index d60cf2da6bd..a17eac18817 100644 --- a/lib/cmetrics/src/cmt_encode_influx.c +++ b/lib/cmetrics/src/cmt_encode_influx.c @@ -266,11 +266,13 @@ static int append_string(cfl_sds_t *buf, cfl_sds_t str) static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, struct cmt_metric *metric) { - int i; int n; + int emitted_count = 0; int static_count = 0; int static_labels = 0; int has_namespace = CMT_FALSE; + int label_key_count; + int label_index; struct cmt_map_label *label_k; struct cmt_map_label *label_v; struct cfl_list *head; @@ -281,6 +283,12 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, return; } + n = cfl_list_size(&metric->labels); + label_key_count = map->label_count; + if (n > label_key_count) { + return; + } + opts = map->opts; /* Measurement */ @@ -322,34 +330,38 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, } /* Labels / Tags */ - n = cfl_list_size(&metric->labels); - if (n > 0) { - if (static_labels > 0 || has_namespace == CMT_TRUE) { - cfl_sds_cat_safe(buf, ",", 1); - } - + if (n > 0 && label_key_count > 0) { label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); - i = 1; + label_index = 0; cfl_list_foreach(head, &metric->labels) { label_v = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_k->name == NULL || label_v->name == NULL) { + label_index++; + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); + continue; + } + + if (static_labels > 0 || has_namespace == CMT_TRUE || + emitted_count > 0) { + cfl_sds_cat_safe(buf, ",", 1); + } + /* key */ append_string(buf, label_k->name); cfl_sds_cat_safe(buf, "=", 1); append_string(buf, label_v->name); + emitted_count++; - if (i < n) { - cfl_sds_cat_safe(buf, ",", 1); - } - i++; - + label_index++; label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, - _head, &map->label_keys); + _head, &map->label_keys); } } - if (has_namespace == CMT_TRUE || static_labels > 0 || n > 0) { + if (has_namespace == CMT_TRUE || static_labels > 0 || emitted_count > 0) { cfl_sds_cat_safe(buf, " ", 1); } append_metric_value(map, buf, metric); diff --git a/lib/cmetrics/src/cmt_encode_msgpack.c b/lib/cmetrics/src/cmt_encode_msgpack.c index e29ce7cc79d..c9328055615 100644 --- a/lib/cmetrics/src/cmt_encode_msgpack.c +++ b/lib/cmetrics/src/cmt_encode_msgpack.c @@ -129,7 +129,12 @@ static void pack_header(mpack_writer_t *writer, struct cmt *cmt, struct cmt_map cfl_list_foreach(head, &map->label_keys) { label = cfl_list_entry(head, struct cmt_map_label, _head); - mpack_write_cstr(writer, label->name); + if (label->name != NULL) { + mpack_write_cstr(writer, label->name); + } + else { + mpack_write_nil(writer); + } } mpack_finish_array(writer); diff --git a/lib/cmetrics/src/cmt_encode_opentelemetry.c b/lib/cmetrics/src/cmt_encode_opentelemetry.c index ee25fae568e..434746c08c0 100644 --- a/lib/cmetrics/src/cmt_encode_opentelemetry.c +++ b/lib/cmetrics/src/cmt_encode_opentelemetry.c @@ -3195,9 +3195,20 @@ int append_sample_to_metric(struct cmt_opentelemetry_context *context, struct cmt_summary *summary; int result; struct cfl_list *head; + size_t label_name_count; + size_t label_name_index; + size_t sample_label_count; + + sample_label_count = 0; + cfl_list_foreach(head, &sample->labels) { + label_value = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_value->name != NULL) { + sample_label_count++; + } + } attribute_count = cfl_list_size(&context->cmt->static_labels->list) + - cfl_list_size(&sample->labels); + sample_label_count; start_timestamp = 0; if (cmt_metric_has_start_timestamp(sample)) { @@ -3361,11 +3372,44 @@ int append_sample_to_metric(struct cmt_opentelemetry_context *context, } } - label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + label_name_count = map->label_count; + if (sample_label_count > label_name_count) { + destroy_data_point(data_point, map->type); + return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (label_name_count > 0) { + label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + } + + label_name_index = 0; cfl_list_foreach(head, &sample->labels) { label_value = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_value->name == NULL) { + label_name_index++; + if (label_name_index < label_name_count) { + label_name = cfl_list_entry_next(&label_name->_head, + struct cmt_map_label, + _head, &map->label_keys); + } + + continue; + } + + if (label_name_index >= label_name_count) { + destroy_data_point(data_point, map->type); + + return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + if (label_name->name == NULL) { + destroy_data_point(data_point, map->type); + + return CMT_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + attribute = initialize_string_attribute(label_name->name, label_value->name); @@ -3387,8 +3431,11 @@ int append_sample_to_metric(struct cmt_opentelemetry_context *context, return result; } - label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label, - _head, &map->label_keys); + label_name_index++; + if (label_name_index < label_name_count) { + label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label, + _head, &map->label_keys); + } } apply_data_point_metadata_from_otlp_context(context->cmt, map, sample, data_point); diff --git a/lib/cmetrics/src/cmt_encode_prometheus.c b/lib/cmetrics/src/cmt_encode_prometheus.c index b5f76149c75..e144a1aa5f4 100644 --- a/lib/cmetrics/src/cmt_encode_prometheus.c +++ b/lib/cmetrics/src/cmt_encode_prometheus.c @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -272,11 +273,16 @@ static int initialize_temporary_metric(struct cmt_metric *destination, return -1; } - destination_label->name = cfl_sds_create(source_label->name); - if (destination_label->name == NULL) { - free(destination_label); - destroy_temporary_metric_labels(destination); - return -1; + if (source_label->name == NULL) { + destination_label->name = NULL; + } + else { + destination_label->name = cfl_sds_create(source_label->name); + if (destination_label->name == NULL) { + free(destination_label); + destroy_temporary_metric_labels(destination); + return -1; + } } cfl_list_add(&destination_label->_head, &destination->labels); @@ -295,7 +301,9 @@ static void format_metric(struct cmt *cmt, int i; int static_labels = 0; int defined_labels = 0; - struct cmt_map_label *label_k; + int label_key_count; + int label_index; + struct cmt_map_label *label_k = NULL; struct cmt_map_label *label_v; struct cfl_list *head; struct cmt_opts *opts; @@ -309,11 +317,25 @@ static void format_metric(struct cmt *cmt, /* Static labels */ static_labels = cmt_labels_count(cmt->static_labels); + label_key_count = map->label_count; + label_index = 0; + if (label_key_count > 0) { + label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + } cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + label_v = cfl_list_entry(head, struct cmt_map_label, _head); - if (strlen(label_v->name)) { + if (label_k->name != NULL && + label_v->name != NULL) { defined_labels++; } + + label_index++; + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); } if (!fmt->brace_open && (static_labels + defined_labels > 0)) { @@ -335,11 +357,17 @@ static void format_metric(struct cmt *cmt, } i = 1; + label_index = 0; label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + label_v = cfl_list_entry(head, struct cmt_map_label, _head); - if (strlen(label_v->name)) { + if (label_k->name != NULL && + label_v->name != NULL) { fmt->labels_count += add_label(buf, label_k->name, label_v->name); if (i < defined_labels) { cfl_sds_cat_safe(buf, ",", 1); @@ -348,8 +376,9 @@ static void format_metric(struct cmt *cmt, i++; } + label_index++; label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, - _head, &map->label_keys); + _head, &map->label_keys); } } @@ -363,6 +392,7 @@ static void format_metric(struct cmt *cmt, static cfl_sds_t bucket_value_to_string(double val) { int len; + double parsed; cfl_sds_t str; str = cfl_sds_create_size(64); @@ -371,9 +401,13 @@ static cfl_sds_t bucket_value_to_string(double val) } len = snprintf(str, 64, "%g", val); + parsed = strtod(str, NULL); + if (parsed != val || strchr(str, 'e') || strchr(str, 'E')) { + len = snprintf(str, 64, "%.17g", val); + } cfl_sds_len_set(str, len); - if (!strchr(str, '.')) { + if (!strchr(str, '.') && !strchr(str, 'e') && !strchr(str, 'E')) { cfl_sds_cat_safe(&str, ".0", 2); } diff --git a/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c b/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c index f974d557f7b..63ce463c696 100644 --- a/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c +++ b/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c @@ -34,6 +34,14 @@ #define SYNTHETIC_METRIC_HISTOGRAM_COUNT_SEQUENCE_DELTA 10000000 #define SYNTHETIC_METRIC_HISTOGRAM_SUM_SEQUENCE_DELTA 100000000 +struct cmt_prometheus_time_series_entry { + uint64_t label_set_hash; + size_t entries_set; + Prometheus__TimeSeries data; + struct cfl_list _head; + size_t samples_capacity; +}; + static cfl_sds_t render_remote_write_context_to_sds( struct cmt_prometheus_remote_write_context *context); @@ -61,13 +69,13 @@ static int set_up_time_series_for_label_set( struct cmt_prometheus_remote_write_context *context, struct cmt_map *map, struct cmt_metric *metric, - struct cmt_prometheus_time_series **time_series); + struct cmt_prometheus_time_series_entry **time_series); static int pack_metric_metadata(struct cmt_prometheus_remote_write_context *context, struct cmt_map *map, struct cmt_metric *metric); -static int append_metric_to_timeseries(struct cmt_prometheus_time_series *time_series, +static int append_metric_to_timeseries(struct cmt_prometheus_time_series_entry *time_series, struct cmt_metric *metric); static int pack_basic_type(struct cmt_prometheus_remote_write_context *context, @@ -111,7 +119,7 @@ cfl_sds_t render_remote_write_context_to_sds( struct cmt_prometheus_remote_write_context *context) { size_t write_request_size; - struct cmt_prometheus_time_series *time_series_entry; + struct cmt_prometheus_time_series_entry *time_series_entry; struct cmt_prometheus_metric_metadata *metadata_entry; cfl_sds_t result_buffer; size_t entry_index; @@ -143,7 +151,7 @@ cfl_sds_t render_remote_write_context_to_sds( entry_index = 0; cfl_list_foreach(head, &context->time_series_entries) { - time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head); + time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series_entry, _head); context->write_request.timeseries[entry_index++] = &time_series_entry->data; } @@ -176,13 +184,13 @@ cfl_sds_t render_remote_write_context_to_sds( void cmt_destroy_prometheus_remote_write_context( struct cmt_prometheus_remote_write_context *context) { - struct cmt_prometheus_time_series *time_series_entry; + struct cmt_prometheus_time_series_entry *time_series_entry; struct cmt_prometheus_metric_metadata *metadata_entry; struct cfl_list *head; struct cfl_list *tmp; cfl_list_foreach_safe(head, tmp, &context->time_series_entries) { - time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head); + time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series_entry, _head); if (time_series_entry->data.labels != NULL) { destroy_prometheus_label_list(time_series_entry->data.labels, @@ -355,21 +363,25 @@ void destroy_prometheus_label_list(Prometheus__Label **label_list, int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context *context, struct cmt_map *map, struct cmt_metric *metric, - struct cmt_prometheus_time_series **time_series) + struct cmt_prometheus_time_series_entry **time_series) { uint8_t time_series_match_found; size_t label_set_hash_matches; - struct cmt_prometheus_time_series *time_series_entry; + struct cmt_prometheus_time_series_entry *time_series_entry; uint64_t label_set_hash; struct cmt_label *static_label; size_t label_index; size_t label_count; + size_t metric_label_count; + size_t metric_label_emit_count; struct cmt_map_label *label_value; struct cmt_map_label *label_name; Prometheus__Label **label_list; Prometheus__Sample **value_list; int result; struct cfl_list *head; + size_t label_name_count; + size_t label_name_index; label_set_hash = calculate_label_set_hash(&metric->labels, context->sequence_number); @@ -377,7 +389,7 @@ int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context time_series_match_found = CMT_FALSE; cfl_list_foreach(head, &context->time_series_entries) { - time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series, _head); + time_series_entry = cfl_list_entry(head, struct cmt_prometheus_time_series_entry, _head); if (time_series_entry->label_set_hash == label_set_hash) { time_series_match_found = CMT_TRUE; @@ -405,12 +417,21 @@ int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context /* Allocate the memory required for the label and value lists, we need to add * one for the fixed __name__ label */ + metric_label_count = cfl_list_size(&metric->labels); + metric_label_emit_count = 0; + cfl_list_foreach(head, &metric->labels) { + label_value = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_value->name != NULL) { + metric_label_emit_count++; + } + } + label_count = cfl_list_size(&context->cmt->static_labels->list) + - cfl_list_size(&metric->labels) + + metric_label_emit_count + 1; - time_series_entry = calloc(1, sizeof(struct cmt_prometheus_time_series)); + time_series_entry = calloc(1, sizeof(struct cmt_prometheus_time_series_entry)); if (time_series_entry == NULL) { cmt_errno(); @@ -444,11 +465,13 @@ int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context time_series_entry->data.n_labels = label_count; time_series_entry->data.labels = label_list; - time_series_entry->data.n_samples = label_set_hash_matches; + time_series_entry->data.n_samples = 0; time_series_entry->data.samples = value_list; time_series_entry->label_set_hash = label_set_hash; time_series_entry->entries_set = 0; + /* Capacity is initialized to at least one and grows geometrically. */ + time_series_entry->samples_capacity = label_set_hash_matches; /* Initialize the label list */ label_index = 0; @@ -486,12 +509,44 @@ int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context } /* Add the specific labels */ - if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS && label_count > 0) { - label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS && metric_label_count > 0) { + label_name_count = map->label_count; + if (metric_label_count > label_name_count) { + result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + } + else { + label_name = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + } + label_name_index = 0; cfl_list_foreach(head, &metric->labels) { + if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + break; + } + label_value = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_value->name == NULL) { + label_name_index++; + if (label_name_index < label_name_count) { + label_name = cfl_list_entry_next(&label_name->_head, + struct cmt_map_label, + _head, &map->label_keys); + } + + continue; + } + + if (label_name_index >= label_name_count) { + result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + break; + } + + if (label_name->name == NULL) { + result = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR; + break; + } + result = append_entry_to_prometheus_label_list(label_list, &label_index, label_name->name, @@ -502,8 +557,11 @@ int set_up_time_series_for_label_set(struct cmt_prometheus_remote_write_context break; } - label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label, - _head, &map->label_keys); + label_name_index++; + if (label_name_index < label_name_count) { + label_name = cfl_list_entry_next(&label_name->_head, struct cmt_map_label, + _head, &map->label_keys); + } } } @@ -599,12 +657,33 @@ int pack_metric_metadata(struct cmt_prometheus_remote_write_context *context, return 0; } -int append_metric_to_timeseries(struct cmt_prometheus_time_series *time_series, +int append_metric_to_timeseries(struct cmt_prometheus_time_series_entry *time_series, struct cmt_metric *metric) { uint64_t ts; + size_t new_capacity; + Prometheus__Sample **samples; Prometheus__Sample *sample; + if (time_series->entries_set >= time_series->samples_capacity) { + new_capacity = time_series->samples_capacity * 2; + + samples = realloc(time_series->data.samples, + new_capacity * sizeof(Prometheus__Sample *)); + if (samples == NULL) { + cmt_errno(); + + return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR; + } + + memset(samples + time_series->samples_capacity, 0, + (new_capacity - time_series->samples_capacity) * + sizeof(Prometheus__Sample *)); + + time_series->data.samples = samples; + time_series->samples_capacity = new_capacity; + } + sample = calloc(1, sizeof(Prometheus__Sample)); if (sample == NULL) { @@ -620,6 +699,7 @@ int append_metric_to_timeseries(struct cmt_prometheus_time_series *time_series, ts = cmt_metric_get_timestamp(metric); sample->timestamp = ts / 1000000; time_series->data.samples[time_series->entries_set++] = sample; + time_series->data.n_samples = time_series->entries_set; return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS; } @@ -629,7 +709,7 @@ int pack_basic_metric_sample(struct cmt_prometheus_remote_write_context *context struct cmt_metric *metric, int add_metadata) { - struct cmt_prometheus_time_series *time_series; + struct cmt_prometheus_time_series_entry *time_series; int result; result = set_up_time_series_for_label_set(context, map, metric, &time_series); @@ -724,7 +804,7 @@ int pack_complex_metric_sample(struct cmt_prometheus_remote_write_context *conte size_t label_key_count; struct cmt_map_label *additional_label; struct cmt_metric dummy_metric; - struct cmt_prometheus_time_series *time_series; + struct cmt_prometheus_time_series_entry *time_series; struct cmt_map_label *dummy_label; struct cmt_histogram *histogram = NULL; struct cmt_summary *summary; @@ -837,7 +917,7 @@ int pack_complex_metric_sample(struct cmt_prometheus_remote_write_context *conte map->opts->fqname = original_metric_name; if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { - label_key_count = cfl_list_size(&map->label_keys); + label_key_count = map->label_count; original_label_value_count = cfl_list_size(&metric->labels); for (label_value_count = original_label_value_count ; @@ -985,7 +1065,7 @@ int pack_complex_metric_sample(struct cmt_prometheus_remote_write_context *conte "%s_bucket", original_metric_name)); - label_key_count = cfl_list_size(&map->label_keys); + label_key_count = map->label_count; original_label_value_count = cfl_list_size(&metric->labels); for (label_value_count = original_label_value_count ; @@ -1121,6 +1201,7 @@ int pack_complex_type(struct cmt_prometheus_remote_write_context *context, cfl_list_add(&additional_label._head, &map->label_keys); + map->label_count++; #pragma GCC diagnostic pop } @@ -1147,9 +1228,10 @@ int pack_complex_type(struct cmt_prometheus_remote_write_context *context, map->type == CMT_HISTOGRAM || map->type == CMT_EXP_HISTOGRAM) { cfl_list_del(&additional_label._head); + map->label_count--; } - return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS; + return result; } /* Format all the registered metrics in Prometheus Text format */ diff --git a/lib/cmetrics/src/cmt_encode_splunk_hec.c b/lib/cmetrics/src/cmt_encode_splunk_hec.c index 4c7e7433b4f..3e52e9ecce1 100644 --- a/lib/cmetrics/src/cmt_encode_splunk_hec.c +++ b/lib/cmetrics/src/cmt_encode_splunk_hec.c @@ -89,11 +89,16 @@ static int initialize_temporary_metric(struct cmt_metric *destination, return -1; } - destination_label->name = cfl_sds_create(source_label->name); - if (destination_label->name == NULL) { - free(destination_label); - destroy_temporary_metric_labels(destination); - return -1; + if (source_label->name == NULL) { + destination_label->name = NULL; + } + else { + destination_label->name = cfl_sds_create(source_label->name); + if (destination_label->name == NULL) { + free(destination_label); + destroy_temporary_metric_labels(destination); + return -1; + } } cfl_list_add(&destination_label->_head, &destination->labels); @@ -266,10 +271,12 @@ static void format_context_common(struct cmt_splunk_hec_context *context, cfl_sd static void format_metric_labels(struct cmt_splunk_hec_context *context, cfl_sds_t *buf, struct cmt_map *map, struct cmt_metric *metric) { - int i; int n; int count = 0; + int emitted_any = CMT_TRUE; int static_labels = 0; + int label_key_count; + int label_index; struct cmt_map_label *label_k; struct cmt_map_label *label_v; @@ -296,26 +303,38 @@ static void format_metric_labels(struct cmt_splunk_hec_context *context, cfl_sds } n = cfl_list_size(&metric->labels); - if (n > 0) { - cfl_sds_cat_safe(buf, ",", 1); + label_key_count = map->label_count; + if (n > 0 && label_key_count > 0) { label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); - i = 0; + label_index = 0; cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + label_v = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_k->name == NULL || label_v->name == NULL) { + label_index++; + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); + continue; + } + + if (emitted_any == CMT_TRUE) { + cfl_sds_cat_safe(buf, ",", 1); + } cfl_sds_cat_safe(buf, "\"", 1); cfl_sds_cat_safe(buf, label_k->name, cfl_sds_len(label_k->name)); cfl_sds_cat_safe(buf, "\":\"", 3); cfl_sds_cat_safe(buf, label_v->name, cfl_sds_len(label_v->name)); cfl_sds_cat_safe(buf, "\"", 1); - i++; + emitted_any = CMT_TRUE; + label_index++; label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, - _head, &map->label_keys); - if (i < n) { - cfl_sds_cat_safe(buf, ",", 1); - } + _head, &map->label_keys); } } } diff --git a/lib/cmetrics/src/cmt_encode_text.c b/lib/cmetrics/src/cmt_encode_text.c index 0b1b5b2e126..7d499d72cee 100644 --- a/lib/cmetrics/src/cmt_encode_text.c +++ b/lib/cmetrics/src/cmt_encode_text.c @@ -497,6 +497,8 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, int len; int count = 0; int static_labels = 0; + int label_key_count; + int label_index; char tmp[128]; uint64_t ts; struct tm tm; @@ -542,7 +544,28 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, } } - n = cfl_list_size(&metric->labels); + n = 0; + label_key_count = map->label_count; + label_index = 0; + if (label_key_count > 0) { + label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + } + cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + + label_v = cfl_list_entry(head, struct cmt_map_label, _head); + + if (label_k->name != NULL && label_v->name != NULL) { + n++; + } + + label_index++; + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); + } + if (n > 0) { if (static_labels > 0) { cfl_sds_cat_safe(buf, ",", 1); @@ -551,12 +574,24 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, cfl_sds_cat_safe(buf, "{", 1); } + label_index = 0; label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); i = 1; cfl_list_foreach(head, &metric->labels) { + if (label_index >= label_key_count) { + break; + } + label_v = cfl_list_entry(head, struct cmt_map_label, _head); + if (label_k->name == NULL || label_v->name == NULL) { + label_index++; + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); + continue; + } + cfl_sds_cat_safe(buf, label_k->name, cfl_sds_len(label_k->name)); cfl_sds_cat_safe(buf, "=\"", 2); cfl_sds_cat_safe(buf, label_v->name, cfl_sds_len(label_v->name)); @@ -569,8 +604,9 @@ static void format_metric(struct cmt *cmt, cfl_sds_t *buf, struct cmt_map *map, } i++; + label_index++; label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, - _head, &map->label_keys); + _head, &map->label_keys); } cfl_sds_cat_safe(buf, "}", 1); diff --git a/lib/cmetrics/src/cmt_map.c b/lib/cmetrics/src/cmt_map.c index 587e0cf8b34..19549d52b45 100644 --- a/lib/cmetrics/src/cmt_map.c +++ b/lib/cmetrics/src/cmt_map.c @@ -120,11 +120,16 @@ static struct cmt_metric *map_metric_create(uint64_t hash, } name = labels_val[i]; - label->name = cfl_sds_create(name); - if (!label->name) { - cmt_errno(); - free(label); - goto error; + if (name == NULL) { + label->name = NULL; + } + else { + label->name = cfl_sds_create(name); + if (!label->name) { + cmt_errno(); + free(label); + goto error; + } } cfl_list_add(&label->_head, &metric->labels); } diff --git a/lib/cmetrics/tests/data/otlp_null_label_histogram.bin b/lib/cmetrics/tests/data/otlp_null_label_histogram.bin new file mode 100644 index 0000000000000000000000000000000000000000..ae3c81e501fe90949bcf7bc8bc3cd7c476c02308 GIT binary patch literal 183 zcmdDw(6OG=7_7zCIA DW3emP literal 0 HcmV?d00001 diff --git a/lib/cmetrics/tests/decoding.c b/lib/cmetrics/tests/decoding.c index d73e46a12ee..0f121ed3879 100644 --- a/lib/cmetrics/tests/decoding.c +++ b/lib/cmetrics/tests/decoding.c @@ -18,17 +18,245 @@ */ #include +#include +#include +#include +#include +#include #include +#include #include #include #include "cmt_tests.h" +static cfl_sds_t generate_remote_write_payload(char *extra_label_name, + char *extra_label_value) +{ + Prometheus__WriteRequest request; + Prometheus__TimeSeries time_series; + Prometheus__Label name_label; + Prometheus__Label extra_label; + Prometheus__Sample sample; + Prometheus__TimeSeries *time_series_list[1]; + Prometheus__Label *label_list[2]; + Prometheus__Sample *sample_list[1]; + size_t payload_size; + unsigned char *packed_payload; + cfl_sds_t payload; + + prometheus__write_request__init(&request); + prometheus__time_series__init(&time_series); + prometheus__label__init(&name_label); + prometheus__label__init(&extra_label); + prometheus__sample__init(&sample); + + name_label.name = "__name__"; + name_label.value = "rw_metric"; + extra_label.name = extra_label_name; + extra_label.value = extra_label_value; + + label_list[0] = &name_label; + label_list[1] = &extra_label; + time_series.n_labels = 2; + time_series.labels = label_list; + + sample.value = 1.0; + sample.timestamp = 123; + sample_list[0] = &sample; + time_series.n_samples = 1; + time_series.samples = sample_list; + + time_series_list[0] = &time_series; + request.n_timeseries = 1; + request.timeseries = time_series_list; + + payload_size = prometheus__write_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + return NULL; + } + + prometheus__write_request__pack(&request, packed_payload); + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + + return payload; +} + +static cfl_sds_t generate_remote_write_out_of_order_metadata_payload() +{ + Prometheus__WriteRequest request; + Prometheus__MetricMetadata metadata; + Prometheus__TimeSeries gauge_series; + Prometheus__TimeSeries counter_series; + Prometheus__Label gauge_name_label; + Prometheus__Label counter_name_label; + Prometheus__Sample gauge_sample; + Prometheus__Sample counter_sample; + Prometheus__MetricMetadata *metadata_list[1]; + Prometheus__TimeSeries *time_series_list[2]; + Prometheus__Label *gauge_label_list[1]; + Prometheus__Label *counter_label_list[1]; + Prometheus__Sample *gauge_sample_list[1]; + Prometheus__Sample *counter_sample_list[1]; + size_t payload_size; + unsigned char *packed_payload; + cfl_sds_t payload; + + prometheus__write_request__init(&request); + prometheus__metric_metadata__init(&metadata); + prometheus__time_series__init(&gauge_series); + prometheus__time_series__init(&counter_series); + prometheus__label__init(&gauge_name_label); + prometheus__label__init(&counter_name_label); + prometheus__sample__init(&gauge_sample); + prometheus__sample__init(&counter_sample); + + metadata.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__COUNTER; + metadata.metric_family_name = "rw_counter"; + metadata.help = "remote write counter"; + metadata_list[0] = &metadata; + request.n_metadata = 1; + request.metadata = metadata_list; + + gauge_name_label.name = "__name__"; + gauge_name_label.value = "rw_gauge"; + gauge_label_list[0] = &gauge_name_label; + gauge_series.n_labels = 1; + gauge_series.labels = gauge_label_list; + gauge_sample.value = 1.0; + gauge_sample.timestamp = 123; + gauge_sample_list[0] = &gauge_sample; + gauge_series.n_samples = 1; + gauge_series.samples = gauge_sample_list; + + counter_name_label.name = "__name__"; + counter_name_label.value = "rw_counter"; + counter_label_list[0] = &counter_name_label; + counter_series.n_labels = 1; + counter_series.labels = counter_label_list; + counter_sample.value = 2.0; + counter_sample.timestamp = 124; + counter_sample_list[0] = &counter_sample; + counter_series.n_samples = 1; + counter_series.samples = counter_sample_list; + + time_series_list[0] = &gauge_series; + time_series_list[1] = &counter_series; + request.n_timeseries = 2; + request.timeseries = time_series_list; + + payload_size = prometheus__write_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + return NULL; + } + + prometheus__write_request__pack(&request, packed_payload); + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + + return payload; +} + +static cfl_sds_t generate_remote_write_sparse_metadata_histogram_payload() +{ + Prometheus__WriteRequest request; + Prometheus__MetricMetadata metadata; + Prometheus__TimeSeries gauge_series; + Prometheus__TimeSeries histogram_series; + Prometheus__Label gauge_name_label; + Prometheus__Label histogram_name_label; + Prometheus__Sample sample; + Prometheus__Histogram histogram; + Prometheus__BucketSpan span; + Prometheus__MetricMetadata *metadata_list[1]; + Prometheus__TimeSeries *time_series_list[2]; + Prometheus__Label *gauge_label_list[1]; + Prometheus__Label *histogram_label_list[1]; + Prometheus__Sample *sample_list[1]; + Prometheus__Histogram *histogram_list[1]; + Prometheus__BucketSpan *span_list[1]; + double positive_counts[3] = {1.0, 2.0, 3.0}; + size_t payload_size; + unsigned char *packed_payload; + cfl_sds_t payload; + + prometheus__write_request__init(&request); + prometheus__metric_metadata__init(&metadata); + prometheus__time_series__init(&gauge_series); + prometheus__time_series__init(&histogram_series); + prometheus__label__init(&gauge_name_label); + prometheus__label__init(&histogram_name_label); + prometheus__sample__init(&sample); + prometheus__histogram__init(&histogram); + prometheus__bucket_span__init(&span); + + metadata.type = PROMETHEUS__METRIC_METADATA__METRIC_TYPE__GAUGE; + metadata.metric_family_name = "rw_gauge"; + metadata.help = "remote write gauge"; + metadata_list[0] = &metadata; + request.n_metadata = 1; + request.metadata = metadata_list; + + gauge_name_label.name = "__name__"; + gauge_name_label.value = "rw_gauge"; + gauge_label_list[0] = &gauge_name_label; + gauge_series.n_labels = 1; + gauge_series.labels = gauge_label_list; + + sample.value = 1.0; + sample.timestamp = 123; + sample_list[0] = &sample; + gauge_series.n_samples = 1; + gauge_series.samples = sample_list; + + histogram_name_label.name = "__name__"; + histogram_name_label.value = "rw_native_hist"; + histogram_label_list[0] = &histogram_name_label; + histogram_series.n_labels = 1; + histogram_series.labels = histogram_label_list; + + span.offset = 1; + span.length = 3; + span_list[0] = &span; + histogram.n_positive_spans = 1; + histogram.positive_spans = span_list; + histogram.n_positive_counts = 3; + histogram.positive_counts = positive_counts; + histogram.sum = 6.0; + histogram.timestamp = 456; + histogram.count_case = PROMETHEUS__HISTOGRAM__COUNT_COUNT_INT; + histogram.count_int = 6; + + histogram_list[0] = &histogram; + histogram_series.n_histograms = 1; + histogram_series.histograms = histogram_list; + + time_series_list[0] = &gauge_series; + time_series_list[1] = &histogram_series; + request.n_timeseries = 2; + request.timeseries = time_series_list; + + payload_size = prometheus__write_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + return NULL; + } + + prometheus__write_request__pack(&request, packed_payload); + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + + return payload; +} + void test_prometheus_remote_write() { int ret; - struct cmt *decoded_context; + struct cmt *decoded_context = NULL; cfl_sds_t payload = read_file(CMT_TESTS_DATA_PATH "/remote_write_dump_originally_from_node_exporter.bin"); cmt_initialize(); @@ -36,11 +264,166 @@ void test_prometheus_remote_write() ret = cmt_decode_prometheus_remote_write_create(&decoded_context, payload, cfl_sds_len(payload)); TEST_CHECK(ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS); - cmt_decode_prometheus_remote_write_destroy(decoded_context); + if (decoded_context != NULL) { + cmt_decode_prometheus_remote_write_destroy(decoded_context); + decoded_context = NULL; + } cfl_sds_destroy(payload); } +void test_prometheus_remote_write_missing_label_name_rejected() +{ + int ret; + struct cmt *decoded_context = NULL; + cfl_sds_t payload; + + cmt_initialize(); + + payload = generate_remote_write_payload(NULL, "value"); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + ret = cmt_decode_prometheus_remote_write_create(&decoded_context, + payload, + cfl_sds_len(payload)); + TEST_CHECK(ret != CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS); + if (decoded_context != NULL) { + cmt_decode_prometheus_remote_write_destroy(decoded_context); + decoded_context = NULL; + } + cfl_sds_destroy(payload); + } +} + +void test_prometheus_remote_write_missing_label_value_no_crash() +{ + int ret; + struct cmt *decoded_context = NULL; + cfl_sds_t payload; + cfl_sds_t encoded_payload; + + cmt_initialize(); + + payload = generate_remote_write_payload("zone", NULL); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + ret = cmt_decode_prometheus_remote_write_create(&decoded_context, + payload, + cfl_sds_len(payload)); + TEST_CHECK(ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS); + if (ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + encoded_payload = cmt_encode_prometheus_remote_write_create(decoded_context); + TEST_CHECK(encoded_payload != NULL); + if (encoded_payload != NULL) { + cmt_encode_prometheus_remote_write_destroy(encoded_payload); + } + } + if (decoded_context != NULL) { + cmt_decode_prometheus_remote_write_destroy(decoded_context); + decoded_context = NULL; + } + cfl_sds_destroy(payload); + } +} + +void test_prometheus_remote_write_sparse_metadata_histogram() +{ + int ret; + struct cmt_metric *metric; + struct cmt_histogram *histogram; + struct cmt *decoded_context = NULL; + cfl_sds_t payload; + + cmt_initialize(); + + payload = generate_remote_write_sparse_metadata_histogram_payload(); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + ret = cmt_decode_prometheus_remote_write_create(&decoded_context, + payload, + cfl_sds_len(payload)); + TEST_CHECK(ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS); + if (ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + TEST_CHECK(cfl_list_size(&decoded_context->gauges) == 1); + TEST_CHECK(cfl_list_size(&decoded_context->histograms) == 1); + + histogram = cfl_list_entry_first(&decoded_context->histograms, + struct cmt_histogram, _head); + TEST_CHECK(histogram != NULL); + if (histogram != NULL) { + TEST_CHECK(histogram->buckets != NULL); + if (histogram->buckets != NULL) { + TEST_CHECK(histogram->buckets->count == 3); + TEST_CHECK(histogram->buckets->upper_bounds[0] == 1.0); + TEST_CHECK(histogram->buckets->upper_bounds[1] == 2.0); + TEST_CHECK(histogram->buckets->upper_bounds[2] == 3.0); + } + + TEST_CHECK(cfl_list_size(&histogram->map->metrics) == 1); + metric = cfl_list_entry_first(&histogram->map->metrics, + struct cmt_metric, _head); + TEST_CHECK(metric != NULL); + if (metric != NULL) { + TEST_CHECK(metric->hist_buckets != NULL); + if (metric->hist_buckets != NULL) { + TEST_CHECK(cmt_metric_hist_get_value(metric, 0) == 1); + TEST_CHECK(cmt_metric_hist_get_value(metric, 1) == 2); + TEST_CHECK(cmt_metric_hist_get_value(metric, 2) == 3); + } + TEST_CHECK(cmt_metric_hist_get_count_value(metric) == 6); + } + } + } + if (decoded_context != NULL) { + cmt_decode_prometheus_remote_write_destroy(decoded_context); + decoded_context = NULL; + } + cfl_sds_destroy(payload); + } +} + +void test_prometheus_remote_write_metadata_matched_by_name() +{ + int ret; + struct cmt *decoded_context = NULL; + struct cmt_counter *counter; + struct cmt_gauge *gauge; + cfl_sds_t payload; + + cmt_initialize(); + + payload = generate_remote_write_out_of_order_metadata_payload(); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + ret = cmt_decode_prometheus_remote_write_create(&decoded_context, + payload, + cfl_sds_len(payload)); + TEST_CHECK(ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS); + if (ret == CMT_DECODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + TEST_CHECK(cfl_list_size(&decoded_context->gauges) == 1); + TEST_CHECK(cfl_list_size(&decoded_context->counters) == 1); + + gauge = cfl_list_entry_first(&decoded_context->gauges, + struct cmt_gauge, _head); + counter = cfl_list_entry_first(&decoded_context->counters, + struct cmt_counter, _head); + TEST_CHECK(gauge != NULL); + TEST_CHECK(counter != NULL); + if (gauge != NULL) { + TEST_CHECK(strcmp(gauge->opts.name, "rw_gauge") == 0); + } + if (counter != NULL) { + TEST_CHECK(strcmp(counter->opts.name, "rw_counter") == 0); + } + } + if (decoded_context != NULL) { + cmt_decode_prometheus_remote_write_destroy(decoded_context); + decoded_context = NULL; + } + cfl_sds_destroy(payload); + } +} + void test_statsd() { int ret; @@ -74,6 +457,10 @@ void test_statsd() TEST_LIST = { {"prometheus_remote_write", test_prometheus_remote_write}, + {"prometheus_remote_write_missing_label_name_rejected", test_prometheus_remote_write_missing_label_name_rejected}, + {"prometheus_remote_write_missing_label_value_no_crash", test_prometheus_remote_write_missing_label_value_no_crash}, + {"prometheus_remote_write_sparse_metadata_histogram", test_prometheus_remote_write_sparse_metadata_histogram}, + {"prometheus_remote_write_metadata_matched_by_name", test_prometheus_remote_write_metadata_matched_by_name}, {"statsd", test_statsd}, { 0 } }; diff --git a/lib/cmetrics/tests/encoding.c b/lib/cmetrics/tests/encoding.c index 7bec9348d35..36c861b444e 100644 --- a/lib/cmetrics/tests/encoding.c +++ b/lib/cmetrics/tests/encoding.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #include #include +#include +#include + #include "cmt_tests.h" static struct cmt *generate_simple_encoder_test_data() @@ -333,6 +337,336 @@ void test_cmt_to_msgpack_cleanup_on_error() #endif } +enum malformed_msgpack_payload { + MSGPACK_MALFORMED_DUPLICATE_TYPE, + MSGPACK_MALFORMED_INVALID_AGGREGATION_TYPE, + MSGPACK_MALFORMED_INVALID_VALUE_TYPE, + MSGPACK_MALFORMED_MISSING_NAME, + MSGPACK_MALFORMED_MISSING_DESCRIPTION, + MSGPACK_MALFORMED_EXP_HIST_SCALE_OVERFLOW +}; + +static void pack_msgpack_context_meta(mpack_writer_t *writer) +{ + mpack_write_cstr(writer, "meta"); + mpack_start_map(writer, 3); + + mpack_write_cstr(writer, "cmetrics"); + mpack_start_map(writer, 0); + mpack_finish_map(writer); + + mpack_write_cstr(writer, "external"); + mpack_start_map(writer, 0); + mpack_finish_map(writer); + + mpack_write_cstr(writer, "processing"); + mpack_start_map(writer, 1); + mpack_write_cstr(writer, "static_labels"); + mpack_start_array(writer, 0); + mpack_finish_array(writer); + mpack_finish_map(writer); + + mpack_finish_map(writer); +} + +static void pack_msgpack_opts(mpack_writer_t *writer, int include_name) +{ + mpack_start_map(writer, include_name ? 5 : 4); + + mpack_write_cstr(writer, "ns"); + mpack_write_cstr(writer, "test"); + mpack_write_cstr(writer, "ss"); + mpack_write_cstr(writer, "msgpack"); + + if (include_name) { + mpack_write_cstr(writer, "name"); + mpack_write_cstr(writer, "malformed"); + } + + mpack_write_cstr(writer, "desc"); + mpack_write_cstr(writer, "malformed msgpack"); + mpack_write_cstr(writer, "unit"); + mpack_write_cstr(writer, ""); + + mpack_finish_map(writer); +} + +static void pack_msgpack_opts_without_description(mpack_writer_t *writer) +{ + mpack_start_map(writer, 4); + + mpack_write_cstr(writer, "ns"); + mpack_write_cstr(writer, "test"); + mpack_write_cstr(writer, "ss"); + mpack_write_cstr(writer, "msgpack"); + mpack_write_cstr(writer, "name"); + mpack_write_cstr(writer, "malformed"); + mpack_write_cstr(writer, "unit"); + mpack_write_cstr(writer, ""); + + mpack_finish_map(writer); +} + +static void pack_msgpack_basic_meta(mpack_writer_t *writer, + enum malformed_msgpack_payload payload_type) +{ + int include_name; + + include_name = (payload_type != MSGPACK_MALFORMED_MISSING_NAME); + + mpack_write_cstr(writer, "meta"); + + if (payload_type == MSGPACK_MALFORMED_DUPLICATE_TYPE) { + mpack_start_map(writer, 5); + + mpack_write_cstr(writer, "ver"); + mpack_write_uint(writer, MSGPACK_ENCODER_VERSION); + mpack_write_cstr(writer, "type"); + mpack_write_uint(writer, CMT_COUNTER); + mpack_write_cstr(writer, "type"); + mpack_write_uint(writer, CMT_GAUGE); + } + else { + mpack_start_map(writer, 4); + + mpack_write_cstr(writer, "ver"); + mpack_write_uint(writer, MSGPACK_ENCODER_VERSION); + mpack_write_cstr(writer, "type"); + if (payload_type == MSGPACK_MALFORMED_EXP_HIST_SCALE_OVERFLOW) { + mpack_write_uint(writer, CMT_EXP_HISTOGRAM); + } + else { + mpack_write_uint(writer, CMT_COUNTER); + } + } + + mpack_write_cstr(writer, "opts"); + if (payload_type == MSGPACK_MALFORMED_MISSING_DESCRIPTION) { + pack_msgpack_opts_without_description(writer); + } + else { + pack_msgpack_opts(writer, include_name); + } + + mpack_write_cstr(writer, "labels"); + mpack_start_array(writer, 0); + mpack_finish_array(writer); + + if (payload_type == MSGPACK_MALFORMED_INVALID_AGGREGATION_TYPE) { + mpack_write_cstr(writer, "aggregation_type"); + mpack_write_uint(writer, 127); + } + else if (payload_type != MSGPACK_MALFORMED_EXP_HIST_SCALE_OVERFLOW) { + mpack_write_cstr(writer, "aggregation_type"); + mpack_write_uint(writer, CMT_AGGREGATION_TYPE_CUMULATIVE); + } + + mpack_finish_map(writer); +} + +static void pack_msgpack_basic_value(mpack_writer_t *writer, + enum malformed_msgpack_payload payload_type) +{ + if (payload_type == MSGPACK_MALFORMED_EXP_HIST_SCALE_OVERFLOW) { + mpack_start_map(writer, 3); + mpack_write_cstr(writer, "ts"); + mpack_write_uint(writer, 0); + mpack_write_cstr(writer, "exp_histogram"); + mpack_start_map(writer, 10); + mpack_write_cstr(writer, "scale"); + mpack_write_i64(writer, (int64_t) INT_MAX + 1); + mpack_write_cstr(writer, "zero_count"); + mpack_write_uint(writer, 0); + mpack_write_cstr(writer, "zero_threshold"); + mpack_write_double(writer, 0.0); + mpack_write_cstr(writer, "positive_offset"); + mpack_write_int(writer, 0); + mpack_write_cstr(writer, "positive_buckets"); + mpack_start_array(writer, 0); + mpack_finish_array(writer); + mpack_write_cstr(writer, "negative_offset"); + mpack_write_int(writer, 0); + mpack_write_cstr(writer, "negative_buckets"); + mpack_start_array(writer, 0); + mpack_finish_array(writer); + mpack_write_cstr(writer, "count"); + mpack_write_uint(writer, 0); + mpack_write_cstr(writer, "sum_set"); + mpack_write_uint(writer, 0); + mpack_write_cstr(writer, "sum"); + mpack_write_uint(writer, 0); + mpack_finish_map(writer); + mpack_write_cstr(writer, "hash"); + mpack_write_uint(writer, 0); + mpack_finish_map(writer); + + return; + } + + if (payload_type == MSGPACK_MALFORMED_INVALID_VALUE_TYPE) { + mpack_start_map(writer, 5); + } + else { + mpack_start_map(writer, 3); + } + + mpack_write_cstr(writer, "ts"); + mpack_write_uint(writer, 0); + mpack_write_cstr(writer, "value"); + mpack_write_double(writer, 1.0); + + if (payload_type == MSGPACK_MALFORMED_INVALID_VALUE_TYPE) { + mpack_write_cstr(writer, "value_type"); + mpack_write_uint(writer, 127); + mpack_write_cstr(writer, "value_int64"); + mpack_write_i64(writer, 1); + } + + mpack_write_cstr(writer, "hash"); + mpack_write_uint(writer, 0); + mpack_finish_map(writer); +} + +static char *generate_malformed_msgpack_payload(size_t *out_size, + enum malformed_msgpack_payload payload_type) +{ + char *data; + size_t size; + mpack_writer_t writer; + + data = NULL; + size = 0; + + mpack_writer_init_growable(&writer, &data, &size); + + mpack_start_map(&writer, 2); + pack_msgpack_context_meta(&writer); + + mpack_write_cstr(&writer, "metrics"); + mpack_start_array(&writer, 1); + mpack_start_map(&writer, 2); + pack_msgpack_basic_meta(&writer, payload_type); + mpack_write_cstr(&writer, "values"); + mpack_start_array(&writer, 1); + pack_msgpack_basic_value(&writer, payload_type); + mpack_finish_array(&writer); + mpack_finish_map(&writer); + mpack_finish_array(&writer); + mpack_finish_map(&writer); + + if (mpack_writer_destroy(&writer) != mpack_ok) { + return NULL; + } + + *out_size = size; + + return data; +} + +static void assert_malformed_msgpack_rejected(enum malformed_msgpack_payload payload_type) +{ + int ret; + char *payload; + size_t offset; + size_t payload_size; + struct cmt *decoded; + + offset = 0; + payload_size = 0; + decoded = (struct cmt *) 0x1; + + payload = generate_malformed_msgpack_payload(&payload_size, payload_type); + TEST_CHECK(payload != NULL); + if (payload == NULL) { + return; + } + + ret = cmt_decode_msgpack_create(&decoded, payload, payload_size, &offset); + TEST_CHECK(ret != CMT_DECODE_MSGPACK_SUCCESS); + TEST_CHECK(decoded == NULL); + + cmt_encode_msgpack_destroy(payload); +} + +void test_cmt_msgpack_rejects_malformed_fields() +{ + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_DUPLICATE_TYPE); + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_INVALID_AGGREGATION_TYPE); + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_INVALID_VALUE_TYPE); + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_MISSING_NAME); + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_MISSING_DESCRIPTION); + assert_malformed_msgpack_rejected(MSGPACK_MALFORMED_EXP_HIST_SCALE_OVERFLOW); +} + +void test_cmt_msgpack_null_label_roundtrip() +{ + int ret; + char *mp_buf; + size_t mp_size; + size_t offset; + cfl_sds_t result; + struct cmt *cmt1; + struct cmt *cmt2; + struct cmt_counter *counter; + + mp_buf = NULL; + mp_size = 0; + offset = 0; + result = NULL; + cmt2 = NULL; + + cmt1 = cmt_create(); + TEST_CHECK(cmt1 != NULL); + if (cmt1 == NULL) { + return; + } + + counter = cmt_counter_create(cmt1, "test", "msgpack", "labels", + "testing msgpack labels", + 3, (char *[]) {"A", "B", "C"}); + TEST_CHECK(counter != NULL); + if (counter == NULL) { + cmt_destroy(cmt1); + return; + } + + cmt_counter_inc(counter, 0, 3, (char *[]) {NULL, "", NULL}); + cmt_counter_inc(counter, 0, 3, (char *[]) {NULL, "", NULL}); + cmt_counter_inc(counter, 0, 3, (char *[]) {NULL, "b", NULL}); + cmt_counter_inc(counter, 0, 3, (char *[]) {"a", "b", "c"}); + + ret = cmt_encode_msgpack_create(cmt1, &mp_buf, &mp_size); + TEST_CHECK(ret == 0); + if (ret != 0) { + cmt_destroy(cmt1); + return; + } + + ret = cmt_decode_msgpack_create(&cmt2, mp_buf, mp_size, &offset); + TEST_CHECK(ret == 0); + TEST_CHECK(cmt2 != NULL); + if (ret == 0 && cmt2 != NULL) { + result = cmt_encode_prometheus_create(cmt2, CMT_TRUE); + TEST_CHECK(result != NULL); + + if (result != NULL) { + TEST_CHECK(strstr(result, "test_msgpack_labels{B=\"\"} 2 0\n") != NULL); + TEST_CHECK(strstr(result, "test_msgpack_labels{B=\"b\"} 1 0\n") != NULL); + TEST_CHECK(strstr(result, "test_msgpack_labels{A=\"a\",B=\"b\",C=\"c\"} 1 0\n") != NULL); + TEST_CHECK(strstr(result, "A=\"\"") == NULL); + TEST_CHECK(strstr(result, "C=\"\"") == NULL); + } + } + + if (result != NULL) { + cfl_sds_destroy(result); + } + + cmt_destroy(cmt1); + cmt_decode_msgpack_destroy(cmt2); + cmt_encode_msgpack_destroy(mp_buf); +} + /* * perform the following data encoding and compare msgpack buffsers * @@ -771,6 +1105,51 @@ void test_prometheus() cmt_destroy(cmt); } +void test_prometheus_histogram_bucket_decimal_label() +{ + uint64_t ts; + cfl_sds_t text; + struct cmt *cmt; + struct cmt_histogram *h; + struct cmt_histogram_buckets *buckets; + + cmt_initialize(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + if (cmt == NULL) { + return; + } + + buckets = cmt_histogram_buckets_create(1, 1000000.0); + TEST_CHECK(buckets != NULL); + if (buckets == NULL) { + cmt_destroy(cmt); + return; + } + + h = cmt_histogram_create(cmt, "cmt", "labels", "bucket", "Bucket label", + buckets, 0, NULL); + TEST_CHECK(h != NULL); + if (h == NULL) { + cmt_destroy(cmt); + return; + } + + ts = 0; + cmt_histogram_observe(h, ts, 42.0, 0, NULL); + + text = cmt_encode_prometheus_create(cmt, CMT_TRUE); + TEST_CHECK(text != NULL); + if (text != NULL) { + TEST_CHECK(strstr(text, "cmt_labels_bucket_bucket{le=\"1000000.0\"}") != NULL); + TEST_CHECK(strstr(text, "cmt_labels_bucket_bucket{le=\"1e+06\"}") == NULL); + cmt_encode_prometheus_destroy(text); + } + + cmt_destroy(cmt); +} + void test_text() { uint64_t ts; @@ -1231,6 +1610,8 @@ void test_splunk_hec_summary() TEST_LIST = { {"cmt_msgpack_cleanup_on_error", test_cmt_to_msgpack_cleanup_on_error}, + {"cmt_msgpack_rejects_malformed_fields", test_cmt_msgpack_rejects_malformed_fields}, + {"cmt_msgpack_null_label_roundtrip", test_cmt_msgpack_null_label_roundtrip}, {"cmt_msgpack_partial_processing", test_cmt_msgpack_partial_processing}, {"prometheus_remote_write", test_prometheus_remote_write}, {"prometheus_remote_write_old_cmt",test_prometheus_remote_write_with_outdated_timestamps}, @@ -1242,6 +1623,7 @@ TEST_LIST = { {"opentelemetry", test_opentelemetry}, {"cloudwatch_emf", test_cloudwatch_emf}, {"prometheus", test_prometheus}, + {"prometheus_histogram_bucket_decimal_label", test_prometheus_histogram_bucket_decimal_label}, {"text", test_text}, {"influx", test_influx}, {"influx_without_namespaces", test_influx_without_namespaces}, diff --git a/lib/cmetrics/tests/histogram.c b/lib/cmetrics/tests/histogram.c index d5fc80bbbad..bf3662bc589 100644 --- a/lib/cmetrics/tests/histogram.c +++ b/lib/cmetrics/tests/histogram.c @@ -205,8 +205,60 @@ void test_set_defaults() cmt_destroy(cmt); } +void test_prometheus_large_integer_bucket_precision() +{ + int ret; + uint64_t ts; + cfl_sds_t buf; + struct cmt *cmt; + struct cmt_histogram *h; + struct cmt_histogram_buckets *buckets; + uint64_t bucket_values[9] = {0}; + double bucket_bounds[8] = { + 100.0, 1024.0, 2048.0, 4096.0, + 100.0 * 1024.0, 1024.0 * 1024.0, + 4.0 * 1024.0 * 1024.0, 10.0 * 1024.0 * 1024.0 + }; + + cmt_initialize(); + + ts = cfl_time_now(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + buckets = cmt_histogram_buckets_create_size(bucket_bounds, + sizeof(bucket_bounds) / sizeof(double)); + TEST_CHECK(buckets != NULL); + + h = cmt_histogram_create(cmt, + "k8s", "network", "record_sizes", "Record sizes", + buckets, + 0, NULL); + TEST_CHECK(h != NULL); + + ret = cmt_histogram_set_default(h, ts, + bucket_values, + 0.0, 0, 0, NULL); + TEST_CHECK(ret == 0); + + buf = cmt_encode_prometheus_create(cmt, CMT_FALSE); + TEST_CHECK(buf != NULL); + + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"1048576.0\"}") != NULL); + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"4194304.0\"}") != NULL); + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"10485760.0\"}") != NULL); + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"1.04858e+06\"}") == NULL); + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"4.1943e+06\"}") == NULL); + TEST_CHECK(strstr(buf, "k8s_network_record_sizes_bucket{le=\"1.04858e+07\"}") == NULL); + + cmt_encode_prometheus_destroy(buf); + cmt_destroy(cmt); +} + TEST_LIST = { - {"histogram" , test_histogram}, - {"set_defaults", test_set_defaults}, + {"histogram" , test_histogram}, + {"set_defaults" , test_set_defaults}, + {"prometheus_large_integer_bucket_precision", test_prometheus_large_integer_bucket_precision}, { 0 } }; diff --git a/lib/cmetrics/tests/null_label.c b/lib/cmetrics/tests/null_label.c index 2d23bc13192..e8151247e90 100644 --- a/lib/cmetrics/tests/null_label.c +++ b/lib/cmetrics/tests/null_label.c @@ -19,7 +19,14 @@ #include #include +#include +#include +#include #include +#include +#include + +#include #include "cmt_tests.h" @@ -85,6 +92,9 @@ void test_encoding() cfl_sds_t result; struct cmt *cmt; struct cmt_counter *c; + struct cmt_metric *metric; + struct cmt_map_label *label; + uint64_t ts; cmt = cmt_create(); c = cmt_counter_create(cmt, "test", "dummy", "labels", "testing labels", @@ -121,6 +131,12 @@ void test_encoding() cfl_sds_destroy(result); + cmt_counter_inc(c, 0, 6, (char *[]) {NULL,"",NULL,NULL,NULL,NULL}); + result = cmt_encode_prometheus_create(cmt, CMT_TRUE); + TEST_CHECK(strstr(result, "test_dummy_labels{B=\"\"} 1 0\n") != NULL); + cfl_sds_destroy(result); + + cmt_counter_set(c, 0, 5, 6, (char *[]) {NULL,NULL,NULL,"d",NULL,NULL}); result = cmt_encode_prometheus_create(cmt, CMT_TRUE); TEST_CHECK(strcmp(result, @@ -128,6 +144,7 @@ void test_encoding() "# TYPE test_dummy_labels counter\n" "test_dummy_labels 2 0\n" "test_dummy_labels{B=\"b\"} 2 0\n" + "test_dummy_labels{B=\"\"} 1 0\n" "test_dummy_labels{D=\"d\"} 5 0\n" ) == 0); cfl_sds_destroy(result); @@ -139,6 +156,7 @@ void test_encoding() "# TYPE test_dummy_labels counter\n" "test_dummy_labels 2 0\n" "test_dummy_labels{B=\"b\"} 2 0\n" + "test_dummy_labels{B=\"\"} 1 0\n" "test_dummy_labels{D=\"d\"} 5 0\n" "test_dummy_labels{B=\"b\",D=\"d\",F=\"f\"} 50 0\n" ) == 0); @@ -151,12 +169,170 @@ void test_encoding() "# TYPE test_dummy_labels counter\n" "test_dummy_labels 2 0\n" "test_dummy_labels{B=\"b\"} 2 0\n" + "test_dummy_labels{B=\"\"} 1 0\n" "test_dummy_labels{D=\"d\"} 5 0\n" "test_dummy_labels{B=\"b\",D=\"d\",F=\"f\"} 50 0\n" "test_dummy_labels{A=\"a\",B=\"b\",C=\"c\",D=\"d\",E=\"e\",F=\"f\"} 1 0\n" ) == 0); cfl_sds_destroy(result); + result = cmt_encode_influx_create(cmt); + TEST_CHECK(result != NULL); + if (result != NULL) { + TEST_CHECK(strstr(result, ", ") == NULL); + TEST_CHECK(strstr(result, ",,") == NULL); + TEST_CHECK(strstr(result, "B=b") != NULL); + cmt_encode_influx_destroy(result); + } + + result = cmt_encode_splunk_hec_create(cmt, "localhost", "main", NULL, NULL); + TEST_CHECK(result != NULL); + if (result != NULL) { + TEST_CHECK(strstr(result, ",,") == NULL); + TEST_CHECK(strstr(result, ",}") == NULL); + TEST_CHECK(strstr(result, "\"B\":\"b\"") != NULL); + cmt_encode_splunk_hec_destroy(result); + } + + cmt_destroy(cmt); + + cmt = cmt_create(); + c = cmt_counter_create(cmt, "test", "influx", "labels", "testing influx labels", + 1, (char *[]) {"A"}); + + cmt_counter_inc(c, 0, 1, (char *[]) {"a"}); + metric = cfl_list_entry_first(&c->map->metrics, struct cmt_metric, _head); + TEST_CHECK(metric != NULL); + if (metric != NULL) { + label = calloc(1, sizeof(struct cmt_map_label)); + TEST_CHECK(label != NULL); + if (label != NULL) { + label->name = cfl_sds_create("extra"); + TEST_CHECK(label->name != NULL); + if (label->name != NULL) { + cfl_list_add(&label->_head, &metric->labels); + } + else { + free(label); + } + } + } + + result = cmt_encode_influx_create(cmt); + TEST_CHECK(result != NULL); + if (result != NULL) { + TEST_CHECK(cfl_sds_len(result) == 0); + cmt_encode_influx_destroy(result); + } + + cmt_destroy(cmt); + + cmt = cmt_create(); + c = cmt_counter_create(cmt, "test", "remote", "labels", "testing remote-write labels", + 3, (char *[]) {"A", "B", "C"}); + ts = cfl_time_now(); + + cmt_counter_inc(c, ts, 3, (char *[]) {NULL, NULL, NULL}); + cmt_counter_inc(c, ts, 3, (char *[]) {NULL, "", NULL}); + cmt_counter_inc(c, ts, 3, (char *[]) {NULL, "b", NULL}); + cmt_counter_inc(c, ts, 3, (char *[]) {"a", "b", "c"}); + + result = cmt_encode_prometheus_remote_write_create(cmt); + TEST_CHECK(result != NULL); + if (result != NULL) { + Prometheus__WriteRequest *request; + size_t series_index; + size_t label_index; + size_t label_a_count; + size_t label_b_count; + size_t label_c_count; + size_t label_d_count; + size_t label_e_count; + size_t label_f_count; + + request = prometheus__write_request__unpack(NULL, + cfl_sds_len(result), + (uint8_t *) result); + TEST_CHECK(request != NULL); + if (request != NULL) { + label_a_count = 0; + label_b_count = 0; + label_c_count = 0; + label_d_count = 0; + label_e_count = 0; + label_f_count = 0; + + for (series_index = 0; series_index < request->n_timeseries; series_index++) { + for (label_index = 0; + label_index < request->timeseries[series_index]->n_labels; + label_index++) { + if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "A") == 0) { + label_a_count++; + } + else if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "B") == 0) { + label_b_count++; + } + else if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "C") == 0) { + label_c_count++; + } + else if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "D") == 0) { + label_d_count++; + } + else if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "E") == 0) { + label_e_count++; + } + else if (strcmp(request->timeseries[series_index]->labels[label_index]->name, "F") == 0) { + label_f_count++; + } + } + } + + TEST_CHECK(label_a_count == 1); + TEST_CHECK(label_b_count == 3); + TEST_CHECK(label_c_count == 1); + TEST_CHECK(label_d_count == 0); + TEST_CHECK(label_e_count == 0); + TEST_CHECK(label_f_count == 0); + prometheus__write_request__free_unpacked(request, NULL); + } + cmt_encode_prometheus_remote_write_destroy(result); + } + + cmt_destroy(cmt); + + cmt = cmt_create(); + c = cmt_counter_create(cmt, "test", "remote_nil", "labels", + "testing remote-write nil labels", + 1, (char *[]) {"A"}); + + cmt_counter_inc(c, ts, 1, (char *[]) {NULL}); + label = cfl_list_entry_first(&c->map->label_keys, struct cmt_map_label, _head); + TEST_CHECK(label != NULL); + if (label != NULL) { + cfl_sds_destroy(label->name); + label->name = NULL; + } + + result = cmt_encode_prometheus_remote_write_create(cmt); + TEST_CHECK(result != NULL); + if (result != NULL) { + Prometheus__WriteRequest *request; + + request = prometheus__write_request__unpack(NULL, + cfl_sds_len(result), + (uint8_t *) result); + TEST_CHECK(request != NULL); + if (request != NULL) { + TEST_CHECK(request->n_timeseries == 1); + if (request->n_timeseries == 1) { + TEST_CHECK(request->timeseries[0]->n_labels == 1); + TEST_CHECK(strcmp(request->timeseries[0]->labels[0]->name, "__name__") == 0); + } + prometheus__write_request__free_unpacked(request, NULL); + } + cmt_encode_prometheus_remote_write_destroy(result); + } + cmt_destroy(cmt); } diff --git a/lib/cmetrics/tests/opentelemetry.c b/lib/cmetrics/tests/opentelemetry.c index 479728e26fa..c24819f0377 100644 --- a/lib/cmetrics/tests/opentelemetry.c +++ b/lib/cmetrics/tests/opentelemetry.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -489,6 +490,264 @@ static cfl_sds_t generate_gauge_int_otlp_payload_with_unit() return payload; } +static cfl_sds_t generate_gauge_int_otlp_payload_with_attribute(char *attribute_key, + int include_value) +{ + static uint8_t zero_length_bytes_value[1] = {'x'}; + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest request; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics resource_metrics; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics scope_metrics; + Opentelemetry__Proto__Metrics__V1__Metric metric; + Opentelemetry__Proto__Metrics__V1__Gauge gauge; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint data_point; + Opentelemetry__Proto__Common__V1__KeyValue attribute; + Opentelemetry__Proto__Common__V1__AnyValue attribute_value; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics *scope_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__Metric *metric_list[1]; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point_list[1]; + Opentelemetry__Proto__Common__V1__KeyValue *attribute_list[1]; + size_t payload_size; + unsigned char *packed_payload; + cfl_sds_t payload; + + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__init(&request); + opentelemetry__proto__metrics__v1__resource_metrics__init(&resource_metrics); + opentelemetry__proto__metrics__v1__scope_metrics__init(&scope_metrics); + opentelemetry__proto__metrics__v1__metric__init(&metric); + opentelemetry__proto__metrics__v1__gauge__init(&gauge); + opentelemetry__proto__metrics__v1__number_data_point__init(&data_point); + opentelemetry__proto__common__v1__key_value__init(&attribute); + opentelemetry__proto__common__v1__any_value__init(&attribute_value); + + if (include_value == 2) { + attribute_value.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE; + attribute_value.bytes_value.data = zero_length_bytes_value; + attribute_value.bytes_value.len = 0; + attribute.value = &attribute_value; + } + else if (include_value) { + attribute_value.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE; + attribute_value.string_value = "value"; + attribute.value = &attribute_value; + } + + attribute.key = attribute_key; + attribute_list[0] = &attribute; + + data_point.time_unix_nano = 123; + data_point.value_case = + OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__VALUE_AS_INT; + data_point.as_int = 1; + data_point.n_attributes = 1; + data_point.attributes = attribute_list; + + data_point_list[0] = &data_point; + gauge.n_data_points = 1; + gauge.data_points = data_point_list; + + metric.name = "g_attr"; + metric.data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE; + metric.gauge = &gauge; + + metric_list[0] = &metric; + scope_metrics.n_metrics = 1; + scope_metrics.metrics = metric_list; + + scope_metrics_list[0] = &scope_metrics; + resource_metrics.n_scope_metrics = 1; + resource_metrics.scope_metrics = scope_metrics_list; + + resource_metrics_list[0] = &resource_metrics; + request.n_resource_metrics = 1; + request.resource_metrics = resource_metrics_list; + + payload_size = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + return NULL; + } + + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__pack(&request, + packed_payload); + + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + + return payload; +} + +static cfl_sds_t generate_gauge_int_otlp_payload_with_many_attributes(size_t attribute_count) +{ + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest request; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics resource_metrics; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics scope_metrics; + Opentelemetry__Proto__Metrics__V1__Metric metric; + Opentelemetry__Proto__Metrics__V1__Gauge gauge; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint data_point; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics *scope_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__Metric *metric_list[1]; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint *data_point_list[1]; + Opentelemetry__Proto__Common__V1__KeyValue *attribute_list; + Opentelemetry__Proto__Common__V1__KeyValue **attribute_ptr_list; + Opentelemetry__Proto__Common__V1__AnyValue *attribute_value_list; + char (*attribute_key_list)[32]; + char (*attribute_value_text_list)[32]; + size_t payload_size; + size_t index; + unsigned char *packed_payload; + cfl_sds_t payload; + + attribute_list = calloc(attribute_count, sizeof(Opentelemetry__Proto__Common__V1__KeyValue)); + attribute_ptr_list = calloc(attribute_count, sizeof(Opentelemetry__Proto__Common__V1__KeyValue *)); + attribute_value_list = calloc(attribute_count, sizeof(Opentelemetry__Proto__Common__V1__AnyValue)); + attribute_key_list = calloc(attribute_count, sizeof(*attribute_key_list)); + attribute_value_text_list = calloc(attribute_count, sizeof(*attribute_value_text_list)); + + if (attribute_list == NULL || attribute_ptr_list == NULL || + attribute_value_list == NULL || attribute_key_list == NULL || + attribute_value_text_list == NULL) { + free(attribute_list); + free(attribute_ptr_list); + free(attribute_value_list); + free(attribute_key_list); + free(attribute_value_text_list); + + return NULL; + } + + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__init(&request); + opentelemetry__proto__metrics__v1__resource_metrics__init(&resource_metrics); + opentelemetry__proto__metrics__v1__scope_metrics__init(&scope_metrics); + opentelemetry__proto__metrics__v1__metric__init(&metric); + opentelemetry__proto__metrics__v1__gauge__init(&gauge); + opentelemetry__proto__metrics__v1__number_data_point__init(&data_point); + + for (index = 0; index < attribute_count; index++) { + opentelemetry__proto__common__v1__key_value__init(&attribute_list[index]); + opentelemetry__proto__common__v1__any_value__init(&attribute_value_list[index]); + + snprintf(attribute_key_list[index], sizeof(attribute_key_list[index]), + "attr_%03zu", index); + snprintf(attribute_value_text_list[index], sizeof(attribute_value_text_list[index]), + "value_%03zu", index); + + attribute_value_list[index].value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE; + attribute_value_list[index].string_value = attribute_value_text_list[index]; + attribute_list[index].key = attribute_key_list[index]; + attribute_list[index].value = &attribute_value_list[index]; + attribute_ptr_list[index] = &attribute_list[index]; + } + + data_point.time_unix_nano = 123; + data_point.value_case = + OPENTELEMETRY__PROTO__METRICS__V1__NUMBER_DATA_POINT__VALUE_AS_INT; + data_point.as_int = 1; + data_point.n_attributes = attribute_count; + data_point.attributes = attribute_ptr_list; + + data_point_list[0] = &data_point; + gauge.n_data_points = 1; + gauge.data_points = data_point_list; + + metric.name = "g_many_attrs"; + metric.data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE; + metric.gauge = &gauge; + + metric_list[0] = &metric; + scope_metrics.n_metrics = 1; + scope_metrics.metrics = metric_list; + + scope_metrics_list[0] = &scope_metrics; + resource_metrics.n_scope_metrics = 1; + resource_metrics.scope_metrics = scope_metrics_list; + + resource_metrics_list[0] = &resource_metrics; + request.n_resource_metrics = 1; + request.resource_metrics = resource_metrics_list; + + payload_size = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + payload = NULL; + } + else { + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__pack(&request, + packed_payload); + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + } + + free(attribute_list); + free(attribute_ptr_list); + free(attribute_value_list); + free(attribute_key_list); + free(attribute_value_text_list); + + return payload; +} + +static cfl_sds_t generate_invalid_otlp_metric_payload(int include_name, + int include_data) +{ + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest request; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics resource_metrics; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics scope_metrics; + Opentelemetry__Proto__Metrics__V1__Metric metric; + Opentelemetry__Proto__Metrics__V1__Gauge gauge; + Opentelemetry__Proto__Metrics__V1__ResourceMetrics *resource_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__ScopeMetrics *scope_metrics_list[1]; + Opentelemetry__Proto__Metrics__V1__Metric *metric_list[1]; + size_t payload_size; + unsigned char *packed_payload; + cfl_sds_t payload; + + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__init(&request); + opentelemetry__proto__metrics__v1__resource_metrics__init(&resource_metrics); + opentelemetry__proto__metrics__v1__scope_metrics__init(&scope_metrics); + opentelemetry__proto__metrics__v1__metric__init(&metric); + opentelemetry__proto__metrics__v1__gauge__init(&gauge); + + if (include_name) { + metric.name = "invalid_metric"; + } + + if (include_data) { + metric.data_case = OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE; + metric.gauge = &gauge; + } + + metric_list[0] = &metric; + scope_metrics.n_metrics = 1; + scope_metrics.metrics = metric_list; + + scope_metrics_list[0] = &scope_metrics; + resource_metrics.n_scope_metrics = 1; + resource_metrics.scope_metrics = scope_metrics_list; + + resource_metrics_list[0] = &resource_metrics; + request.n_resource_metrics = 1; + request.resource_metrics = resource_metrics_list; + + payload_size = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__get_packed_size(&request); + packed_payload = calloc(1, payload_size); + if (packed_payload == NULL) { + return NULL; + } + + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__pack(&request, + packed_payload); + + payload = cfl_sds_create_len((char *) packed_payload, payload_size); + free(packed_payload); + + return payload; +} + static cfl_sds_t generate_sum_non_monotonic_int_otlp_payload() { Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest request; @@ -1279,6 +1538,365 @@ void test_opentelemetry_large_int_roundtrip_with_msgpack() cfl_sds_destroy(payload); } +/* Regression: decoding a histogram with an unrecognised AnyValue type must not crash. + * + * An attribute with AnyValue.value_case = NOT_SET (or any unrecognised value) caused + * decode_data_point_labels to pass NULL to append_new_metric_label_value, leaving + * label->name as NULL; compute_metric_hash then called cfl_sds_len(NULL) and segfaulted. + * + * otlp_null_label_histogram.bin: single-resource ExportMetricsServiceRequest with one + * Histogram data point whose sole attribute has value_case = NOT_SET. + */ +static void test_opentelemetry_histogram_null_label_no_crash(void) +{ + struct cfl_list result_list; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = read_file(CMT_TESTS_DATA_PATH "/otlp_null_label_histogram.bin"); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret == CMT_DECODE_OPENTELEMETRY_SUCCESS); + + if (ret == CMT_DECODE_OPENTELEMETRY_SUCCESS) { + cmt_decode_opentelemetry_destroy(&result_list); + } + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_missing_attribute_key_rejected(void) +{ + struct cfl_list result_list; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_gauge_int_otlp_payload_with_attribute(NULL, CMT_TRUE); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret != CMT_DECODE_OPENTELEMETRY_SUCCESS); + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_missing_attribute_value_no_crash(void) +{ + struct cfl_list result_list; + struct cmt *decoded_context; + struct cmt_gauge *gauge; + struct cmt_metric *metric; + struct cmt_map_label *label_value; + cfl_sds_t encoded_payload; + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *service_request; + Opentelemetry__Proto__Metrics__V1__Metric *roundtrip_metric; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint *roundtrip_dp; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_gauge_int_otlp_payload_with_attribute("missing_value", CMT_FALSE); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret == CMT_DECODE_OPENTELEMETRY_SUCCESS); + if (ret == CMT_DECODE_OPENTELEMETRY_SUCCESS) { + decoded_context = cfl_list_entry_first(&result_list, struct cmt, _head); + TEST_CHECK(decoded_context != NULL); + if (decoded_context != NULL) { + gauge = cfl_list_entry_first(&decoded_context->gauges, + struct cmt_gauge, _head); + TEST_CHECK(gauge != NULL); + if (gauge != NULL) { + metric = cfl_list_entry_first(&gauge->map->metrics, + struct cmt_metric, _head); + TEST_CHECK(metric != NULL); + if (metric != NULL) { + label_value = cfl_list_entry_first(&metric->labels, + struct cmt_map_label, _head); + TEST_CHECK(label_value != NULL); + if (label_value != NULL) { + TEST_CHECK(label_value->name == NULL); + } + } + } + + encoded_payload = cmt_encode_opentelemetry_create(decoded_context); + TEST_CHECK(encoded_payload != NULL); + if (encoded_payload != NULL) { + service_request = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__unpack( + NULL, cfl_sds_len(encoded_payload), (uint8_t *) encoded_payload); + TEST_CHECK(service_request != NULL); + + if (service_request != NULL && + service_request->n_resource_metrics == 1 && + service_request->resource_metrics[0]->n_scope_metrics == 1 && + service_request->resource_metrics[0]->scope_metrics[0]->n_metrics == 1) { + roundtrip_metric = service_request->resource_metrics[0]->scope_metrics[0]->metrics[0]; + TEST_CHECK(roundtrip_metric->data_case == + OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE); + if (roundtrip_metric->data_case == + OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) { + TEST_CHECK(roundtrip_metric->gauge->n_data_points == 1); + roundtrip_dp = roundtrip_metric->gauge->data_points[0]; + TEST_CHECK(roundtrip_dp->n_attributes == 0); + } + } + + if (service_request != NULL) { + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__free_unpacked(service_request, NULL); + } + cmt_encode_opentelemetry_destroy(encoded_payload); + } + } + cmt_decode_opentelemetry_destroy(&result_list); + } + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_zero_length_bytes_attribute(void) +{ + struct cfl_list result_list; + struct cmt *decoded_context; + struct cmt_gauge *gauge; + struct cmt_metric *metric; + struct cmt_map_label *label_value; + cfl_sds_t encoded_payload; + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *service_request; + Opentelemetry__Proto__Metrics__V1__Metric *roundtrip_metric; + Opentelemetry__Proto__Metrics__V1__NumberDataPoint *roundtrip_dp; + Opentelemetry__Proto__Common__V1__KeyValue *attribute; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_gauge_int_otlp_payload_with_attribute("empty_bytes", 2); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret == CMT_DECODE_OPENTELEMETRY_SUCCESS); + if (ret == CMT_DECODE_OPENTELEMETRY_SUCCESS) { + decoded_context = cfl_list_entry_first(&result_list, struct cmt, _head); + TEST_CHECK(decoded_context != NULL); + if (decoded_context != NULL) { + TEST_CHECK(cfl_list_size(&decoded_context->gauges) == 1); + gauge = cfl_list_entry_first(&decoded_context->gauges, + struct cmt_gauge, _head); + TEST_CHECK(gauge != NULL); + if (gauge != NULL) { + TEST_CHECK(cfl_list_size(&gauge->map->metrics) == 1); + metric = cfl_list_entry_first(&gauge->map->metrics, + struct cmt_metric, _head); + TEST_CHECK(metric != NULL); + if (metric != NULL) { + label_value = cfl_list_entry_first(&metric->labels, + struct cmt_map_label, _head); + TEST_CHECK(label_value != NULL); + if (label_value != NULL) { + TEST_CHECK(label_value->name != NULL); + if (label_value->name != NULL) { + TEST_CHECK(cfl_sds_len(label_value->name) == 0); + } + } + } + } + + encoded_payload = cmt_encode_opentelemetry_create(decoded_context); + TEST_CHECK(encoded_payload != NULL); + if (encoded_payload != NULL) { + service_request = opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__unpack( + NULL, cfl_sds_len(encoded_payload), (uint8_t *) encoded_payload); + TEST_CHECK(service_request != NULL); + + if (service_request != NULL && + service_request->n_resource_metrics == 1 && + service_request->resource_metrics[0]->n_scope_metrics == 1 && + service_request->resource_metrics[0]->scope_metrics[0]->n_metrics == 1) { + roundtrip_metric = service_request->resource_metrics[0]->scope_metrics[0]->metrics[0]; + TEST_CHECK(roundtrip_metric->data_case == + OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE); + if (roundtrip_metric->data_case == + OPENTELEMETRY__PROTO__METRICS__V1__METRIC__DATA_GAUGE) { + TEST_CHECK(roundtrip_metric->gauge->n_data_points == 1); + roundtrip_dp = roundtrip_metric->gauge->data_points[0]; + TEST_CHECK(roundtrip_dp->n_attributes == 1); + if (roundtrip_dp->n_attributes == 1) { + attribute = roundtrip_dp->attributes[0]; + TEST_CHECK(strcmp(attribute->key, "empty_bytes") == 0); + TEST_CHECK(attribute->value->value_case == + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE); + TEST_CHECK(attribute->value->string_value != NULL); + if (attribute->value->string_value != NULL) { + TEST_CHECK(attribute->value->string_value[0] == '\0'); + } + } + } + } + + if (service_request != NULL) { + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__free_unpacked(service_request, NULL); + } + cmt_encode_opentelemetry_destroy(encoded_payload); + } + } + cmt_decode_opentelemetry_destroy(&result_list); + } + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_many_attributes(void) +{ + struct cfl_list result_list; + struct cmt *decoded_context; + struct cmt_gauge *gauge; + struct cmt_metric *metric; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_gauge_int_otlp_payload_with_many_attributes(130); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret == CMT_DECODE_OPENTELEMETRY_SUCCESS); + if (ret == CMT_DECODE_OPENTELEMETRY_SUCCESS) { + decoded_context = cfl_list_entry_first(&result_list, struct cmt, _head); + TEST_CHECK(decoded_context != NULL); + if (decoded_context != NULL) { + gauge = cfl_list_entry_first(&decoded_context->gauges, + struct cmt_gauge, _head); + TEST_CHECK(gauge != NULL); + if (gauge != NULL) { + TEST_CHECK(gauge->map->label_count == 130); + metric = cfl_list_entry_first(&gauge->map->metrics, + struct cmt_metric, _head); + TEST_CHECK(metric != NULL); + if (metric != NULL) { + TEST_CHECK(cfl_list_size(&metric->labels) == 130); + } + } + } + cmt_decode_opentelemetry_destroy(&result_list); + } + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_missing_metric_name_rejected(void) +{ + struct cfl_list result_list; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_invalid_otlp_metric_payload(CMT_FALSE, CMT_TRUE); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret != CMT_DECODE_OPENTELEMETRY_SUCCESS); + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_missing_metric_data_rejected(void) +{ + struct cfl_list result_list; + cfl_sds_t payload; + size_t offset; + int ret; + + payload = generate_invalid_otlp_metric_payload(CMT_TRUE, CMT_FALSE); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + offset = 0; + ret = cmt_decode_opentelemetry_create(&result_list, + payload, cfl_sds_len(payload), + &offset); + TEST_CHECK(ret != CMT_DECODE_OPENTELEMETRY_SUCCESS); + cfl_sds_destroy(payload); + } +} + +static void test_opentelemetry_omitted_null_key_label_encoded(void) +{ + int ret; + cfl_sds_t payload; + struct cmt *cmt; + struct cmt_counter *counter; + struct cmt_map_label *label_key; + Opentelemetry__Proto__Collector__Metrics__V1__ExportMetricsServiceRequest *request; + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + if (cmt == NULL) { + return; + } + + counter = cmt_counter_create(cmt, "test", "otlp", "labels", + "testing otlp labels", + 1, (char *[]) {"A"}); + TEST_CHECK(counter != NULL); + if (counter == NULL) { + cmt_destroy(cmt); + return; + } + + ret = cmt_counter_inc(counter, 0, 1, (char *[]) {NULL}); + TEST_CHECK(ret == 0); + + label_key = cfl_list_entry_first(&counter->map->label_keys, + struct cmt_map_label, _head); + TEST_CHECK(label_key != NULL); + if (label_key != NULL) { + cfl_sds_destroy(label_key->name); + label_key->name = NULL; + } + + payload = cmt_encode_opentelemetry_create(cmt); + TEST_CHECK(payload != NULL); + if (payload != NULL) { + request = + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__unpack(NULL, + cfl_sds_len(payload), + (uint8_t *) payload); + TEST_CHECK(request != NULL); + if (request != NULL) { + TEST_CHECK(request->n_resource_metrics == 1); + TEST_CHECK(request->resource_metrics[0]->n_scope_metrics == 1); + TEST_CHECK(request->resource_metrics[0]->scope_metrics[0]->n_metrics == 1); + TEST_CHECK(request->resource_metrics[0]->scope_metrics[0]->metrics[0]->sum->n_data_points == 1); + TEST_CHECK(request->resource_metrics[0]->scope_metrics[0]->metrics[0]->sum->data_points[0]->n_attributes == 0); + opentelemetry__proto__collector__metrics__v1__export_metrics_service_request__free_unpacked(request, NULL); + } + cmt_encode_opentelemetry_destroy(payload); + } + + cmt_destroy(cmt); +} + TEST_LIST = { {"opentelemetry_api_full_roundtrip_with_msgpack", test_opentelemetry_api_full_roundtrip_with_msgpack}, {"opentelemetry_encode_multi_resource_scope_containers", test_opentelemetry_encode_multi_resource_scope_containers}, @@ -1286,5 +1904,13 @@ TEST_LIST = { {"opentelemetry_gauge_int_and_unit_decode", test_opentelemetry_gauge_int_and_unit_decode}, {"opentelemetry_sum_non_monotonic_int_roundtrip", test_opentelemetry_sum_non_monotonic_int_roundtrip}, {"opentelemetry_large_int_roundtrip_with_msgpack", test_opentelemetry_large_int_roundtrip_with_msgpack}, + {"opentelemetry_histogram_null_label_no_crash", test_opentelemetry_histogram_null_label_no_crash}, + {"opentelemetry_missing_attribute_key_rejected", test_opentelemetry_missing_attribute_key_rejected}, + {"opentelemetry_missing_attribute_value_no_crash", test_opentelemetry_missing_attribute_value_no_crash}, + {"opentelemetry_zero_length_bytes_attribute", test_opentelemetry_zero_length_bytes_attribute}, + {"opentelemetry_many_attributes", test_opentelemetry_many_attributes}, + {"opentelemetry_missing_metric_name_rejected", test_opentelemetry_missing_metric_name_rejected}, + {"opentelemetry_missing_metric_data_rejected", test_opentelemetry_missing_metric_data_rejected}, + {"opentelemetry_omitted_null_key_label_encoded", test_opentelemetry_omitted_null_key_label_encoded}, { 0 } }; diff --git a/lib/cmetrics/tests/prometheus_parser.c b/lib/cmetrics/tests/prometheus_parser.c index f760a394c3a..28643fe8418 100644 --- a/lib/cmetrics/tests/prometheus_parser.c +++ b/lib/cmetrics/tests/prometheus_parser.c @@ -1084,8 +1084,8 @@ void test_issue_fluent_bit_6021() "envoy_http_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" - "envoy_http_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" - "envoy_http_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" + "envoy_http_downstream_cx_length_ms_bucket{le=\"1800000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" + "envoy_http_downstream_cx_length_ms_bucket{le=\"3600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" "envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix=\"admin\"} 15.5 0\n" "envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix=\"admin\"} 1 0\n" @@ -1106,8 +1106,8 @@ void test_issue_fluent_bit_6021() "envoy_http_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" - "envoy_http_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" - "envoy_http_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" + "envoy_http_downstream_cx_length_ms_bucket{le=\"1800000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" + "envoy_http_downstream_cx_length_ms_bucket{le=\"3600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_cx_length_ms_sum{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_cx_length_ms_count{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" @@ -1130,8 +1130,8 @@ void test_issue_fluent_bit_6021() "envoy_http_downstream_rq_time_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" "envoy_http_downstream_rq_time_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" "envoy_http_downstream_rq_time_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" - "envoy_http_downstream_rq_time_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" - "envoy_http_downstream_rq_time_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" + "envoy_http_downstream_rq_time_bucket{le=\"1800000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" + "envoy_http_downstream_rq_time_bucket{le=\"3600000.0\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" "envoy_http_downstream_rq_time_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" "envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix=\"admin\"} 25.5 0\n" "envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix=\"admin\"} 10 0\n" @@ -1152,8 +1152,8 @@ void test_issue_fluent_bit_6021() "envoy_http_downstream_rq_time_bucket{le=\"60000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_rq_time_bucket{le=\"300000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_rq_time_bucket{le=\"600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" - "envoy_http_downstream_rq_time_bucket{le=\"1.8e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" - "envoy_http_downstream_rq_time_bucket{le=\"3.6e+06\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" + "envoy_http_downstream_rq_time_bucket{le=\"1800000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" + "envoy_http_downstream_rq_time_bucket{le=\"3600000.0\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_rq_time_bucket{le=\"+Inf\",envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_rq_time_sum{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" "envoy_http_downstream_rq_time_count{envoy_http_conn_manager_prefix=\"ingress_http\"} 0 0\n" @@ -1176,8 +1176,8 @@ void test_issue_fluent_bit_6021() "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"60000.0\"} 1 0\n" "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"300000.0\"} 1 0\n" "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"600000.0\"} 1 0\n" - "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"1.8e+06\"} 1 0\n" - "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"3.6e+06\"} 1 0\n" + "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"1800000.0\"} 1 0\n" + "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"3600000.0\"} 1 0\n" "envoy_listener_admin_downstream_cx_length_ms_bucket{le=\"+Inf\"} 1 0\n" "envoy_listener_admin_downstream_cx_length_ms_sum 15.5 0\n" "envoy_listener_admin_downstream_cx_length_ms_count 1 0\n" @@ -1200,8 +1200,8 @@ void test_issue_fluent_bit_6021() "envoy_listener_downstream_cx_length_ms_bucket{le=\"60000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" "envoy_listener_downstream_cx_length_ms_bucket{le=\"300000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" "envoy_listener_downstream_cx_length_ms_bucket{le=\"600000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" - "envoy_listener_downstream_cx_length_ms_bucket{le=\"1.8e+06\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" - "envoy_listener_downstream_cx_length_ms_bucket{le=\"3.6e+06\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" + "envoy_listener_downstream_cx_length_ms_bucket{le=\"1800000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" + "envoy_listener_downstream_cx_length_ms_bucket{le=\"3600000.0\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" "envoy_listener_downstream_cx_length_ms_bucket{le=\"+Inf\",envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" "envoy_listener_downstream_cx_length_ms_sum{envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" "envoy_listener_downstream_cx_length_ms_count{envoy_listener_address=\"0.0.0.0_10000\"} 0 0\n" @@ -1224,8 +1224,8 @@ void test_issue_fluent_bit_6021() "envoy_listener_manager_lds_update_duration_bucket{le=\"60000.0\"} 0 0\n" "envoy_listener_manager_lds_update_duration_bucket{le=\"300000.0\"} 0 0\n" "envoy_listener_manager_lds_update_duration_bucket{le=\"600000.0\"} 0 0\n" - "envoy_listener_manager_lds_update_duration_bucket{le=\"1.8e+06\"} 0 0\n" - "envoy_listener_manager_lds_update_duration_bucket{le=\"3.6e+06\"} 0 0\n" + "envoy_listener_manager_lds_update_duration_bucket{le=\"1800000.0\"} 0 0\n" + "envoy_listener_manager_lds_update_duration_bucket{le=\"3600000.0\"} 0 0\n" "envoy_listener_manager_lds_update_duration_bucket{le=\"+Inf\"} 0 0\n" "envoy_listener_manager_lds_update_duration_sum 0 0\n" "envoy_listener_manager_lds_update_duration_count 0 0\n" @@ -1248,8 +1248,8 @@ void test_issue_fluent_bit_6021() "envoy_sds_tls_sds_update_duration_bucket{le=\"60000.0\"} 0 0\n" "envoy_sds_tls_sds_update_duration_bucket{le=\"300000.0\"} 0 0\n" "envoy_sds_tls_sds_update_duration_bucket{le=\"600000.0\"} 0 0\n" - "envoy_sds_tls_sds_update_duration_bucket{le=\"1.8e+06\"} 0 0\n" - "envoy_sds_tls_sds_update_duration_bucket{le=\"3.6e+06\"} 0 0\n" + "envoy_sds_tls_sds_update_duration_bucket{le=\"1800000.0\"} 0 0\n" + "envoy_sds_tls_sds_update_duration_bucket{le=\"3600000.0\"} 0 0\n" "envoy_sds_tls_sds_update_duration_bucket{le=\"+Inf\"} 0 0\n" "envoy_sds_tls_sds_update_duration_sum 0 0\n" "envoy_sds_tls_sds_update_duration_count 0 0\n" @@ -1272,8 +1272,8 @@ void test_issue_fluent_bit_6021() "envoy_server_initialization_time_ms_bucket{le=\"60000.0\"} 1 0\n" "envoy_server_initialization_time_ms_bucket{le=\"300000.0\"} 1 0\n" "envoy_server_initialization_time_ms_bucket{le=\"600000.0\"} 1 0\n" - "envoy_server_initialization_time_ms_bucket{le=\"1.8e+06\"} 1 0\n" - "envoy_server_initialization_time_ms_bucket{le=\"3.6e+06\"} 1 0\n" + "envoy_server_initialization_time_ms_bucket{le=\"1800000.0\"} 1 0\n" + "envoy_server_initialization_time_ms_bucket{le=\"3600000.0\"} 1 0\n" "envoy_server_initialization_time_ms_bucket{le=\"+Inf\"} 1 0\n" "envoy_server_initialization_time_ms_sum 30.5 0\n" "envoy_server_initialization_time_ms_count 1 0\n"