From 6fc67b415b5a85e3fcf27ce3f02a7f46c8f49cae Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 5 Feb 2026 16:24:46 -0600 Subject: [PATCH 1/8] lib: cfl: update to v0.6.1 Signed-off-by: Eduardo Silva --- lib/cfl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cfl b/lib/cfl index f10ae81..d4f891d 160000 --- a/lib/cfl +++ b/lib/cfl @@ -1 +1 @@ -Subproject commit f10ae81dfbf91cbb401c9761e37579593ccd9a38 +Subproject commit d4f891d48f68dd6df30bfa96dc53c1ab5e310d6c From f82a8a7c7f8dd962259a3e357729e214d5534abf Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 5 Feb 2026 16:55:01 -0600 Subject: [PATCH 2/8] build: bump to v0.2.0 Signed-off-by: Eduardo Silva --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c60146d..3f2fa83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # CMetrics Version set(CPROF_VERSION_MAJOR 0) -set(CPROF_VERSION_MINOR 1) -set(CPROF_VERSION_PATCH 1) +set(CPROF_VERSION_MINOR 2) +set(CPROF_VERSION_PATCH 0) set(CPROF_VERSION_STR "${CPROF_VERSION_MAJOR}.${CPROF_VERSION_MINOR}.${CPROF_VERSION_PATCH}") # Include helpers From cea5eb33c28ea8f5008e26a98aaaabe25f4e7a25 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 5 Feb 2026 16:55:12 -0600 Subject: [PATCH 3/8] profiles: update to new proto specs Signed-off-by: Eduardo Silva --- lib/fluent-otel-proto | 2 +- src/cprof_decode_opentelemetry.c | 528 +++++++-------- src/cprof_encode_opentelemetry.c | 781 +++++----------------- src/cprof_opentelemetry_variant_helpers.c | 44 ++ tests/opentelemetry_transcoder.c | 366 +++++----- 5 files changed, 588 insertions(+), 1133 deletions(-) diff --git a/lib/fluent-otel-proto b/lib/fluent-otel-proto index 714f5cd..afb7125 160000 --- a/lib/fluent-otel-proto +++ b/lib/fluent-otel-proto @@ -1 +1 @@ -Subproject commit 714f5cdd129c4d6d09ff8db7f37b8be4f3a1d5c9 +Subproject commit afb71250cceb9f54d679d7bb84894b9074976bf8 diff --git a/src/cprof_decode_opentelemetry.c b/src/cprof_decode_opentelemetry.c index 14038a7..5300f29 100644 --- a/src/cprof_decode_opentelemetry.c +++ b/src/cprof_decode_opentelemetry.c @@ -103,58 +103,53 @@ static struct cprof_instrumentation_scope *decode_instrumentation_scope( static int decode_profile_sample_entry(struct cprof_sample *sample, - Opentelemetry__Proto__Profiles__V1development__Sample *input_sample) + Opentelemetry__Proto__Profiles__V1development__Sample *input_sample, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { int result; size_t index; - - for (index = 0 ; - index < input_sample->n_location_index; - index++) { - - result = cprof_sample_add_location_index(sample, input_sample->location_index[index]); - - if (result != 0) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + Opentelemetry__Proto__Profiles__V1development__Stack *stack; + + /* Resolve stack_index to location indices from dictionary.stack_table */ + if (dictionary != NULL && dictionary->stack_table != NULL && + input_sample->stack_index >= 0 && + (size_t)input_sample->stack_index < dictionary->n_stack_table) { + stack = dictionary->stack_table[input_sample->stack_index]; + if (stack != NULL && stack->location_indices != NULL) { + for (index = 0; index < stack->n_location_indices; index++) { + result = cprof_sample_add_location_index(sample, + (uint64_t)stack->location_indices[index]); + if (result != 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } } } - for (index = 0 ; - index < input_sample->n_value; - index++) { - - result = cprof_sample_add_value(sample, input_sample->value[index]); - + for (index = 0; index < input_sample->n_values; index++) { + result = cprof_sample_add_value(sample, input_sample->values[index]); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - for (index = 0 ; - index < input_sample->n_attributes; - index++) { - - result = cprof_sample_add_attribute(sample, input_sample->attributes[index]); - + for (index = 0; index < input_sample->n_attribute_indices; index++) { + result = cprof_sample_add_attribute(sample, + (uint64_t)input_sample->attribute_indices[index]); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - for (index = 0 ; - index < input_sample->n_timestamps_unix_nano; - index++) { - - result = cprof_sample_add_timestamp(sample, input_sample->timestamps_unix_nano[index]); - + for (index = 0; index < input_sample->n_timestamps_unix_nano; index++) { + result = cprof_sample_add_timestamp(sample, + input_sample->timestamps_unix_nano[index]); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - sample->locations_start_index = input_sample->locations_start_index; - sample->locations_length = input_sample->locations_length; - sample->link = input_sample->link; + sample->link = (uint64_t)(input_sample->link_index >= 0 ? input_sample->link_index : 0); return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } @@ -168,35 +163,31 @@ static int decode_mapping_entry(struct cprof_mapping *mapping, int result; size_t index; - for (index = 0 ; - index < input_mapping->n_attributes; - index++) { - - result = cprof_mapping_add_attribute(mapping, input_mapping->attributes[index]); - + mapping->id = 0; + mapping->memory_start = input_mapping->memory_start; + mapping->memory_limit = input_mapping->memory_limit; + mapping->file_offset = input_mapping->file_offset; + mapping->filename = (int64_t)input_mapping->filename_strindex; + mapping->has_functions = 0; + mapping->has_filenames = 0; + mapping->has_line_numbers = 0; + mapping->has_inline_frames = 0; + + for (index = 0; index < input_mapping->n_attribute_indices; index++) { + result = cprof_mapping_add_attribute(mapping, + (uint64_t)input_mapping->attribute_indices[index]); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - mapping->id = input_mapping->id; - mapping->memory_start = input_mapping->memory_start; - mapping->memory_limit = input_mapping->memory_limit; - mapping->file_offset = input_mapping->file_offset; - mapping->filename = input_mapping->filename; - - mapping->has_functions = input_mapping->has_functions; - mapping->has_filenames = input_mapping->has_filenames; - mapping->has_line_numbers = input_mapping->has_line_numbers; - mapping->has_inline_frames = input_mapping->has_inline_frames; - return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } static int decode_line_entry(struct cprof_line *line, Opentelemetry__Proto__Profiles__V1development__Line *input_line) { - line->function_index = input_line->function_index; + line->function_index = (uint64_t)input_line->function_index; line->line = input_line->line; line->column = input_line->column; @@ -205,41 +196,30 @@ static int decode_line_entry(struct cprof_line *line, static int decode_location_entry(struct cprof_location *location, Opentelemetry__Proto__Profiles__V1development__Location *input_location) - { int result; size_t index; struct cprof_line *line; - location->id = input_location->id; - location->mapping_index = input_location->mapping_index; + location->id = 0; + location->mapping_index = (uint64_t)input_location->mapping_index; location->address = input_location->address; + location->is_folded = 0; - for (index = 0 ; - index < input_location->n_line ; - index++) { + for (index = 0; index < input_location->n_lines; index++) { line = cprof_line_create(location); - if (line == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - - result = decode_line_entry(line, - input_location->line[index]); - + result = decode_line_entry(line, input_location->lines[index]); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - location->is_folded = input_location->is_folded; - - for (index = 0 ; - index < input_location->n_attributes; - index++) { - - result = cprof_location_add_attribute(location, input_location->attributes[index]); - + for (index = 0; index < input_location->n_attribute_indices; index++) { + result = cprof_location_add_attribute(location, + (uint64_t)input_location->attribute_indices[index]); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -251,20 +231,20 @@ static int decode_location_entry(struct cprof_location *location, static int decode_function_entry(struct cprof_function *function, Opentelemetry__Proto__Profiles__V1development__Function *input_function) { - function->id = input_function->id; - function->name = input_function->name; - function->system_name = input_function->system_name; - function->filename = input_function->filename; + function->id = 0; + function->name = (int64_t)input_function->name_strindex; + function->system_name = (int64_t)input_function->system_name_strindex; + function->filename = (int64_t)input_function->filename_strindex; function->start_line = input_function->start_line; return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } -static int decode_attribute_unit_entry(struct cprof_attribute_unit *attribute_unit, - Opentelemetry__Proto__Profiles__V1development__AttributeUnit *input_attribute_unit) +static int decode_keyvalueandunit_entry(struct cprof_attribute_unit *attribute_unit, + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *input_kv) { - attribute_unit->attribute_key = input_attribute_unit->attribute_key; - attribute_unit->unit = input_attribute_unit->unit; + attribute_unit->attribute_key = (int64_t)input_kv->key_strindex; + attribute_unit->unit = (int64_t)input_kv->unit_strindex; return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } @@ -299,7 +279,8 @@ static int decode_link_table_entry(struct cprof_link *link, static int decode_profile_entry(struct cprof_profile *profile, - Opentelemetry__Proto__Profiles__V1development__Profile *input_profile) + Opentelemetry__Proto__Profiles__V1development__Profile *input_profile, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { size_t string_table_add_result; struct cprof_attribute_unit *attribute_unit; @@ -308,313 +289,254 @@ static int decode_profile_entry(struct cprof_profile *profile, struct cprof_function *function; struct cprof_mapping *mapping; struct cprof_sample *sample; + struct cprof_link *link; int result; size_t index; - struct cprof_link *link; - - for (index = 0 ; - index < input_profile->n_sample_type ; - index++) { - sample_type = cprof_sample_type_create( - profile, - input_profile->sample_type[index]->type, - input_profile->sample_type[index]->unit, - input_profile->sample_type[index]->aggregation_temporality); - - if (sample_type == NULL) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - } - - for (index = 0 ; - index < input_profile->n_sample ; - index++) { - sample = cprof_sample_create(profile); - - if (sample == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - result = decode_profile_sample_entry(sample, - input_profile->sample[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - } - - for (index = 0 ; - index < input_profile->n_mapping; - index++) { - mapping = cprof_mapping_create(profile); - - if (mapping == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - result = decode_mapping_entry(mapping, - input_profile->mapping[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - } - - for (index = 0 ; - index < input_profile->n_location; - index++) { - location = cprof_location_create(profile); - - if (location == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - result = decode_location_entry(location, - input_profile->location[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - } - - for (index = 0 ; - index < input_profile->n_location_indices; - index++) { - - result = cprof_profile_add_location_index(profile, input_profile->location_indices[index]); - - if (result != 0) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - } - - for (index = 0 ; - index < input_profile->n_function; - index++) { - function = cprof_function_create(profile); - if (function == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + /* Copy dictionary tables into profile when dictionary is present */ + if (dictionary != NULL) { + /* String table */ + if (dictionary->string_table != NULL) { + for (index = 0; index < dictionary->n_string_table; index++) { + const char *s = dictionary->string_table[index]; + string_table_add_result = cprof_profile_string_add(profile, + (char *)(uintptr_t)(s != NULL ? s : ""), s != NULL ? (int)strlen(s) : 0); + if (string_table_add_result == (size_t)-1) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } } - result = decode_function_entry(function, - input_profile->function[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + /* Mappings */ + if (dictionary->mapping_table != NULL) { + for (index = 0; index < dictionary->n_mapping_table; index++) { + mapping = cprof_mapping_create(profile); + if (mapping == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_mapping_entry(mapping, dictionary->mapping_table[index]); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } } - } - - result = convert_kvarray_to_kvlist(profile->attribute_table, - input_profile->attribute_table, - input_profile->n_attribute_table); - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } - - for (index = 0 ; - index < input_profile->n_attribute_units; - index++) { - attribute_unit = cprof_attribute_unit_create(profile); - - if (attribute_unit == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + /* Locations */ + if (dictionary->location_table != NULL) { + for (index = 0; index < dictionary->n_location_table; index++) { + location = cprof_location_create(profile); + if (location == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_location_entry(location, dictionary->location_table[index]); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } } - result = decode_attribute_unit_entry(attribute_unit, - input_profile->attribute_units[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + /* Functions */ + if (dictionary->function_table != NULL) { + for (index = 0; index < dictionary->n_function_table; index++) { + function = cprof_function_create(profile); + if (function == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_function_entry(function, dictionary->function_table[index]); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } } - } - - for (index = 0 ; - index < input_profile->n_link_table; - index++) { - link = cprof_link_create(profile); - if (link == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + /* Attribute table (KeyValueAndUnit) and attribute_units */ + if (dictionary->attribute_table != NULL && dictionary->n_attribute_table > 0) { + result = convert_keyvalueandunit_array_to_kvlist(profile->attribute_table, + dictionary->attribute_table, + dictionary->n_attribute_table, + dictionary->string_table, + dictionary->n_string_table); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + for (index = 0; index < dictionary->n_attribute_table; index++) { + attribute_unit = cprof_attribute_unit_create(profile); + if (attribute_unit == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_keyvalueandunit_entry(attribute_unit, + dictionary->attribute_table[index]); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } } - result = decode_link_table_entry(link, - input_profile->link_table[index]); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + /* Link table */ + if (dictionary->link_table != NULL) { + for (index = 0; index < dictionary->n_link_table; index++) { + link = cprof_link_create(profile); + if (link == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_link_table_entry(link, dictionary->link_table[index]); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } } } - for (index = 0 ; - index < input_profile->n_string_table; - index++) { - string_table_add_result = cprof_profile_string_add( - profile, - input_profile->string_table[index], - strlen(input_profile->string_table[index])); - - if (string_table_add_result == -1) { + /* Profile sample_type (single ValueType in new proto) */ + if (input_profile->sample_type != NULL) { + sample_type = cprof_sample_type_create(profile, + (int64_t)input_profile->sample_type->type_strindex, + (int64_t)input_profile->sample_type->unit_strindex, + 0); + if (sample_type == NULL) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } } - for (index = 0 ; - index < input_profile->n_comment; - index++) { - result = cprof_profile_add_comment( - profile, - input_profile->comment[index]); - - if (result != 0) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + /* Samples */ + if (input_profile->samples != NULL) { + for (index = 0; index < input_profile->n_samples; index++) { + sample = cprof_sample_create(profile); + if (sample == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_profile_sample_entry(sample, + input_profile->samples[index], dictionary); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } } } - profile->drop_frames = input_profile->drop_frames; - profile->keep_frames = input_profile->keep_frames; - - profile->time_nanos = input_profile->time_nanos; - profile->duration_nanos = input_profile->duration_nanos; + profile->time_nanos = (int64_t)input_profile->time_unix_nano; + profile->duration_nanos = (int64_t)input_profile->duration_nano; + profile->drop_frames = 0; + profile->keep_frames = 0; + profile->period = input_profile->period; if (input_profile->period_type != NULL) { - profile->period_type.type = input_profile->period_type->type; - profile->period_type.unit = input_profile->period_type->unit; - profile->period_type.aggregation_temporality = input_profile->period_type->aggregation_temporality; + profile->period_type.type = (int64_t)input_profile->period_type->type_strindex; + profile->period_type.unit = (int64_t)input_profile->period_type->unit_strindex; + profile->period_type.aggregation_temporality = 0; } - profile->period = input_profile->period; - profile->default_sample_type = input_profile->default_sample_type; + profile->default_sample_type = 0; return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } -static int decode_profile_container_entry(struct cprof_scope_profiles *scope_profiles, - Opentelemetry__Proto__Profiles__V1development__ProfileContainer *input_profile_container) +static int decode_profile_into_scope(struct cprof_scope_profiles *scope_profiles, + Opentelemetry__Proto__Profiles__V1development__Profile *input_profile, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { - struct cprof_profile *profile; - int result; - - if (input_profile_container->profile_id.data == NULL || - input_profile_container->profile_id.len != 16) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; - } + struct cprof_profile *profile; + int result; profile = cprof_profile_create(); - if (profile == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - memcpy(profile->profile_id, - input_profile_container->profile_id.data, - sizeof(profile->profile_id)); - - profile->start_time_unix_nano = input_profile_container->start_time_unix_nano; - profile->end_time_unix_nano = input_profile_container->end_time_unix_nano; - - - result = convert_kvarray_to_kvlist(profile->attributes, - input_profile_container->attributes, - input_profile_container->n_attributes); - - if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - cprof_profile_destroy(profile); - - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + /* Profile-level fields (no ProfileContainer in new proto) */ + if (input_profile->profile_id.data != NULL && input_profile->profile_id.len >= 16) { + memcpy(profile->profile_id, input_profile->profile_id.data, 16); + } + else { + memset(profile->profile_id, 0, sizeof(profile->profile_id)); } - profile->dropped_attributes_count = input_profile_container->dropped_attributes_count; + profile->start_time_unix_nano = (int64_t)input_profile->time_unix_nano; + profile->end_time_unix_nano = (int64_t)input_profile->time_unix_nano + + (int64_t)input_profile->duration_nano; - if (input_profile_container->original_payload_format != NULL) { - profile->original_payload_format = cfl_sds_create(input_profile_container->original_payload_format); + profile->dropped_attributes_count = input_profile->dropped_attributes_count; + if (input_profile->original_payload_format != NULL && input_profile->original_payload_format[0] != '\0') { + profile->original_payload_format = cfl_sds_create(input_profile->original_payload_format); if (profile->original_payload_format == NULL) { cprof_profile_destroy(profile); - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } } - if (input_profile_container->original_payload.data != NULL && - input_profile_container->original_payload.len > 0) { - profile->original_payload = \ - cfl_sds_create_len( - (const char *) input_profile_container->original_payload.data, - input_profile_container->original_payload.len); - + if (input_profile->original_payload.data != NULL && input_profile->original_payload.len > 0) { + profile->original_payload = cfl_sds_create_len( + (const char *)input_profile->original_payload.data, + input_profile->original_payload.len); if (profile->original_payload == NULL) { cprof_profile_destroy(profile); - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } } - result = decode_profile_entry( - profile, - input_profile_container->profile); - + result = decode_profile_entry(profile, input_profile, dictionary); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cprof_profile_destroy(profile); return result; } cfl_list_add(&profile->_head, &scope_profiles->profiles); - return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } - static int decode_scope_profiles_entry(struct cprof_resource_profiles *resource_profiles, - Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *input_scope_profiles) + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *input_scope_profiles, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { struct cprof_scope_profiles *profiles; int result; size_t index; profiles = cprof_scope_profiles_create( - resource_profiles, - input_scope_profiles->schema_url); + resource_profiles, + input_scope_profiles->schema_url != NULL ? input_scope_profiles->schema_url : ""); if (profiles == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - profiles->scope = decode_instrumentation_scope(input_scope_profiles->scope); + if (input_scope_profiles->scope != NULL) { + profiles->scope = decode_instrumentation_scope(input_scope_profiles->scope); + } + else { + profiles->scope = cprof_instrumentation_scope_create(NULL, NULL, NULL, 0); + } if (profiles->scope == NULL) { cprof_scope_profiles_destroy(profiles); - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - if (input_scope_profiles->n_profiles > 0) { - for (index = 0 ; - index < input_scope_profiles->n_profiles ; - index++) { - result = decode_profile_container_entry( - profiles, - input_scope_profiles->profiles[index]); - + if (input_scope_profiles->profiles != NULL && input_scope_profiles->n_profiles > 0) { + for (index = 0; index < input_scope_profiles->n_profiles; index++) { + result = decode_profile_into_scope(profiles, + input_scope_profiles->profiles[index], dictionary); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { cprof_scope_profiles_destroy(profiles); - return result; } } } + /* cprof_scope_profiles_create already added profiles to resource_profiles */ return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } static int decode_resource_profiles_entry(struct cprof *context, - Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *resource_profile) + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *resource_profile, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { struct cprof_resource_profiles *profile; int result; size_t index; - profile = cprof_resource_profiles_create(resource_profile->schema_url); + profile = cprof_resource_profiles_create( + resource_profile->schema_url != NULL ? resource_profile->schema_url : ""); if (profile == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -630,15 +552,13 @@ static int decode_resource_profiles_entry(struct cprof *context, result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; - if (resource_profile->n_scope_profiles > 0) { - for (index = 0 ; + if (resource_profile->scope_profiles != NULL && resource_profile->n_scope_profiles > 0) { + for (index = 0; result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && - index < resource_profile->n_scope_profiles ; + index < resource_profile->n_scope_profiles; index++) { - - result = decode_scope_profiles_entry( - profile, - resource_profile->scope_profiles[index]); + result = decode_scope_profiles_entry(profile, + resource_profile->scope_profiles[index], dictionary); } } @@ -661,20 +581,20 @@ static int decode_resource_profiles_entry(struct cprof *context, static int decode_service_request(struct cprof *context, Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *service_request) { - int result; - size_t index; + int result; + size_t index; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary; + dictionary = service_request->dictionary; result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; - if (service_request->n_resource_profiles > 0) { - for (index = 0 ; + if (service_request->resource_profiles != NULL && service_request->n_resource_profiles > 0) { + for (index = 0; result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && - index < service_request->n_resource_profiles ; + index < service_request->n_resource_profiles; index++) { - - result = decode_resource_profiles_entry( - context, - service_request->resource_profiles[index]); + result = decode_resource_profiles_entry(context, + service_request->resource_profiles[index], dictionary); } } diff --git a/src/cprof_encode_opentelemetry.c b/src/cprof_encode_opentelemetry.c index 59409c2..2d19ed3 100644 --- a/src/cprof_encode_opentelemetry.c +++ b/src/cprof_encode_opentelemetry.c @@ -612,16 +612,12 @@ static void destroy_sample( instance) { if (instance != NULL) { - if (instance->location_index != NULL) { - free(instance->location_index); + if (instance->values != NULL) { + free(instance->values); } - if (instance->value != NULL) { - free(instance->value); - } - - if (instance->attributes != NULL) { - free(instance->attributes); + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); } if (instance->timestamps_unix_nano != NULL) { @@ -638,8 +634,8 @@ static void destroy_mapping( instance) { if (instance != NULL) { - if (instance->attributes != NULL) { - free(instance->attributes); + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); } free(instance); @@ -696,27 +692,30 @@ static void destroy_location( size_t index; if (instance != NULL) { - if (instance->line != NULL) { - for (index = 0 ; index < instance->n_line ; index++) { - destroy_line(instance->line[index]); + if (instance->lines != NULL) { + for (index = 0 ; index < instance->n_lines ; index++) { + destroy_line(instance->lines[index]); } - free(instance->line); + free(instance->lines); } - if (instance->attributes != NULL) { - free(instance->attributes); + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); } free(instance); } } -static void destroy_attribute_unit( - Opentelemetry__Proto__Profiles__V1development__AttributeUnit * +static void destroy_keyvalueandunit( + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit * instance) { if (instance != NULL) { + if (instance->value != NULL) { + otlp_any_value_destroy(instance->value); + } free(instance); } } @@ -761,114 +760,36 @@ static void destroy_profile( if (instance != NULL) { if (instance->sample_type != NULL) { - for (index = 0 ; index < instance->n_sample_type ; index++) { - destroy_value_type(instance->sample_type[index]); - } - - free(instance->sample_type); - } - - if (instance->sample != NULL) { - for (index = 0 ; index < instance->n_sample ; index++) { - destroy_sample(instance->sample[index]); - } - - free(instance->sample); - } - - if (instance->mapping != NULL) { - for (index = 0 ; index < instance->n_mapping ; index++) { - destroy_mapping(instance->mapping[index]); - } - - free(instance->mapping); - } - - if (instance->location != NULL) { - for (index = 0 ; index < instance->n_location ; index++) { - destroy_location(instance->location[index]); - } - - free(instance->location); - } - - if (instance->location_indices != NULL) { - free(instance->location_indices); - } - - if (instance->function != NULL) { - for (index = 0 ; index < instance->n_function ; index++) { - destroy_function(instance->function[index]); - } - - free(instance->function); - } - - if (instance->attribute_table != NULL) { - destroy_attribute_list(instance->attribute_table); - } - - for (index = 0 ; index < instance->n_attribute_units ; index++) { - destroy_attribute_unit(instance->attribute_units[index]); - } - - if (instance->link_table != NULL) { - for (index = 0 ; index < instance->n_link_table ; index++) { - destroy_link(instance->link_table[index]); - } - - free(instance->link_table); + destroy_value_type(instance->sample_type); } - if (instance->string_table != NULL) { - for (index = 0 ; index < instance->n_string_table ; index++) { - if (is_string_releaseable(instance->string_table[index])) { - cfl_sds_destroy(instance->string_table[index]); - } + if (instance->samples != NULL) { + for (index = 0 ; index < instance->n_samples ; index++) { + destroy_sample(instance->samples[index]); } - - free(instance->string_table); + free(instance->samples); } if (instance->period_type != NULL) { destroy_value_type(instance->period_type); } - if (instance->comment != NULL) { - free(instance->comment); + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); } - free(instance); - } -} - -static void destroy_profile_container( - Opentelemetry__Proto__Profiles__V1development__ProfileContainer * - instance) -{ - if (instance != NULL) { - if (instance->profile_id.data != NULL) { - if (is_string_releaseable((cfl_sds_t) instance->profile_id.data)) { - cfl_sds_destroy((cfl_sds_t) instance->profile_id.data); - } + if (instance->profile_id.data != NULL && is_string_releaseable((char *)instance->profile_id.data)) { + free(instance->profile_id.data); } - destroy_attribute_list(instance->attributes); - - if (instance->original_payload_format != NULL) { - if (is_string_releaseable(instance->original_payload_format)) { - cfl_sds_destroy(instance->original_payload_format); - } + if (instance->original_payload_format != NULL && is_string_releaseable(instance->original_payload_format)) { + free(instance->original_payload_format); } - if (instance->original_payload.data != NULL) { - if (is_string_releaseable((cfl_sds_t) instance->original_payload.data)) { - cfl_sds_destroy((cfl_sds_t) instance->original_payload.data); - } + if (instance->original_payload.data != NULL && is_string_releaseable((char *)instance->original_payload.data)) { + free(instance->original_payload.data); } - destroy_profile(instance->profile); - free(instance); } } @@ -886,7 +807,7 @@ static void destroy_scope_profiles( if (instance->profiles != NULL) { for (index = 0 ; index < instance->n_profiles ; index++) { - destroy_profile_container(instance->profiles[index]); + destroy_profile(instance->profiles[index]); } free(instance->profiles); @@ -930,6 +851,71 @@ static void destroy_resource_profiles( } } +static void destroy_stack(Opentelemetry__Proto__Profiles__V1development__Stack *instance) +{ + if (instance != NULL) { + if (instance->location_indices != NULL) { + free(instance->location_indices); + } + free(instance); + } +} + +static void destroy_profiles_dictionary( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict) +{ + size_t index; + + if (dict == NULL) { + return; + } + if (dict->mapping_table != NULL) { + for (index = 0; index < dict->n_mapping_table; index++) { + destroy_mapping(dict->mapping_table[index]); + } + free(dict->mapping_table); + } + if (dict->location_table != NULL) { + for (index = 0; index < dict->n_location_table; index++) { + destroy_location(dict->location_table[index]); + } + free(dict->location_table); + } + if (dict->function_table != NULL) { + for (index = 0; index < dict->n_function_table; index++) { + destroy_function(dict->function_table[index]); + } + free(dict->function_table); + } + if (dict->link_table != NULL) { + for (index = 0; index < dict->n_link_table; index++) { + destroy_link(dict->link_table[index]); + } + free(dict->link_table); + } + if (dict->string_table != NULL) { + for (index = 0; index < dict->n_string_table; index++) { + if (dict->string_table[index] != NULL && is_string_releaseable(dict->string_table[index])) { + free(dict->string_table[index]); + } + } + free(dict->string_table); + } + if (dict->attribute_table != NULL) { + for (index = 0; index < dict->n_attribute_table; index++) { + destroy_keyvalueandunit(dict->attribute_table[index]); + } + free(dict->attribute_table); + } + if (dict->stack_table != NULL) { + for (index = 0; index < dict->n_stack_table; index++) { + destroy_stack(dict->stack_table[index]); + } + free(dict->stack_table); + } + free(dict); +} + static void destroy_export_profiles_service_request( Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest * instance) @@ -945,6 +931,10 @@ static void destroy_export_profiles_service_request( free(instance->resource_profiles); } + if (instance->dictionary != NULL) { + destroy_profiles_dictionary(instance->dictionary); + } + free(instance); } } @@ -973,7 +963,6 @@ static static Opentelemetry__Proto__Profiles__V1development__Sample * initialize_sample( - size_t location_index_count, size_t value_count, size_t attributes_count, size_t timestamps_count) { @@ -987,40 +976,28 @@ static opentelemetry__proto__profiles__v1development__sample__init(instance); - if (location_index_count > 0) { - instance->location_index = calloc(location_index_count, sizeof(uint64_t)); - - if (instance->location_index == NULL) { - destroy_sample(instance); - - return NULL; - } - - instance->n_location_index = location_index_count; - } - if (value_count > 0) { - instance->value = calloc(value_count, sizeof(int64_t)); + instance->values = calloc(value_count, sizeof(int64_t)); - if (instance->value == NULL) { + if (instance->values == NULL) { destroy_sample(instance); return NULL; } - instance->n_value = value_count; + instance->n_values = value_count; } if (attributes_count > 0) { - instance->attributes = calloc(attributes_count, sizeof(uint64_t)); + instance->attribute_indices = calloc(attributes_count, sizeof(int32_t)); - if (instance->attributes == NULL) { + if (instance->attribute_indices == NULL) { destroy_sample(instance); return NULL; } - instance->n_attributes = attributes_count; + instance->n_attribute_indices = attributes_count; } if (timestamps_count > 0) { @@ -1072,17 +1049,17 @@ static } static - Opentelemetry__Proto__Profiles__V1development__AttributeUnit * - initialize_attribute_unit() { - Opentelemetry__Proto__Profiles__V1development__AttributeUnit *instance; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit * + initialize_keyvalueandunit(void) { + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *instance; - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__AttributeUnit)); + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit)); if (instance == NULL) { return NULL; } - opentelemetry__proto__profiles__v1development__attribute_unit__init(instance); + opentelemetry__proto__profiles__v1development__key_value_and_unit__init(instance); return instance; } @@ -1133,27 +1110,27 @@ static opentelemetry__proto__profiles__v1development__location__init(instance); if (line_count > 0) { - instance->line = calloc(line_count, sizeof(void *)); + instance->lines = calloc(line_count, sizeof(void *)); - if (instance->line == NULL) { + if (instance->lines == NULL) { destroy_location(instance); return NULL; } - instance->n_line = line_count; + instance->n_lines = line_count; } if (attribute_count > 0) { - instance->attributes = calloc(attribute_count, sizeof(uint64_t)); + instance->attribute_indices = calloc(attribute_count, sizeof(int32_t)); - if (instance->attributes == NULL) { + if (instance->attribute_indices == NULL) { destroy_location(instance); return NULL; } - instance->n_attributes = attribute_count; + instance->n_attribute_indices = attribute_count; } return instance; @@ -1189,15 +1166,15 @@ static opentelemetry__proto__profiles__v1development__mapping__init(instance); if (attribute_count > 0) { - instance->attributes = calloc(attribute_count, sizeof(uint64_t)); + instance->attribute_indices = calloc(attribute_count, sizeof(int32_t)); - if (instance->attributes == NULL) { + if (instance->attribute_indices == NULL) { destroy_mapping(instance); return NULL; } - instance->n_attributes = attribute_count; + instance->n_attribute_indices = attribute_count; } return instance; @@ -1233,18 +1210,7 @@ static static Opentelemetry__Proto__Profiles__V1development__Profile * - initialize_profile( - size_t sample_type_count, - size_t sample_count, - size_t mapping_count, - size_t location_count, - size_t location_index_count, - size_t function_count, - size_t attribute_count, - size_t attribute_unit_count, - size_t link_count, - size_t string_count, - size_t comment_count) { + initialize_profile(size_t sample_count, size_t attribute_index_count) { Opentelemetry__Proto__Profiles__V1development__Profile *instance; instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Profile)); @@ -1255,170 +1221,34 @@ static opentelemetry__proto__profiles__v1development__profile__init(instance); - if (sample_type_count > 0) { - instance->sample_type = calloc(sample_type_count, sizeof(void *)); - - if (instance->sample_type == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_sample_type = sample_type_count; - } - if (sample_count > 0) { - instance->sample = calloc(sample_count, sizeof(void *)); - - if (instance->sample == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_sample = sample_count; - } - - if (mapping_count > 0) { - instance->mapping = calloc(mapping_count, sizeof(void *)); + instance->samples = calloc(sample_count, sizeof(void *)); - if (instance->mapping == NULL) { + if (instance->samples == NULL) { destroy_profile(instance); return NULL; } - instance->n_mapping = mapping_count; + instance->n_samples = sample_count; } - if (location_count > 0) { - instance->location = calloc(location_count, sizeof(void *)); + if (attribute_index_count > 0) { + instance->attribute_indices = calloc(attribute_index_count, sizeof(int32_t)); - if (instance->location == NULL) { + if (instance->attribute_indices == NULL) { destroy_profile(instance); return NULL; } - instance->n_location = location_count; - } - - if (location_index_count > 0) { - instance->location_indices = calloc(location_index_count, sizeof(uint64_t)); - - if (instance->location_indices == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_location_indices = location_index_count; - } - - if (function_count > 0) { - instance->function = calloc(function_count, sizeof(void *)); - - if (instance->function == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_function = function_count; - } - - if (attribute_count > 0) { - instance->attribute_table = calloc(attribute_count, sizeof(void *)); - - if (instance->attribute_table == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_attribute_table = attribute_count; - } - - if (attribute_unit_count > 0) { - instance->attribute_units = calloc(attribute_unit_count, sizeof(void *)); - - if (instance->attribute_units == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_attribute_units = attribute_unit_count; - } - - if (link_count > 0) { - instance->link_table = calloc(link_count, sizeof(void *)); - - if (instance->link_table == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_link_table = link_count; - } - - if (string_count > 0) { - instance->string_table = calloc(string_count, sizeof(void *)); - - if (instance->string_table == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_string_table = string_count; - } - - if (comment_count > 0) { - instance->comment = calloc(comment_count, sizeof(void *)); - - if (instance->comment == NULL) { - destroy_profile(instance); - - return NULL; - } - - instance->n_comment = comment_count; + instance->n_attribute_indices = attribute_index_count; } return instance; } -static - Opentelemetry__Proto__Profiles__V1development__ProfileContainer * - initialize_profile_container(size_t attribute_count) { - Opentelemetry__Proto__Profiles__V1development__ProfileContainer *instance; - - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__ProfileContainer)); - - if (instance == NULL) { - return NULL; - } - - opentelemetry__proto__profiles__v1development__profile_container__init(instance); - - if (attribute_count > 0) { - instance->attributes = initialize_attribute_list(attribute_count); - - if (instance->attributes == NULL) { - free(instance); - - return NULL; - } - } - - instance->n_attributes = attribute_count; - - return instance; -} - static Opentelemetry__Proto__Profiles__V1development__ScopeProfiles * initialize_scope_profiles(size_t profiles_count) { @@ -1598,9 +1428,9 @@ static int pack_cprof_value_type( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - otlp_value_type->type = input_instance->type; - otlp_value_type->unit = input_instance->unit; - otlp_value_type->aggregation_temporality = input_instance->aggregation_temporality; + /* type/unit are now string table indices; use 0 until dictionary encoding */ + otlp_value_type->type_strindex = 0; + otlp_value_type->unit_strindex = 0; *output_instance = otlp_value_type; @@ -1614,8 +1444,7 @@ static int pack_cprof_sample( Opentelemetry__Proto__Profiles__V1development__Sample *otlp_sample; size_t index; - otlp_sample = initialize_sample(input_instance->location_index_count, - input_instance->value_count, + otlp_sample = initialize_sample(input_instance->value_count, input_instance->attributes_count, input_instance->timestamps_count); @@ -1623,28 +1452,22 @@ static int pack_cprof_sample( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - for (index = 0 ; - index < input_instance->location_index_count ; - index++) { - otlp_sample->location_index[index] = input_instance->location_index[index]; - } - - otlp_sample->locations_start_index = input_instance->locations_start_index; - otlp_sample->locations_length = input_instance->locations_length; + /* stack_index into dictionary stack_table; 0 until dictionary encoding */ + otlp_sample->stack_index = 0; for (index = 0 ; index < input_instance->value_count ; index++) { - otlp_sample->value[index] = input_instance->values[index]; + otlp_sample->values[index] = input_instance->values[index]; } for (index = 0 ; index < input_instance->attributes_count ; index++) { - otlp_sample->attributes[index] = input_instance->attributes[index]; + otlp_sample->attribute_indices[index] = (int32_t) input_instance->attributes[index]; } - otlp_sample->link = input_instance->link; + otlp_sample->link_index = input_instance->link >= 0 ? (int32_t) input_instance->link : -1; for (index = 0 ; index < input_instance->timestamps_count ; @@ -1671,23 +1494,14 @@ static int pack_cprof_mapping( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - otlp_mapping->id = input_instance->id; - otlp_mapping->memory_start = input_instance->memory_start; - otlp_mapping->memory_limit = input_instance->memory_limit; - otlp_mapping->file_offset = input_instance->file_offset; - otlp_mapping->filename = input_instance->filename; + otlp_mapping->filename_strindex = 0; /* TODO: resolve via dictionary string_table */ for (index = 0 ; index < input_instance->attributes_count ; index++) { - otlp_mapping->attributes[index] = input_instance->attributes[index]; + otlp_mapping->attribute_indices[index] = (int32_t) input_instance->attributes[index]; } - otlp_mapping->has_functions = input_instance->has_functions; - otlp_mapping->has_filenames = input_instance->has_filenames; - otlp_mapping->has_line_numbers = input_instance->has_line_numbers; - otlp_mapping->has_inline_frames = input_instance->has_inline_frames; - *output_instance = otlp_mapping; return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; @@ -1733,11 +1547,9 @@ static int pack_cprof_location( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - otlp_location->id = input_instance->id; otlp_location->mapping_index = input_instance->mapping_index; otlp_location->address = input_instance->address; - index = 0; cfl_list_foreach(iterator, &input_instance->lines) { @@ -1746,7 +1558,7 @@ static int pack_cprof_location( struct cprof_line, _head); result = pack_cprof_line( - &otlp_location->line[index], + &otlp_location->lines[index], line); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { @@ -1758,12 +1570,10 @@ static int pack_cprof_location( index++; } - otlp_location->is_folded = input_instance->is_folded; - for (index = 0 ; index < input_instance->attributes_count ; index++) { - otlp_location->attributes[index] = input_instance->attributes[index]; + otlp_location->attribute_indices[index] = (int32_t) input_instance->attributes[index]; } *output_instance = otlp_location; @@ -1783,10 +1593,10 @@ static int pack_cprof_function( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - otlp_function->id = input_instance->id; - otlp_function->name = input_instance->name; - otlp_function->system_name = input_instance->system_name; - otlp_function->filename = input_instance->filename; + /* String fields are now string table indices; use 0 until dictionary encoding */ + otlp_function->name_strindex = 0; + otlp_function->system_name_strindex = 0; + otlp_function->filename_strindex = 0; otlp_function->start_line = input_instance->start_line; *output_instance = otlp_function; @@ -1794,22 +1604,24 @@ static int pack_cprof_function( return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; } -static int pack_cprof_attribute_unit( - Opentelemetry__Proto__Profiles__V1development__AttributeUnit **output_instance, +static int pack_cprof_keyvalueandunit( + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **output_instance, struct cprof_attribute_unit *input_instance) { - Opentelemetry__Proto__Profiles__V1development__AttributeUnit *otlp_attribute_unit; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *otlp_kv; - otlp_attribute_unit = initialize_attribute_unit(); + otlp_kv = initialize_keyvalueandunit(); - if (otlp_attribute_unit == NULL) { + if (otlp_kv == NULL) { return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - otlp_attribute_unit->attribute_key = input_instance->attribute_key; - otlp_attribute_unit->unit = input_instance->unit; + /* key_strindex / unit_strindex require dictionary string_table; use 0 for now */ + otlp_kv->key_strindex = 0; + otlp_kv->unit_strindex = 0; + otlp_kv->value = NULL; /* TODO: convert attribute value to AnyValue if needed */ - *output_instance = otlp_attribute_unit; + *output_instance = otlp_kv; return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; } @@ -1864,310 +1676,55 @@ static int pack_cprof_profile( Opentelemetry__Proto__Profiles__V1development__Profile *otlp_profile; struct cfl_list *iterator; struct cprof_sample *sample; - struct cprof_link *link; - struct cprof_mapping *mapping; - struct cprof_location *location; - struct cprof_function *function; struct cprof_value_type *sample_type; - struct cprof_attribute_unit *attribute_unit; int result; size_t index; - otlp_profile = initialize_profile(cfl_list_size(&input_instance->sample_type), - cfl_list_size(&input_instance->samples), - cfl_list_size(&input_instance->mappings), - cfl_list_size(&input_instance->locations), - input_instance->location_indices_count, - cfl_list_size(&input_instance->functions), - 0, - cfl_list_size(&input_instance->attribute_units), - cfl_list_size(&input_instance->link_table), - input_instance->string_table_count, - input_instance->comments_count); + otlp_profile = initialize_profile(cfl_list_size(&input_instance->samples), 0); if (otlp_profile == NULL) { return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - index = 0; - cfl_list_foreach(iterator, - &input_instance->sample_type) { - sample_type = cfl_list_entry( - iterator, - struct cprof_value_type, _head); - - result = pack_cprof_value_type( - &otlp_profile->sample_type[index], - sample_type); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile(otlp_profile); - - return result; - } - - index++; - } - - index = 0; - cfl_list_foreach(iterator, - &input_instance->samples) { - sample = cfl_list_entry( - iterator, - struct cprof_sample, _head); - - result = pack_cprof_sample( - &otlp_profile->sample[index], - sample); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile(otlp_profile); - - return result; - } - - index++; - } - - index = 0; - cfl_list_foreach(iterator, - &input_instance->mappings) { - mapping = cfl_list_entry( - iterator, - struct cprof_mapping, _head); - - result = pack_cprof_mapping( - &otlp_profile->mapping[index], - mapping); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile(otlp_profile); - - return result; - } - - index++; - } - - index = 0; - cfl_list_foreach(iterator, - &input_instance->locations) { - location = cfl_list_entry( - iterator, - struct cprof_location, _head); - - result = pack_cprof_location( - &otlp_profile->location[index], - location); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile(otlp_profile); - - return result; - } - - index++; - } - - for (index = 0 ; - index < input_instance->location_indices_count ; - index++) { - otlp_profile->location_indices[index] = input_instance->location_indices[index]; - } - - index = 0; - cfl_list_foreach(iterator, - &input_instance->functions) { - function = cfl_list_entry( - iterator, - struct cprof_function, _head); - - result = pack_cprof_function( - &otlp_profile->function[index], - function); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile(otlp_profile); - - return result; - } - - index++; - } - - if (input_instance->attribute_table != NULL) { - otlp_profile->attribute_table = cfl_kvlist_to_otlp_kvpair_list(input_instance->attribute_table); - - if (otlp_profile->attribute_table == NULL) { - destroy_profile(otlp_profile); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_profile->n_attribute_table = cfl_kvlist_count(input_instance->attribute_table); - } - - index = 0; - cfl_list_foreach(iterator, - &input_instance->attribute_units) { - attribute_unit = cfl_list_entry( - iterator, - struct cprof_attribute_unit, _head); - - result = pack_cprof_attribute_unit( - &otlp_profile->attribute_units[index], - attribute_unit); - + /* New Profile has single sample_type; use first from list if any */ + if (!cfl_list_is_empty(&input_instance->sample_type)) { + sample_type = cfl_list_entry_first(&input_instance->sample_type, + struct cprof_value_type, _head); + result = pack_cprof_value_type(&otlp_profile->sample_type, sample_type); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); - return result; } - - index++; } index = 0; - cfl_list_foreach(iterator, - &input_instance->link_table) { - link = cfl_list_entry( - iterator, - struct cprof_link, _head); - - result = pack_cprof_link( - &otlp_profile->link_table[index], - link); + cfl_list_foreach(iterator, &input_instance->samples) { + sample = cfl_list_entry(iterator, struct cprof_sample, _head); + result = pack_cprof_sample(&otlp_profile->samples[index], sample); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); - return result; } - index++; } - for (index = 0 ; - index < input_instance->string_table_count ; - index++) { - otlp_profile->string_table[index] = cfl_sds_create(input_instance->string_table[index]); - - if (otlp_profile->string_table[index] == NULL) { - destroy_profile(otlp_profile); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - } - - otlp_profile->drop_frames = input_instance->drop_frames; - otlp_profile->keep_frames = input_instance->keep_frames; - otlp_profile->time_nanos = input_instance->time_nanos; - otlp_profile->duration_nanos = input_instance->duration_nanos; - - result = pack_cprof_value_type( - &otlp_profile->period_type, - &input_instance->period_type); + otlp_profile->time_unix_nano = input_instance->time_nanos; + otlp_profile->duration_nano = input_instance->duration_nanos; + result = pack_cprof_value_type(&otlp_profile->period_type, &input_instance->period_type); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); - return result; } otlp_profile->period = input_instance->period; - for (index = 0 ; - index < input_instance->comments_count ; - index++) { - otlp_profile->comment[index] = input_instance->comments[index]; - } - - otlp_profile->default_sample_type = input_instance->default_sample_type; - - *output_instance = otlp_profile; + *output_instance = otlp_profile; return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; } -static int pack_cprof_profile_container( - Opentelemetry__Proto__Profiles__V1development__ProfileContainer **output_instance, - struct cprof_profile *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__ProfileContainer *otlp_profile_container; - int result; - - otlp_profile_container = initialize_profile_container(0); - - if (otlp_profile_container == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_profile_container->profile_id.data = \ - (uint8_t *) cfl_sds_create_len((const char *) input_instance->profile_id, - sizeof(input_instance->profile_id)); - - if (otlp_profile_container->profile_id.data == NULL) { - destroy_profile_container(otlp_profile_container); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_profile_container->profile_id.len = sizeof(input_instance->profile_id); - - otlp_profile_container->start_time_unix_nano = (uint64_t) input_instance->start_time_unix_nano; - otlp_profile_container->end_time_unix_nano = (uint64_t) input_instance->end_time_unix_nano; - - otlp_profile_container->attributes = cfl_kvlist_to_otlp_kvpair_list(input_instance->attributes); - - if (otlp_profile_container->attributes == NULL) { - destroy_profile_container(otlp_profile_container); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_profile_container->n_attributes = cfl_kvlist_count(input_instance->attributes); - - otlp_profile_container->dropped_attributes_count = input_instance->dropped_attributes_count; - - if (input_instance->original_payload_format != NULL) { - otlp_profile_container->original_payload_format = \ - cfl_sds_create(input_instance->original_payload_format); - - if (otlp_profile_container->original_payload_format == NULL) { - destroy_profile_container(otlp_profile_container); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - } - - if (input_instance->original_payload != NULL) { - otlp_profile_container->original_payload.data = \ - (uint8_t *) cfl_sds_create_len(input_instance->original_payload, - cfl_sds_len(input_instance->original_payload)); - - if (otlp_profile_container->original_payload.data == NULL) { - destroy_profile_container(otlp_profile_container); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_profile_container->original_payload.len = cfl_sds_len(input_instance->original_payload); - } - - result = pack_cprof_profile(&otlp_profile_container->profile, input_instance); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_profile_container(otlp_profile_container); - - return result; - } - - *output_instance = otlp_profile_container; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - - static int pack_cprof_scope_profiles( Opentelemetry__Proto__Profiles__V1development__ScopeProfiles **output_instance, struct cprof_scope_profiles *input_instance) @@ -2199,7 +1756,7 @@ static int pack_cprof_scope_profiles( iterator, struct cprof_profile, _head); - result = pack_cprof_profile_container( + result = pack_cprof_profile( &otlp_scope_profiles->profiles[index], profile); diff --git a/src/cprof_opentelemetry_variant_helpers.c b/src/cprof_opentelemetry_variant_helpers.c index d1b3070..d0304d7 100644 --- a/src/cprof_opentelemetry_variant_helpers.c +++ b/src/cprof_opentelemetry_variant_helpers.c @@ -15,6 +15,12 @@ static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, Opentelemetry__Proto__Common__V1__KeyValue **source, size_t source_length); +static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **source, + size_t source_length, + char **string_table, + size_t string_table_len); + static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source) { @@ -185,3 +191,41 @@ static int clone_kvlist_entry(struct cfl_kvlist *target, return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } + +static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **source, + size_t source_length, + char **string_table, + size_t string_table_len) +{ + size_t index; + const char *key; + struct cfl_variant *val; + + for (index = 0; index < source_length; index++) { + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry = source[index]; + + if (entry == NULL) { + continue; + } + + key = ""; + if (string_table != NULL && entry->key_strindex >= 0 && + (size_t)entry->key_strindex < string_table_len && + string_table[entry->key_strindex] != NULL) { + key = string_table[entry->key_strindex]; + } + + val = clone_variant(entry->value); + if (val == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + if (cfl_kvlist_insert(target, (char *)key, val) != 0) { + cfl_variant_destroy(val); + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} diff --git a/tests/opentelemetry_transcoder.c b/tests/opentelemetry_transcoder.c index 7c59e25..fa25e5d 100644 --- a/tests/opentelemetry_transcoder.c +++ b/tests/opentelemetry_transcoder.c @@ -21,11 +21,125 @@ #include "cprof_tests.h" #include #include +#include #include #include #include +/* + * Build a minimal cprof (resource_profiles -> scope_profiles -> profile with one sample) + * so we can test OTLP encode then decode round-trip without depending on old wire data. + */ +static struct cprof *create_minimal_cprof(void) +{ + struct cprof *cprof; + struct cprof_resource_profiles *resource_profiles; + struct cprof_resource *resource; + struct cprof_scope_profiles *scope_profiles; + struct cprof_profile *profile; + struct cprof_sample *sample; + struct cfl_kvlist *attrs; + size_t id; + int ret; + + cprof = cprof_create(); + if (cprof == NULL) { + return NULL; + } + + resource_profiles = cprof_resource_profiles_create(""); + if (resource_profiles == NULL) { + cprof_destroy(cprof); + return NULL; + } + + attrs = cfl_kvlist_create(); + if (attrs == NULL) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + resource = cprof_resource_create(attrs); + if (resource == NULL) { + cfl_kvlist_destroy(attrs); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + resource_profiles->resource = resource; + + scope_profiles = cprof_scope_profiles_create(resource_profiles, ""); + if (scope_profiles == NULL) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + scope_profiles->scope = cprof_instrumentation_scope_create("", "", NULL, 0); + if (scope_profiles->scope == NULL) { + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + profile = cprof_profile_create(); + if (profile == NULL) { + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + profile->time_nanos = 1000000000ULL; + profile->duration_nanos = 100000000ULL; + + cprof_sample_type_str_create(profile, "count", "", CPROF_AGGREGATION_TEMPORALITY_CUMULATIVE); + id = cprof_profile_string_add(profile, "main", -1); + if (id == 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + sample = cprof_sample_create(profile); + if (sample == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + ret = cprof_sample_add_location_index(sample, id); + if (ret != 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + ret = cprof_sample_add_value(sample, 1); + if (ret != 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + cfl_list_add(&profile->_head, &scope_profiles->profiles); + + ret = cprof_resource_profiles_add(cprof, resource_profiles); + if (ret != 0) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + return cprof; +} + /* unsigned char encoded_packet[] = { 0x0A, 0x9F, 0x11, 0x0A, 0x72, 0x0A, 0x0E, 0x0A, 0x07, 0x68, 0x6F, 0x73, 0x74, 0x2E, 0x69, 0x64, 0x12, 0x03, 0x0A, 0x01, 0x30, 0x0A, 0x16, 0x0A, 0x07, 0x68, 0x6F, 0x73, 0x74, 0x2E, \ 0x69, 0x70, 0x12, 0x0B, 0x0A, 0x09, 0x31, 0x32, 0x37, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x31, 0x0A, 0x1B, 0x0A, 0x09, 0x68, 0x6F, 0x73, 0x74, 0x2E, 0x6E, 0x61, 0x6D, 0x65, 0x12, 0x0E, \ @@ -104,243 +218,63 @@ unsigned char encoded_packet[] = { 0x0A, 0x9F, 0x11, 0x0A, 0x72, 0x0A, 0x0E, 0x }; */ -unsigned char serialized_data[] = { - 0x0A, 0x9F, 0x11, 0x0A, 0x72, 0x0A, 0x0E, 0x0A, 0x07, 0x68, 0x6F, 0x73, - 0x74, 0x2E, 0x69, 0x64, 0x12, 0x03, 0x0A, 0x01, 0x30, 0x0A, 0x16, 0x0A, - 0x07, 0x68, 0x6F, 0x73, 0x74, 0x2E, 0x69, 0x70, 0x12, 0x0B, 0x0A, 0x09, - 0x31, 0x32, 0x37, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x31, 0x0A, 0x1B, 0x0A, - 0x09, 0x68, 0x6F, 0x73, 0x74, 0x2E, 0x6E, 0x61, 0x6D, 0x65, 0x12, 0x0E, - 0x0A, 0x0C, 0x6C, 0x69, 0x6D, 0x61, 0x2D, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6C, 0x74, 0x0A, 0x15, 0x0A, 0x0F, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2E, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x12, 0x02, 0x0A, - 0x00, 0x0A, 0x14, 0x0A, 0x09, 0x6F, 0x73, 0x2E, 0x6B, 0x65, 0x72, 0x6E, - 0x65, 0x6C, 0x12, 0x07, 0x0A, 0x05, 0x36, 0x2E, 0x35, 0x2E, 0x30, 0x12, - 0xA8, 0x10, 0x0A, 0x00, 0x12, 0xA3, 0x10, 0x0A, 0x10, 0xA4, 0x58, 0x33, - 0xE6, 0x18, 0xE6, 0x34, 0x8D, 0xD6, 0xFE, 0x00, 0x58, 0x56, 0xF5, 0xA7, - 0x54, 0x11, 0x69, 0x39, 0xAC, 0x2C, 0x53, 0xC7, 0x04, 0x18, 0x19, 0x69, - 0x39, 0xAC, 0x2C, 0x53, 0xC7, 0x04, 0x18, 0x42, 0xFC, 0x0F, 0x0A, 0x04, - 0x08, 0x01, 0x10, 0x02, 0x12, 0x16, 0x12, 0x01, 0x01, 0x40, 0x05, 0x48, - 0x05, 0x52, 0x02, 0x01, 0x02, 0x6A, 0x09, 0xF6, 0xEA, 0xCA, 0xD2, 0xAC, - 0xEA, 0xB1, 0x82, 0x18, 0x12, 0x18, 0x12, 0x01, 0x01, 0x38, 0x05, 0x40, - 0x0E, 0x48, 0x07, 0x52, 0x02, 0x06, 0x07, 0x6A, 0x09, 0xA8, 0xD3, 0xE3, - 0x8E, 0xB1, 0xEA, 0xB1, 0x82, 0x18, 0x12, 0x18, 0x12, 0x01, 0x01, 0x38, - 0x13, 0x40, 0x12, 0x48, 0x09, 0x52, 0x02, 0x09, 0x0A, 0x6A, 0x09, 0xE9, - 0xF2, 0xB0, 0xE5, 0xB2, 0xEA, 0xB1, 0x82, 0x18, 0x1A, 0x04, 0x30, 0x06, - 0x58, 0x01, 0x1A, 0x10, 0x10, 0x80, 0x80, 0x80, 0x02, 0x18, 0x80, 0x80, - 0xF4, 0x08, 0x28, 0x08, 0x62, 0x02, 0x04, 0x05, 0x1A, 0x0E, 0x10, 0x80, - 0x80, 0x04, 0x18, 0x80, 0xC0, 0xFB, 0x07, 0x28, 0x0A, 0x62, 0x01, 0x08, - 0x22, 0x0B, 0x18, 0xDB, 0xC1, 0x43, 0x22, 0x02, 0x08, 0x01, 0x3A, 0x01, - 0x00, 0x22, 0x0C, 0x18, 0xC7, 0x8B, 0xD2, 0x0A, 0x22, 0x02, 0x08, 0x02, - 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, 0xB7, 0x94, 0x3A, 0x22, 0x02, 0x08, - 0x03, 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, 0xF7, 0x96, 0x3D, 0x22, 0x02, - 0x08, 0x04, 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, 0x87, 0xFA, 0x01, 0x22, - 0x02, 0x08, 0x05, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xC3, 0xD6, 0xA1, - 0x02, 0x22, 0x02, 0x08, 0x06, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xBB, - 0xB5, 0xB3, 0x02, 0x22, 0x02, 0x08, 0x07, 0x3A, 0x01, 0x00, 0x22, 0x0C, - 0x18, 0xE7, 0xB7, 0xB3, 0x02, 0x22, 0x02, 0x08, 0x08, 0x3A, 0x01, 0x00, - 0x22, 0x0C, 0x18, 0x93, 0xD5, 0xB9, 0x01, 0x22, 0x02, 0x08, 0x09, 0x3A, - 0x01, 0x00, 0x22, 0x0C, 0x18, 0xD7, 0x8C, 0xB8, 0x01, 0x22, 0x02, 0x08, - 0x0A, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0x97, 0x93, 0xBA, 0x01, 0x22, - 0x02, 0x08, 0x0B, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0x9F, 0xA8, 0xBA, - 0x01, 0x22, 0x02, 0x08, 0x0C, 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, 0xEB, - 0xFA, 0x08, 0x22, 0x02, 0x08, 0x0D, 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, - 0xD3, 0xFE, 0x08, 0x22, 0x02, 0x08, 0x0E, 0x3A, 0x01, 0x00, 0x22, 0x0B, - 0x18, 0xB7, 0xFF, 0x08, 0x22, 0x02, 0x08, 0x0F, 0x3A, 0x01, 0x00, 0x22, - 0x0C, 0x18, 0xA7, 0xBD, 0xCF, 0x0A, 0x22, 0x02, 0x08, 0x10, 0x3A, 0x01, - 0x00, 0x22, 0x0C, 0x18, 0xCF, 0xCA, 0xCF, 0x0A, 0x22, 0x02, 0x08, 0x11, - 0x3A, 0x01, 0x00, 0x22, 0x0A, 0x18, 0xC7, 0x3C, 0x22, 0x02, 0x08, 0x12, - 0x3A, 0x01, 0x00, 0x22, 0x0A, 0x10, 0x01, 0x18, 0x9F, 0xA6, 0x81, 0x02, - 0x3A, 0x01, 0x03, 0x22, 0x0C, 0x18, 0x9F, 0xCD, 0xA9, 0x02, 0x22, 0x02, - 0x08, 0x13, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xC3, 0xDF, 0xAC, 0x02, - 0x22, 0x02, 0x08, 0x14, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xB3, 0x9F, - 0xAD, 0x02, 0x22, 0x02, 0x08, 0x15, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, - 0x9B, 0xE1, 0xA0, 0x02, 0x22, 0x02, 0x08, 0x16, 0x3A, 0x01, 0x00, 0x22, - 0x0C, 0x18, 0xCB, 0xE8, 0x86, 0x09, 0x22, 0x02, 0x08, 0x17, 0x3A, 0x01, - 0x00, 0x22, 0x0C, 0x18, 0xEB, 0xE3, 0x87, 0x09, 0x22, 0x02, 0x08, 0x18, - 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xFB, 0xAE, 0xFE, 0x09, 0x22, 0x02, - 0x08, 0x19, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xE7, 0xB0, 0xFE, 0x09, - 0x22, 0x02, 0x08, 0x1A, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xEF, 0x90, - 0x85, 0x09, 0x22, 0x02, 0x08, 0x1B, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, - 0xF7, 0xF3, 0x85, 0x09, 0x22, 0x02, 0x08, 0x1C, 0x3A, 0x01, 0x00, 0x22, - 0x0C, 0x18, 0xCB, 0xF5, 0x85, 0x09, 0x22, 0x02, 0x08, 0x1D, 0x3A, 0x01, - 0x00, 0x22, 0x0B, 0x18, 0xEB, 0xFA, 0x08, 0x22, 0x02, 0x08, 0x0D, 0x3A, - 0x01, 0x00, 0x22, 0x0B, 0x18, 0xD3, 0xFE, 0x08, 0x22, 0x02, 0x08, 0x0E, - 0x3A, 0x01, 0x00, 0x22, 0x0B, 0x18, 0xB7, 0xFF, 0x08, 0x22, 0x02, 0x08, - 0x0F, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xA7, 0xBD, 0xCF, 0x0A, 0x22, - 0x02, 0x08, 0x10, 0x3A, 0x01, 0x00, 0x22, 0x0C, 0x18, 0xCF, 0xCA, 0xCF, - 0x0A, 0x22, 0x02, 0x08, 0x11, 0x3A, 0x01, 0x00, 0x22, 0x0A, 0x18, 0xC7, - 0x3C, 0x22, 0x02, 0x08, 0x12, 0x3A, 0x01, 0x00, 0x22, 0x09, 0x10, 0x02, - 0x18, 0xAF, 0xBF, 0x05, 0x3A, 0x01, 0x03, 0x2A, 0x00, 0x2A, 0x02, 0x10, - 0x18, 0x2A, 0x02, 0x10, 0x10, 0x2A, 0x02, 0x10, 0x24, 0x2A, 0x02, 0x10, - 0x19, 0x2A, 0x02, 0x10, 0x21, 0x2A, 0x02, 0x10, 0x1A, 0x2A, 0x02, 0x10, - 0x25, 0x2A, 0x02, 0x10, 0x0B, 0x2A, 0x02, 0x10, 0x26, 0x2A, 0x02, 0x10, - 0x11, 0x2A, 0x02, 0x10, 0x14, 0x2A, 0x02, 0x10, 0x15, 0x2A, 0x02, 0x10, - 0x12, 0x2A, 0x02, 0x10, 0x22, 0x2A, 0x02, 0x10, 0x16, 0x2A, 0x02, 0x10, - 0x0E, 0x2A, 0x02, 0x10, 0x1B, 0x2A, 0x02, 0x10, 0x1E, 0x2A, 0x02, 0x10, - 0x17, 0x2A, 0x02, 0x10, 0x27, 0x2A, 0x02, 0x10, 0x0C, 0x2A, 0x02, 0x10, - 0x13, 0x2A, 0x02, 0x10, 0x1F, 0x2A, 0x02, 0x10, 0x23, 0x2A, 0x02, 0x10, - 0x0F, 0x2A, 0x02, 0x10, 0x1C, 0x2A, 0x02, 0x10, 0x0D, 0x2A, 0x02, 0x10, - 0x20, 0x2A, 0x02, 0x10, 0x1D, 0x32, 0x00, 0x32, 0x07, 0x73, 0x61, 0x6D, - 0x70, 0x6C, 0x65, 0x73, 0x32, 0x05, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x32, - 0x03, 0x63, 0x70, 0x75, 0x32, 0x0B, 0x6E, 0x61, 0x6E, 0x6F, 0x73, 0x65, - 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x32, 0x16, 0x41, 0x54, 0x73, 0x51, 0x46, - 0x31, 0x72, 0x72, 0x49, 0x63, 0x30, 0x35, 0x74, 0x65, 0x6A, 0x66, 0x79, - 0x65, 0x73, 0x5A, 0x52, 0x77, 0x32, 0x20, 0x62, 0x61, 0x31, 0x63, 0x39, - 0x61, 0x35, 0x61, 0x38, 0x32, 0x35, 0x36, 0x61, 0x36, 0x65, 0x66, 0x33, - 0x65, 0x36, 0x36, 0x33, 0x63, 0x63, 0x37, 0x33, 0x31, 0x36, 0x38, 0x30, - 0x33, 0x64, 0x39, 0x32, 0x16, 0x7A, 0x6A, 0x32, 0x51, 0x75, 0x43, 0x52, - 0x61, 0x70, 0x75, 0x6E, 0x30, 0x32, 0x73, 0x79, 0x68, 0x6C, 0x41, 0x48, - 0x71, 0x54, 0x77, 0x32, 0x0D, 0x65, 0x62, 0x70, 0x66, 0x2D, 0x70, 0x72, - 0x6F, 0x66, 0x69, 0x6C, 0x65, 0x72, 0x32, 0x16, 0x36, 0x34, 0x41, 0x49, - 0x75, 0x76, 0x39, 0x69, 0x70, 0x47, 0x42, 0x41, 0x41, 0x64, 0x30, 0x52, - 0x5A, 0x6D, 0x76, 0x4C, 0x4C, 0x77, 0x32, 0x0A, 0x63, 0x6F, 0x6E, 0x74, - 0x61, 0x69, 0x6E, 0x65, 0x72, 0x64, 0x32, 0x13, 0x5F, 0x5F, 0x63, 0x68, - 0x65, 0x63, 0x6B, 0x5F, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x5F, 0x73, - 0x69, 0x7A, 0x65, 0x32, 0x11, 0x6F, 0x62, 0x6A, 0x5F, 0x63, 0x67, 0x72, - 0x6F, 0x75, 0x70, 0x5F, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x32, 0x0D, - 0x5F, 0x5F, 0x73, 0x6F, 0x63, 0x6B, 0x5F, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x32, 0x07, 0x65, 0x6C, 0x30, 0x5F, 0x73, 0x76, 0x63, 0x32, 0x0C, - 0x75, 0x6E, 0x69, 0x78, 0x5F, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x31, - 0x32, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6C, 0x65, 0x32, 0x10, - 0x62, 0x70, 0x66, 0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x64, 0x6F, 0x5F, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x32, 0x0E, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, - 0x5F, 0x73, 0x79, 0x73, 0x63, 0x61, 0x6C, 0x6C, 0x32, 0x10, 0x6B, 0x6D, - 0x65, 0x6D, 0x5F, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5F, 0x61, 0x6C, 0x6C, - 0x6F, 0x63, 0x32, 0x09, 0x5F, 0x5F, 0x73, 0x79, 0x73, 0x5F, 0x62, 0x70, - 0x66, 0x32, 0x0F, 0x5F, 0x5F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x5F, 0x73, - 0x79, 0x73, 0x5F, 0x62, 0x70, 0x66, 0x32, 0x0A, 0x64, 0x6F, 0x5F, 0x65, - 0x6C, 0x30, 0x5F, 0x73, 0x76, 0x63, 0x32, 0x19, 0x70, 0x72, 0x6F, 0x70, - 0x61, 0x67, 0x61, 0x74, 0x65, 0x5F, 0x70, 0x72, 0x6F, 0x74, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x5F, 0x75, 0x73, 0x61, 0x67, 0x65, 0x32, 0x19, 0x66, - 0x69, 0x6E, 0x69, 0x73, 0x68, 0x5F, 0x74, 0x61, 0x73, 0x6B, 0x5F, 0x73, - 0x77, 0x69, 0x74, 0x63, 0x68, 0x2E, 0x69, 0x73, 0x72, 0x61, 0x2E, 0x30, - 0x32, 0x07, 0x6B, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x32, 0x13, 0x5F, - 0x5F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F, - 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x32, 0x14, 0x65, 0x6C, 0x30, 0x74, - 0x5F, 0x36, 0x34, 0x5F, 0x73, 0x79, 0x6E, 0x63, 0x5F, 0x68, 0x61, 0x6E, - 0x64, 0x6C, 0x65, 0x72, 0x32, 0x0B, 0x75, 0x6E, 0x69, 0x78, 0x5F, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x32, 0x12, 0x5F, 0x5F, 0x61, 0x72, 0x6D, - 0x36, 0x34, 0x5F, 0x73, 0x79, 0x73, 0x5F, 0x73, 0x6F, 0x63, 0x6B, 0x65, - 0x74, 0x32, 0x0C, 0x65, 0x6C, 0x30, 0x74, 0x5F, 0x36, 0x34, 0x5F, 0x73, - 0x79, 0x6E, 0x63, 0x32, 0x0D, 0x73, 0x6B, 0x5F, 0x70, 0x72, 0x6F, 0x74, - 0x5F, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x32, 0x0C, 0x5F, 0x5F, 0x73, 0x79, - 0x73, 0x5F, 0x73, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x32, 0x0D, 0x72, 0x65, - 0x74, 0x5F, 0x66, 0x72, 0x6F, 0x6D, 0x5F, 0x66, 0x6F, 0x72, 0x6B, 0x32, - 0x1A, 0x65, 0x6C, 0x30, 0x5F, 0x73, 0x76, 0x63, 0x5F, 0x63, 0x6F, 0x6D, - 0x6D, 0x6F, 0x6E, 0x2E, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x70, 0x72, 0x6F, - 0x70, 0x2E, 0x30, 0x32, 0x08, 0x73, 0x6B, 0x5F, 0x61, 0x6C, 0x6C, 0x6F, - 0x63, 0x32, 0x0D, 0x77, 0x6F, 0x72, 0x6B, 0x65, 0x72, 0x5F, 0x74, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x32, 0x1A, 0x5F, 0x5F, 0x63, 0x68, 0x65, 0x63, - 0x6B, 0x5F, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x5F, 0x73, 0x69, 0x7A, - 0x65, 0x2E, 0x70, 0x61, 0x72, 0x74, 0x2E, 0x30, 0x32, 0x18, 0x67, 0x65, - 0x6E, 0x65, 0x72, 0x69, 0x63, 0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x5F, 0x62, 0x61, 0x74, 0x63, 0x68, 0x32, 0x10, - 0x74, 0x72, 0x79, 0x5F, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5F, 0x6D, - 0x65, 0x6D, 0x63, 0x67, 0x48, 0xE9, 0xF2, 0xB0, 0xE5, 0xB2, 0xEA, 0xB1, - 0x82, 0x18, 0x5A, 0x04, 0x08, 0x03, 0x10, 0x04, 0x60, 0x80, 0xE1, 0xEB, - 0x17, 0x7A, 0x25, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, - 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x82, 0x01, 0x1E, 0x0A, 0x12, 0x70, 0x72, 0x6F, - 0x66, 0x69, 0x6C, 0x65, 0x2E, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x2E, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x08, 0x0A, 0x06, 0x6B, 0x65, 0x72, 0x6E, 0x65, - 0x6C, 0x82, 0x01, 0x13, 0x0A, 0x0C, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, - 0x6E, 0x65, 0x72, 0x2E, 0x69, 0x64, 0x12, 0x03, 0x0A, 0x01, 0x2F, 0x82, - 0x01, 0x1C, 0x0A, 0x0B, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2E, 0x6E, - 0x61, 0x6D, 0x65, 0x12, 0x0D, 0x0A, 0x0B, 0x6B, 0x77, 0x6F, 0x72, 0x6B, - 0x65, 0x72, 0x2F, 0x36, 0x3A, 0x31, 0x82, 0x01, 0x1E, 0x0A, 0x12, 0x70, - 0x72, 0x6F, 0x66, 0x69, 0x6C, 0x65, 0x2E, 0x66, 0x72, 0x61, 0x6D, 0x65, - 0x2E, 0x74, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0A, 0x06, 0x6E, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x82, 0x01, 0x4D, 0x0A, 0x1F, 0x70, 0x72, 0x6F, 0x63, - 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, - 0x6C, 0x65, 0x2E, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x5F, 0x69, 0x64, 0x2E, - 0x67, 0x6E, 0x75, 0x12, 0x2A, 0x0A, 0x28, 0x33, 0x34, 0x31, 0x39, 0x61, - 0x33, 0x65, 0x66, 0x30, 0x30, 0x31, 0x35, 0x34, 0x35, 0x64, 0x38, 0x65, - 0x65, 0x39, 0x66, 0x63, 0x64, 0x34, 0x62, 0x31, 0x38, 0x64, 0x34, 0x63, - 0x65, 0x31, 0x37, 0x32, 0x30, 0x35, 0x37, 0x65, 0x33, 0x33, 0x39, 0x82, - 0x01, 0x4B, 0x0A, 0x25, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x2E, 0x62, - 0x75, 0x69, 0x6C, 0x64, 0x5F, 0x69, 0x64, 0x2E, 0x70, 0x72, 0x6F, 0x66, - 0x69, 0x6C, 0x69, 0x6E, 0x67, 0x12, 0x22, 0x0A, 0x20, 0x37, 0x30, 0x35, - 0x62, 0x64, 0x66, 0x31, 0x34, 0x39, 0x38, 0x30, 0x66, 0x32, 0x63, 0x32, - 0x32, 0x64, 0x63, 0x34, 0x64, 0x65, 0x32, 0x65, 0x36, 0x35, 0x37, 0x36, - 0x65, 0x64, 0x66, 0x66, 0x38, 0x82, 0x01, 0x3C, 0x0A, 0x0C, 0x63, 0x6F, - 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x65, 0x72, 0x2E, 0x69, 0x64, 0x12, 0x2C, - 0x0A, 0x2A, 0x2F, 0x75, 0x73, 0x65, 0x72, 0x2E, 0x73, 0x6C, 0x69, 0x63, - 0x65, 0x2F, 0x75, 0x73, 0x65, 0x72, 0x2D, 0x35, 0x30, 0x31, 0x2E, 0x73, - 0x6C, 0x69, 0x63, 0x65, 0x2F, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, - 0x2D, 0x32, 0x2E, 0x73, 0x63, 0x6F, 0x70, 0x65, 0x82, 0x01, 0x1E, 0x0A, - 0x0B, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2E, 0x6E, 0x61, 0x6D, 0x65, - 0x12, 0x0F, 0x0A, 0x0D, 0x65, 0x62, 0x70, 0x66, 0x2D, 0x70, 0x72, 0x6F, - 0x66, 0x69, 0x6C, 0x65, 0x72, 0x82, 0x01, 0x4B, 0x0A, 0x25, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x61, 0x62, 0x6C, 0x65, 0x2E, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x5F, 0x69, - 0x64, 0x2E, 0x70, 0x72, 0x6F, 0x66, 0x69, 0x6C, 0x69, 0x6E, 0x67, 0x12, - 0x22, 0x0A, 0x20, 0x64, 0x37, 0x38, 0x30, 0x31, 0x36, 0x30, 0x34, 0x65, - 0x38, 0x62, 0x38, 0x39, 0x64, 0x64, 0x61, 0x63, 0x66, 0x65, 0x31, 0x39, - 0x38, 0x39, 0x39, 0x64, 0x36, 0x62, 0x61, 0x33, 0x35, 0x36, 0x34, 0x82, - 0x01, 0x5A, 0x0A, 0x0C, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x65, - 0x72, 0x2E, 0x69, 0x64, 0x12, 0x4A, 0x0A, 0x48, 0x2F, 0x75, 0x73, 0x65, - 0x72, 0x2E, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x2F, 0x75, 0x73, 0x65, 0x72, - 0x2D, 0x35, 0x30, 0x31, 0x2E, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x2F, 0x75, - 0x73, 0x65, 0x72, 0x40, 0x35, 0x30, 0x31, 0x2E, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2F, 0x61, 0x70, 0x70, 0x2E, 0x73, 0x6C, 0x69, 0x63, - 0x65, 0x2F, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x65, 0x72, 0x64, - 0x2E, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x82, 0x01, 0x1B, 0x0A, - 0x0B, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x2E, 0x6E, 0x61, 0x6D, 0x65, - 0x12, 0x0C, 0x0A, 0x0A, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x65, - 0x72, 0x64 -}; +/* Encode minimal cprof to OTLP and check success. */ static void test_encoder() { - cfl_sds_t otlp_encoder_result; + cfl_sds_t otlp_result; struct cprof *context; int result; - size_t offset; - - offset = 0; - context = NULL; - - result = cprof_decode_opentelemetry_create(&context, - serialized_data, - sizeof(serialized_data), - &offset); - TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); - - if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - result = cprof_encode_opentelemetry_create(&otlp_encoder_result, context); - - TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + context = create_minimal_cprof(); + TEST_CHECK(context != NULL); + if (context == NULL) { + return; + } - if (result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - cprof_encode_opentelemetry_destroy(otlp_encoder_result); - } + result = cprof_encode_opentelemetry_create(&otlp_result, context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); - cprof_decode_opentelemetry_destroy(context); + if (result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS && otlp_result != NULL) { + cprof_encode_opentelemetry_destroy(otlp_result); } + cprof_destroy(context); } +/* Round-trip: encode minimal cprof to OTLP, decode it back, assert decode success. */ static void test_decoder() { - struct cprof *context; + cfl_sds_t otlp_result; + struct cprof *encoded_context; + struct cprof *decoded_context; int result; size_t offset; - offset = 0; - context = NULL; + encoded_context = create_minimal_cprof(); + TEST_CHECK(encoded_context != NULL); + if (encoded_context == NULL) { + return; + } - result = cprof_decode_opentelemetry_create(&context, - serialized_data, - sizeof(serialized_data), - &offset); + result = cprof_encode_opentelemetry_create(&otlp_result, encoded_context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + cprof_destroy(encoded_context); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS || otlp_result == NULL) { + return; + } + offset = 0; + decoded_context = NULL; + result = cprof_decode_opentelemetry_create(&decoded_context, + (unsigned char *) otlp_result, + cfl_sds_len(otlp_result), + &offset); TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); - if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - cprof_decode_opentelemetry_destroy(context); + if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && decoded_context != NULL) { + cprof_decode_opentelemetry_destroy(decoded_context); } + cprof_encode_opentelemetry_destroy(otlp_result); } TEST_LIST = { From aa98511b2d91cc94cebc7b3cc4a66c4ecd3daa72 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 6 Feb 2026 12:30:44 -0600 Subject: [PATCH 4/8] lib: fluent-otel-proto: update to v0.11.0 (otel v1.9.0) Signed-off-by: Eduardo Silva --- lib/fluent-otel-proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fluent-otel-proto b/lib/fluent-otel-proto index afb7125..8379fe4 160000 --- a/lib/fluent-otel-proto +++ b/lib/fluent-otel-proto @@ -1 +1 @@ -Subproject commit afb71250cceb9f54d679d7bb84894b9074976bf8 +Subproject commit 8379fe405e46167a8334b27c4ff37f4106f20d67 From 27b8f57bff0d4ea24eac00cd1952f535b1ede472 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 6 Feb 2026 12:33:30 -0600 Subject: [PATCH 5/8] encode_opentelemetry: adjust to new profiles schema v1.9.0 Signed-off-by: Eduardo Silva --- src/cprof_encode_opentelemetry.c | 1151 ++++++++++++++++++++++-------- 1 file changed, 864 insertions(+), 287 deletions(-) diff --git a/src/cprof_encode_opentelemetry.c b/src/cprof_encode_opentelemetry.c index 2d19ed3..2a86fb7 100644 --- a/src/cprof_encode_opentelemetry.c +++ b/src/cprof_encode_opentelemetry.c @@ -20,6 +20,7 @@ #include #include +#include static int is_string_releaseable(char *address) { @@ -895,8 +896,8 @@ static void destroy_profiles_dictionary( } if (dict->string_table != NULL) { for (index = 0; index < dict->n_string_table; index++) { - if (dict->string_table[index] != NULL && is_string_releaseable(dict->string_table[index])) { - free(dict->string_table[index]); + if (dict->string_table[index] != NULL) { + cfl_sds_destroy((cfl_sds_t) dict->string_table[index]); } } free(dict->string_table); @@ -939,8 +940,745 @@ static void destroy_export_profiles_service_request( } } +/* + * Per-profile encoding state: maps profile-local indices to dictionary indices. + * Used when packing so ValueType.type_strindex, Sample.stack_index, etc. point into the dictionary. + */ +struct profile_encoding_state { + int32_t *string_map; /* profile string_table index -> dict string index */ + size_t string_map_count; + int32_t *mapping_map; /* profile mapping index -> dict mapping index */ + size_t mapping_map_count; + int32_t *function_map; + size_t function_map_count; + int32_t *location_map; + size_t location_map_count; + int32_t *link_map; + size_t link_map_count; + int32_t *stack_index_by_sample; /* sample index -> dict stack index */ + size_t sample_count; +}; + +static void free_profile_encoding_state(struct profile_encoding_state *s) +{ + if (s == NULL) { + return; + } + free(s->string_map); + free(s->mapping_map); + free(s->function_map); + free(s->location_map); + free(s->link_map); + free(s->stack_index_by_sample); +} + +/* Internal context passed through pack_* to access dictionary encoding state per profile */ +typedef struct { + struct cprof_opentelemetry_encoding_context *pub; + struct profile_encoding_state *encoding_states; + size_t encoding_states_count; + size_t current_profile_index; +} encoder_internal_ctx_t; + +/* Find or add string in dictionary; returns dict string index. */ +static int32_t dict_add_string( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + const char *str) +{ + size_t i; + char *dup; + + if (str == NULL) { + str = ""; + } + for (i = 0; i < dict->n_string_table; i++) { + if (dict->string_table[i] != NULL && strcmp(dict->string_table[i], str) == 0) { + return (int32_t) i; + } + } + dup = cfl_sds_create(str); + if (dup == NULL) { + return -1; + } + dict->string_table = realloc(dict->string_table, + (dict->n_string_table + 1) * sizeof(char *)); + if (dict->string_table == NULL) { + cfl_sds_destroy(dup); + return -1; + } + dict->string_table[dict->n_string_table] = dup; + return (int32_t) dict->n_string_table++; +} + +/* Find or add stack (location_indices) in dictionary; returns dict stack index. */ +static int32_t dict_add_stack( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + const int32_t *location_indices, + size_t n_location_indices) +{ + size_t i; + size_t j; + Opentelemetry__Proto__Profiles__V1development__Stack **stacks; + Opentelemetry__Proto__Profiles__V1development__Stack *stack; + + for (i = 0; i < dict->n_stack_table; i++) { + if (dict->stack_table[i]->n_location_indices != n_location_indices) { + continue; + } + for (j = 0; j < n_location_indices; j++) { + if (dict->stack_table[i]->location_indices[j] != location_indices[j]) { + break; + } + } + if (j == n_location_indices) { + return (int32_t) i; + } + } + stack = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Stack)); + if (stack == NULL) { + return -1; + } + opentelemetry__proto__profiles__v1development__stack__init(stack); + if (n_location_indices > 0) { + stack->location_indices = malloc(n_location_indices * sizeof(int32_t)); + if (stack->location_indices == NULL) { + free(stack); + return -1; + } + memcpy(stack->location_indices, location_indices, n_location_indices * sizeof(int32_t)); + stack->n_location_indices = n_location_indices; + } + stacks = realloc(dict->stack_table, + (dict->n_stack_table + 1) * sizeof(Opentelemetry__Proto__Profiles__V1development__Stack *)); + if (stacks == NULL) { + free(stack->location_indices); + free(stack); + return -1; + } + dict->stack_table = stacks; + dict->stack_table[dict->n_stack_table] = stack; + return (int32_t) dict->n_stack_table++; +} + +/* Build OTLP Mapping from cprof_mapping; caller must destroy. Uses string_map for filename_strindex. */ +static Opentelemetry__Proto__Profiles__V1development__Mapping * +dict_build_mapping(struct cprof_mapping *m, + const int32_t *string_map, + size_t string_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Mapping *otlp; + + otlp = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Mapping)); + if (otlp == NULL) { + return NULL; + } + opentelemetry__proto__profiles__v1development__mapping__init(otlp); + otlp->memory_start = m->memory_start; + otlp->memory_limit = m->memory_limit; + otlp->file_offset = m->file_offset; + if (m->filename >= 0 && (size_t)m->filename < string_map_count) { + otlp->filename_strindex = string_map[m->filename]; + } + else { + otlp->filename_strindex = 0; + } + /* attribute_indices reference dict attribute_table; leave 0 for now */ + return otlp; +} + +/* Compare two OTLP Mappings (excluding attribute_indices). */ +static int mapping_equal(const Opentelemetry__Proto__Profiles__V1development__Mapping *a, + const Opentelemetry__Proto__Profiles__V1development__Mapping *b) +{ + return a->memory_start == b->memory_start && + a->memory_limit == b->memory_limit && + a->file_offset == b->file_offset && + a->filename_strindex == b->filename_strindex; +} + +/* Find or add Mapping in dictionary; returns dict mapping index or -1 on error. */ +static int32_t dict_add_mapping( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + struct cprof_mapping *m, + const int32_t *string_map, + size_t string_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Mapping *otlp; + Opentelemetry__Proto__Profiles__V1development__Mapping **tab; + size_t i; + + otlp = dict_build_mapping(m, string_map, string_map_count); + if (otlp == NULL) { + return -1; + } + for (i = 0; i < dict->n_mapping_table; i++) { + if (mapping_equal(dict->mapping_table[i], otlp)) { + destroy_mapping(otlp); + return (int32_t) i; + } + } + tab = realloc(dict->mapping_table, + (dict->n_mapping_table + 1) * sizeof(Opentelemetry__Proto__Profiles__V1development__Mapping *)); + if (tab == NULL) { + destroy_mapping(otlp); + return -1; + } + dict->mapping_table = tab; + dict->mapping_table[dict->n_mapping_table] = otlp; + return (int32_t) dict->n_mapping_table++; +} + +/* Build OTLP Function from cprof_function; caller must destroy. */ +static Opentelemetry__Proto__Profiles__V1development__Function * +dict_build_function(struct cprof_function *f, + const int32_t *string_map, + size_t string_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Function *otlp; + + otlp = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Function)); + if (otlp == NULL) { + return NULL; + } + opentelemetry__proto__profiles__v1development__function__init(otlp); + if (f->name >= 0 && (size_t)f->name < string_map_count) { + otlp->name_strindex = string_map[f->name]; + } + else { + otlp->name_strindex = 0; + } + if (f->system_name >= 0 && (size_t)f->system_name < string_map_count) { + otlp->system_name_strindex = string_map[f->system_name]; + } + else { + otlp->system_name_strindex = 0; + } + if (f->filename >= 0 && (size_t)f->filename < string_map_count) { + otlp->filename_strindex = string_map[f->filename]; + } + else { + otlp->filename_strindex = 0; + } + otlp->start_line = f->start_line; + return otlp; +} + +static int function_equal(const Opentelemetry__Proto__Profiles__V1development__Function *a, + const Opentelemetry__Proto__Profiles__V1development__Function *b) +{ + return a->name_strindex == b->name_strindex && + a->system_name_strindex == b->system_name_strindex && + a->filename_strindex == b->filename_strindex && + a->start_line == b->start_line; +} + +static int32_t dict_add_function( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + struct cprof_function *f, + const int32_t *string_map, + size_t string_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Function *otlp; + Opentelemetry__Proto__Profiles__V1development__Function **tab; + size_t i; + + otlp = dict_build_function(f, string_map, string_map_count); + if (otlp == NULL) { + return -1; + } + for (i = 0; i < dict->n_function_table; i++) { + if (function_equal(dict->function_table[i], otlp)) { + destroy_function(otlp); + return (int32_t) i; + } + } + tab = realloc(dict->function_table, + (dict->n_function_table + 1) * sizeof(Opentelemetry__Proto__Profiles__V1development__Function *)); + if (tab == NULL) { + destroy_function(otlp); + return -1; + } + dict->function_table = tab; + dict->function_table[dict->n_function_table] = otlp; + return (int32_t) dict->n_function_table++; +} + +static Opentelemetry__Proto__Profiles__V1development__Location *initialize_location(size_t line_count, size_t attribute_count); +static Opentelemetry__Proto__Profiles__V1development__Link *initialize_link(void); + +/* Build OTLP Location from cprof_location; uses mapping_map and function_map for indices. Caller must destroy. */ +static Opentelemetry__Proto__Profiles__V1development__Location * +dict_build_location(struct cprof_location *loc, + const int32_t *mapping_map, + size_t mapping_map_count, + const int32_t *function_map, + size_t function_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Location *otlp; + struct cfl_list *line_iter; + struct cprof_line *line; + size_t n_lines; + size_t idx; + + n_lines = cfl_list_size(&loc->lines); + otlp = initialize_location(n_lines, 0); + if (otlp == NULL) { + return NULL; + } + if (loc->mapping_index < mapping_map_count) { + otlp->mapping_index = mapping_map[loc->mapping_index]; + } + else { + otlp->mapping_index = 0; + } + otlp->address = loc->address; + + idx = 0; + cfl_list_foreach(line_iter, &loc->lines) { + line = cfl_list_entry(line_iter, struct cprof_line, _head); + otlp->lines[idx] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Line)); + if (otlp->lines[idx] == NULL) { + destroy_location(otlp); + return NULL; + } + opentelemetry__proto__profiles__v1development__line__init(otlp->lines[idx]); + if ((size_t)line->function_index < function_map_count) { + otlp->lines[idx]->function_index = function_map[line->function_index]; + } + else { + otlp->lines[idx]->function_index = 0; + } + otlp->lines[idx]->line = line->line; + otlp->lines[idx]->column = line->column; + idx++; + } + + return otlp; +} + +static int location_equal(const Opentelemetry__Proto__Profiles__V1development__Location *a, + const Opentelemetry__Proto__Profiles__V1development__Location *b) +{ + size_t i; + + if (a->mapping_index != b->mapping_index || a->address != b->address || a->n_lines != b->n_lines) { + return 0; + } + for (i = 0; i < a->n_lines; i++) { + if (a->lines[i]->function_index != b->lines[i]->function_index || + a->lines[i]->line != b->lines[i]->line || + a->lines[i]->column != b->lines[i]->column) { + return 0; + } + } + return 1; +} + +static int32_t dict_add_location( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + struct cprof_location *loc, + const int32_t *mapping_map, + size_t mapping_map_count, + const int32_t *function_map, + size_t function_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Location *otlp; + Opentelemetry__Proto__Profiles__V1development__Location **tab; + size_t i; + + otlp = dict_build_location(loc, mapping_map, mapping_map_count, function_map, function_map_count); + if (otlp == NULL) { + return -1; + } + for (i = 0; i < dict->n_location_table; i++) { + if (location_equal(dict->location_table[i], otlp)) { + destroy_location(otlp); + return (int32_t) i; + } + } + tab = realloc(dict->location_table, + (dict->n_location_table + 1) * sizeof(Opentelemetry__Proto__Profiles__V1development__Location *)); + if (tab == NULL) { + destroy_location(otlp); + return -1; + } + dict->location_table = tab; + dict->location_table[dict->n_location_table] = otlp; + return (int32_t) dict->n_location_table++; +} + +/* Build OTLP Link from cprof_link; caller must destroy. */ +static Opentelemetry__Proto__Profiles__V1development__Link * +dict_build_link(struct cprof_link *l) +{ + Opentelemetry__Proto__Profiles__V1development__Link *otlp; + + otlp = initialize_link(); + if (otlp == NULL) { + return NULL; + } + otlp->trace_id.data = (uint8_t *) cfl_sds_create_len((const char *) l->trace_id, sizeof(l->trace_id)); + if (otlp->trace_id.data == NULL) { + destroy_link(otlp); + return NULL; + } + otlp->trace_id.len = sizeof(l->trace_id); + otlp->span_id.data = (uint8_t *) cfl_sds_create_len((const char *) l->span_id, sizeof(l->span_id)); + if (otlp->span_id.data == NULL) { + destroy_link(otlp); + return NULL; + } + otlp->span_id.len = sizeof(l->span_id); + return otlp; +} + +static int link_equal(const Opentelemetry__Proto__Profiles__V1development__Link *a, + const Opentelemetry__Proto__Profiles__V1development__Link *b) +{ + if (a->trace_id.len != b->trace_id.len || a->span_id.len != b->span_id.len) { + return 0; + } + return memcmp(a->trace_id.data, b->trace_id.data, a->trace_id.len) == 0 && + memcmp(a->span_id.data, b->span_id.data, a->span_id.len) == 0; +} + +static int32_t dict_add_link( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + struct cprof_link *l) +{ + Opentelemetry__Proto__Profiles__V1development__Link *otlp; + Opentelemetry__Proto__Profiles__V1development__Link **tab; + size_t i; + + otlp = dict_build_link(l); + if (otlp == NULL) { + return -1; + } + for (i = 0; i < dict->n_link_table; i++) { + if (link_equal(dict->link_table[i], otlp)) { + destroy_link(otlp); + return (int32_t) i; + } + } + tab = realloc(dict->link_table, + (dict->n_link_table + 1) * sizeof(Opentelemetry__Proto__Profiles__V1development__Link *)); + if (tab == NULL) { + destroy_link(otlp); + return -1; + } + dict->link_table = tab; + dict->link_table[dict->n_link_table] = otlp; + return (int32_t) dict->n_link_table++; +} + +/* + * Build ProfilesDictionary and per-profile encoding states from the full cprof tree. + * Caller must free encoding_states (and each state's arrays) and destroy the dictionary. + */ +static int build_profiles_dictionary( + struct cprof *cprof, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary **out_dict, + struct profile_encoding_state **out_states, + size_t *out_state_count) +{ + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict; + struct cfl_list *rp_iter; + struct cfl_list *sp_iter; + struct cfl_list *prof_iter; + struct cfl_list *map_iter; + struct cfl_list *func_iter; + struct cfl_list *loc_iter; + struct cfl_list *link_iter; + struct cfl_list *sample_iter; + struct cprof_resource_profiles *rp; + struct cprof_scope_profiles *sp; + struct cprof_profile *profile; + struct cprof_mapping *cprof_mapping; + struct cprof_function *cprof_func; + struct cprof_location *cprof_loc; + struct cprof_link *cprof_link; + struct cprof_sample *sample; + struct profile_encoding_state *states; + size_t state_count; + size_t state_idx; + size_t i; + size_t j; + size_t n_loc; + uint64_t loc_idx; + int32_t si; + int32_t loc_indices_buf[256]; + int32_t *loc_indices; + + state_count = 0; + cfl_list_foreach(rp_iter, &cprof->profiles) { + rp = cfl_list_entry(rp_iter, struct cprof_resource_profiles, _head); + cfl_list_foreach(sp_iter, &rp->scope_profiles) { + sp = cfl_list_entry(sp_iter, struct cprof_scope_profiles, _head); + state_count += cfl_list_size(&sp->profiles); + } + } + if (state_count == 0) { + *out_dict = NULL; + *out_states = NULL; + *out_state_count = 0; + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; + } + + dict = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary)); + if (dict == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__profiles_dictionary__init(dict); + + /* string_table[0] = "" (required) */ + dict->string_table = malloc(sizeof(char *)); + if (dict->string_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->string_table[0] = cfl_sds_create(""); + if (dict->string_table[0] == NULL) { + free(dict->string_table); + free(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->n_string_table = 1; + + /* stack_table[0] = zero Stack (required) */ + dict->stack_table = malloc(sizeof(Opentelemetry__Proto__Profiles__V1development__Stack *)); + if (dict->stack_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->stack_table[0] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Stack)); + if (dict->stack_table[0] == NULL) { + free(dict->stack_table); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__stack__init(dict->stack_table[0]); + dict->n_stack_table = 1; + + /* mapping_table[0] = zero Mapping (required) */ + dict->mapping_table = malloc(sizeof(Opentelemetry__Proto__Profiles__V1development__Mapping *)); + if (dict->mapping_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->mapping_table[0] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Mapping)); + if (dict->mapping_table[0] == NULL) { + free(dict->mapping_table); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__mapping__init(dict->mapping_table[0]); + dict->n_mapping_table = 1; + /* location_table[0] = zero Location (required) */ + dict->location_table = malloc(sizeof(Opentelemetry__Proto__Profiles__V1development__Location *)); + if (dict->location_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->location_table[0] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Location)); + if (dict->location_table[0] == NULL) { + free(dict->location_table); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__location__init(dict->location_table[0]); + dict->n_location_table = 1; + /* function_table[0] = zero Function (required) */ + dict->function_table = malloc(sizeof(Opentelemetry__Proto__Profiles__V1development__Function *)); + if (dict->function_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->function_table[0] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Function)); + if (dict->function_table[0] == NULL) { + free(dict->function_table); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__function__init(dict->function_table[0]); + dict->n_function_table = 1; + + /* link_table[0] = zero Link (required) */ + dict->link_table = malloc(sizeof(Opentelemetry__Proto__Profiles__V1development__Link *)); + if (dict->link_table == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + dict->link_table[0] = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Link)); + if (dict->link_table[0] == NULL) { + free(dict->link_table); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + opentelemetry__proto__profiles__v1development__link__init(dict->link_table[0]); + dict->n_link_table = 1; + + states = calloc(state_count, sizeof(struct profile_encoding_state)); + if (states == NULL) { + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + state_idx = 0; + + cfl_list_foreach(rp_iter, &cprof->profiles) { + rp = cfl_list_entry(rp_iter, struct cprof_resource_profiles, _head); + cfl_list_foreach(sp_iter, &rp->scope_profiles) { + sp = cfl_list_entry(sp_iter, struct cprof_scope_profiles, _head); + cfl_list_foreach(prof_iter, &sp->profiles) { + profile = cfl_list_entry(prof_iter, struct cprof_profile, _head); + struct profile_encoding_state *st = &states[state_idx]; + + /* string_map: profile string_table index -> dict index */ + st->string_map_count = profile->string_table_count; + if (st->string_map_count > 0) { + st->string_map = malloc(st->string_map_count * sizeof(int32_t)); + if (st->string_map == NULL) { + goto fail; + } + for (i = 0; i < st->string_map_count; i++) { + si = dict_add_string(dict, + profile->string_table[i] ? profile->string_table[i] : ""); + if (si < 0) { + goto fail; + } + st->string_map[i] = si; + } + } + + /* stack_index_by_sample: for each sample, resolve location_index[] to dict stack */ + st->sample_count = cfl_list_size(&profile->samples); + if (st->sample_count > 0) { + st->stack_index_by_sample = malloc(st->sample_count * sizeof(int32_t)); + if (st->stack_index_by_sample == NULL) { + goto fail; + } + } + + /* Build mapping_table, function_table, location_table, link_table entries and + * per-profile maps (profile index -> dict index) so stacks reference real locations. */ + st->location_map_count = cfl_list_size(&profile->locations); + st->mapping_map_count = cfl_list_size(&profile->mappings); + st->function_map_count = cfl_list_size(&profile->functions); + st->link_map_count = cfl_list_size(&profile->link_table); + + if (st->mapping_map_count > 0) { + st->mapping_map = malloc(st->mapping_map_count * sizeof(int32_t)); + if (st->mapping_map == NULL) { + goto fail; + } + i = 0; + cfl_list_foreach(map_iter, &profile->mappings) { + cprof_mapping = cfl_list_entry(map_iter, struct cprof_mapping, _head); + si = dict_add_mapping(dict, cprof_mapping, st->string_map, st->string_map_count); + if (si < 0) { + goto fail; + } + st->mapping_map[i++] = si; + } + } + if (st->function_map_count > 0) { + st->function_map = malloc(st->function_map_count * sizeof(int32_t)); + if (st->function_map == NULL) { + goto fail; + } + i = 0; + cfl_list_foreach(func_iter, &profile->functions) { + cprof_func = cfl_list_entry(func_iter, struct cprof_function, _head); + si = dict_add_function(dict, cprof_func, st->string_map, st->string_map_count); + if (si < 0) { + goto fail; + } + st->function_map[i++] = si; + } + } + if (st->location_map_count > 0) { + st->location_map = malloc(st->location_map_count * sizeof(int32_t)); + if (st->location_map == NULL) { + goto fail; + } + i = 0; + cfl_list_foreach(loc_iter, &profile->locations) { + cprof_loc = cfl_list_entry(loc_iter, struct cprof_location, _head); + si = dict_add_location(dict, cprof_loc, + st->mapping_map, st->mapping_map_count, + st->function_map, st->function_map_count); + if (si < 0) { + goto fail; + } + st->location_map[i++] = si; + } + } + if (st->link_map_count > 0) { + st->link_map = malloc(st->link_map_count * sizeof(int32_t)); + if (st->link_map == NULL) { + goto fail; + } + i = 0; + cfl_list_foreach(link_iter, &profile->link_table) { + cprof_link = cfl_list_entry(link_iter, struct cprof_link, _head); + si = dict_add_link(dict, cprof_link); + if (si < 0) { + goto fail; + } + st->link_map[i++] = si; + } + } + + /* Build stack_index_by_sample: map each sample's location_index[] to dict stack */ + j = 0; + cfl_list_foreach(sample_iter, &profile->samples) { + loc_indices = loc_indices_buf; + sample = cfl_list_entry(sample_iter, struct cprof_sample, _head); + n_loc = sample->location_index_count; + if (n_loc == 0) { + si = 0; /* zero stack */ + } + else { + if (n_loc > 256) { + loc_indices = malloc(n_loc * sizeof(int32_t)); + if (loc_indices == NULL) { + goto fail; + } + } + for (i = 0; i < n_loc; i++) { + loc_idx = sample->location_index[i]; + loc_indices[i] = (loc_idx < st->location_map_count) + ? st->location_map[loc_idx] : 0; + } + si = dict_add_stack(dict, loc_indices, n_loc); + if (n_loc > 256) { + free(loc_indices); + } + if (si < 0) { + goto fail; + } + } + st->stack_index_by_sample[j++] = si; + } + + state_idx++; + } + } + } + + *out_dict = dict; + *out_states = states; + *out_state_count = state_count; + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +fail: + for (i = 0; i < state_idx; i++) { + free_profile_encoding_state(&states[i]); + } + free(states); + destroy_profiles_dictionary(dict); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; +} static Opentelemetry__Proto__Profiles__V1development__ValueType * @@ -1048,38 +1786,6 @@ static return instance; } -static - Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit * - initialize_keyvalueandunit(void) { - Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *instance; - - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit)); - - if (instance == NULL) { - return NULL; - } - - opentelemetry__proto__profiles__v1development__key_value_and_unit__init(instance); - - return instance; -} - -static - Opentelemetry__Proto__Profiles__V1development__Line * - initialize_line() { - Opentelemetry__Proto__Profiles__V1development__Line *instance; - - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Line)); - - if (instance == NULL) { - return NULL; - } - - opentelemetry__proto__profiles__v1development__line__init(instance); - - return instance; -} - static Opentelemetry__Proto__Profiles__V1development__Link * initialize_link() { @@ -1136,50 +1842,6 @@ static return instance; } -static - Opentelemetry__Proto__Profiles__V1development__Function * - initialize_function() { - Opentelemetry__Proto__Profiles__V1development__Function *instance; - - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Function)); - - if (instance == NULL) { - return NULL; - } - - opentelemetry__proto__profiles__v1development__function__init(instance); - - return instance; -} - -static - Opentelemetry__Proto__Profiles__V1development__Mapping * - initialize_mapping(size_t attribute_count) { - Opentelemetry__Proto__Profiles__V1development__Mapping *instance; - - instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Mapping)); - - if (instance == NULL) { - return NULL; - } - - opentelemetry__proto__profiles__v1development__mapping__init(instance); - - if (attribute_count > 0) { - instance->attribute_indices = calloc(attribute_count, sizeof(int32_t)); - - if (instance->attribute_indices == NULL) { - destroy_mapping(instance); - - return NULL; - } - - instance->n_attribute_indices = attribute_count; - } - - return instance; -} - static Opentelemetry__Proto__Common__V1__InstrumentationScope * initialize_instrumentation_scope(size_t attribute_count) { @@ -1418,7 +2080,8 @@ static int pack_cprof_instrumentation_scope( static int pack_cprof_value_type( Opentelemetry__Proto__Profiles__V1development__ValueType **output_instance, - struct cprof_value_type *input_instance) + struct cprof_value_type *input_instance, + struct profile_encoding_state *encoding_state) { Opentelemetry__Proto__Profiles__V1development__ValueType *otlp_value_type; @@ -1428,9 +2091,24 @@ static int pack_cprof_value_type( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - /* type/unit are now string table indices; use 0 until dictionary encoding */ - otlp_value_type->type_strindex = 0; - otlp_value_type->unit_strindex = 0; + if (encoding_state != NULL && encoding_state->string_map != NULL) { + if ((size_t)input_instance->type < encoding_state->string_map_count) { + otlp_value_type->type_strindex = encoding_state->string_map[input_instance->type]; + } + else { + otlp_value_type->type_strindex = 0; + } + if ((size_t)input_instance->unit < encoding_state->string_map_count) { + otlp_value_type->unit_strindex = encoding_state->string_map[input_instance->unit]; + } + else { + otlp_value_type->unit_strindex = 0; + } + } + else { + otlp_value_type->type_strindex = 0; + otlp_value_type->unit_strindex = 0; + } *output_instance = otlp_value_type; @@ -1439,7 +2117,9 @@ static int pack_cprof_value_type( static int pack_cprof_sample( Opentelemetry__Proto__Profiles__V1development__Sample **output_instance, - struct cprof_sample *input_instance) + struct cprof_sample *input_instance, + struct profile_encoding_state *encoding_state, + size_t sample_index) { Opentelemetry__Proto__Profiles__V1development__Sample *otlp_sample; size_t index; @@ -1452,8 +2132,13 @@ static int pack_cprof_sample( return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } - /* stack_index into dictionary stack_table; 0 until dictionary encoding */ - otlp_sample->stack_index = 0; + if (encoding_state != NULL && encoding_state->stack_index_by_sample != NULL && + sample_index < encoding_state->sample_count) { + otlp_sample->stack_index = encoding_state->stack_index_by_sample[sample_index]; + } + else { + otlp_sample->stack_index = 0; + } for (index = 0 ; index < input_instance->value_count ; @@ -1467,7 +2152,13 @@ static int pack_cprof_sample( otlp_sample->attribute_indices[index] = (int32_t) input_instance->attributes[index]; } - otlp_sample->link_index = input_instance->link >= 0 ? (int32_t) input_instance->link : -1; + if (encoding_state != NULL && encoding_state->link_map != NULL && + (size_t)input_instance->link < encoding_state->link_map_count) { + otlp_sample->link_index = encoding_state->link_map[input_instance->link]; + } + else { + otlp_sample->link_index = 0; /* no link or link_table[0] sentinel */ + } for (index = 0 ; index < input_instance->timestamps_count ; @@ -1481,197 +2172,21 @@ static int pack_cprof_sample( } -static int pack_cprof_mapping( - Opentelemetry__Proto__Profiles__V1development__Mapping **output_instance, - struct cprof_mapping *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__Mapping *otlp_mapping; - size_t index; - - otlp_mapping = initialize_mapping(input_instance->attributes_count); - - if (otlp_mapping == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_mapping->filename_strindex = 0; /* TODO: resolve via dictionary string_table */ - - for (index = 0 ; - index < input_instance->attributes_count ; - index++) { - otlp_mapping->attribute_indices[index] = (int32_t) input_instance->attributes[index]; - } - - *output_instance = otlp_mapping; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - - - -static int pack_cprof_line( - Opentelemetry__Proto__Profiles__V1development__Line **output_instance, - struct cprof_line *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__Line *otlp_line; - - otlp_line = initialize_line(); - - if (otlp_line == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_line->function_index = input_instance->function_index; - otlp_line->line = input_instance->line; - otlp_line->column = input_instance->column; - - *output_instance = otlp_line; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - -static int pack_cprof_location( - Opentelemetry__Proto__Profiles__V1development__Location **output_instance, - struct cprof_location *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__Location *otlp_location; - struct cfl_list *iterator; - int result; - struct cprof_line *line; - size_t index; - - otlp_location = initialize_location(cfl_list_size(&input_instance->lines), - input_instance->attributes_count); - - if (otlp_location == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_location->mapping_index = input_instance->mapping_index; - otlp_location->address = input_instance->address; - - index = 0; - cfl_list_foreach(iterator, - &input_instance->lines) { - line = cfl_list_entry( - iterator, - struct cprof_line, _head); - - result = pack_cprof_line( - &otlp_location->lines[index], - line); - - if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_location(otlp_location); - - return result; - } - - index++; - } - - for (index = 0 ; - index < input_instance->attributes_count ; - index++) { - otlp_location->attribute_indices[index] = (int32_t) input_instance->attributes[index]; - } - - *output_instance = otlp_location; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - -static int pack_cprof_function( - Opentelemetry__Proto__Profiles__V1development__Function **output_instance, - struct cprof_function *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__Function *otlp_function; - - otlp_function = initialize_function(); - - if (otlp_function == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - /* String fields are now string table indices; use 0 until dictionary encoding */ - otlp_function->name_strindex = 0; - otlp_function->system_name_strindex = 0; - otlp_function->filename_strindex = 0; - otlp_function->start_line = input_instance->start_line; - - *output_instance = otlp_function; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - -static int pack_cprof_keyvalueandunit( - Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **output_instance, - struct cprof_attribute_unit *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *otlp_kv; - - otlp_kv = initialize_keyvalueandunit(); - - if (otlp_kv == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - /* key_strindex / unit_strindex require dictionary string_table; use 0 for now */ - otlp_kv->key_strindex = 0; - otlp_kv->unit_strindex = 0; - otlp_kv->value = NULL; /* TODO: convert attribute value to AnyValue if needed */ - - *output_instance = otlp_kv; - - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} - -static int pack_cprof_link( - Opentelemetry__Proto__Profiles__V1development__Link **output_instance, - struct cprof_link *input_instance) -{ - Opentelemetry__Proto__Profiles__V1development__Link *otlp_link; - - otlp_link = initialize_link(); - - if (otlp_link == NULL) { - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_link->trace_id.data = \ - (uint8_t *) cfl_sds_create_len((const char *) input_instance->trace_id, - sizeof(input_instance->trace_id)); - - if (otlp_link->trace_id.data == NULL) { - destroy_link(otlp_link); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_link->trace_id.len = sizeof(input_instance->trace_id); - - - otlp_link->span_id.data = \ - (uint8_t *) cfl_sds_create_len((const char *) input_instance->span_id, - sizeof(input_instance->span_id)); - - if (otlp_link->span_id.data == NULL) { - destroy_link(otlp_link); - - return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; - } - - otlp_link->span_id.len = sizeof(input_instance->span_id); - - - *output_instance = otlp_link; +static int pack_cprof_value_type( + Opentelemetry__Proto__Profiles__V1development__ValueType **output_instance, + struct cprof_value_type *input_instance, + struct profile_encoding_state *encoding_state); - return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; -} +static int pack_cprof_sample( + Opentelemetry__Proto__Profiles__V1development__Sample **output_instance, + struct cprof_sample *input_instance, + struct profile_encoding_state *encoding_state, + size_t sample_index); static int pack_cprof_profile( Opentelemetry__Proto__Profiles__V1development__Profile **output_instance, - struct cprof_profile *input_instance) + struct cprof_profile *input_instance, + struct profile_encoding_state *encoding_state) { Opentelemetry__Proto__Profiles__V1development__Profile *otlp_profile; struct cfl_list *iterator; @@ -1690,7 +2205,7 @@ static int pack_cprof_profile( if (!cfl_list_is_empty(&input_instance->sample_type)) { sample_type = cfl_list_entry_first(&input_instance->sample_type, struct cprof_value_type, _head); - result = pack_cprof_value_type(&otlp_profile->sample_type, sample_type); + result = pack_cprof_value_type(&otlp_profile->sample_type, sample_type, encoding_state); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); return result; @@ -1700,7 +2215,7 @@ static int pack_cprof_profile( index = 0; cfl_list_foreach(iterator, &input_instance->samples) { sample = cfl_list_entry(iterator, struct cprof_sample, _head); - result = pack_cprof_sample(&otlp_profile->samples[index], sample); + result = pack_cprof_sample(&otlp_profile->samples[index], sample, encoding_state, index); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); @@ -1712,7 +2227,7 @@ static int pack_cprof_profile( otlp_profile->time_unix_nano = input_instance->time_nanos; otlp_profile->duration_nano = input_instance->duration_nanos; - result = pack_cprof_value_type(&otlp_profile->period_type, &input_instance->period_type); + result = pack_cprof_value_type(&otlp_profile->period_type, &input_instance->period_type, encoding_state); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_profile(otlp_profile); return result; @@ -1727,11 +2242,13 @@ static int pack_cprof_profile( static int pack_cprof_scope_profiles( Opentelemetry__Proto__Profiles__V1development__ScopeProfiles **output_instance, - struct cprof_scope_profiles *input_instance) + struct cprof_scope_profiles *input_instance, + encoder_internal_ctx_t *internal_ctx) { Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *otlp_scope_profiles; struct cfl_list *iterator; struct cprof_profile *profile; + struct profile_encoding_state *encoding_state; int result; size_t index; @@ -1756,9 +2273,13 @@ static int pack_cprof_scope_profiles( iterator, struct cprof_profile, _head); + encoding_state = (internal_ctx->current_profile_index < internal_ctx->encoding_states_count) + ? &internal_ctx->encoding_states[internal_ctx->current_profile_index++] : NULL; + result = pack_cprof_profile( &otlp_scope_profiles->profiles[index], - profile); + profile, + encoding_state); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_scope_profiles(otlp_scope_profiles); @@ -1786,7 +2307,8 @@ static int pack_cprof_scope_profiles( static int pack_cprof_resource_profiles( Opentelemetry__Proto__Profiles__V1development__ResourceProfiles **output_instance, - struct cprof_resource_profiles *input_instance) + struct cprof_resource_profiles *input_instance, + encoder_internal_ctx_t *internal_ctx) { Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *otlp_resource_profiles; struct cprof_scope_profiles *scope_profiles; @@ -1815,7 +2337,8 @@ static int pack_cprof_resource_profiles( result = pack_cprof_scope_profiles( &otlp_resource_profiles->scope_profiles[index], - scope_profiles); + scope_profiles, + internal_ctx); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { destroy_resource_profiles(otlp_resource_profiles); @@ -1842,8 +2365,23 @@ static int pack_cprof_resource_profiles( } +static int pack_cprof_resource_profiles( + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles **output_instance, + struct cprof_resource_profiles *input_instance, + encoder_internal_ctx_t *internal_ctx); + +static int pack_cprof_scope_profiles( + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles **output_instance, + struct cprof_scope_profiles *input_instance, + encoder_internal_ctx_t *internal_ctx); + +static int pack_cprof_profile( + Opentelemetry__Proto__Profiles__V1development__Profile **output_instance, + struct cprof_profile *input_instance, + struct profile_encoding_state *encoding_state); + static int pack_context_profiles( - struct cprof_opentelemetry_encoding_context *context, + encoder_internal_ctx_t *internal_ctx, struct cprof *profile) { size_t index; @@ -1851,10 +2389,10 @@ static int pack_context_profiles( struct cfl_list *iterator; struct cprof_resource_profiles *resource_profiles; - context->export_service_request = \ + internal_ctx->pub->export_service_request = \ initialize_export_profiles_service_request(cfl_list_size(&profile->profiles)); - if (context->export_service_request == NULL) { + if (internal_ctx->pub->export_service_request == NULL) { return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; } @@ -1866,11 +2404,12 @@ static int pack_context_profiles( struct cprof_resource_profiles, _head); result = pack_cprof_resource_profiles( - &context->export_service_request->resource_profiles[index], - resource_profiles); + &internal_ctx->pub->export_service_request->resource_profiles[index], + resource_profiles, + internal_ctx); if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { - destroy_export_profiles_service_request(context->export_service_request); + destroy_export_profiles_service_request(internal_ctx->pub->export_service_request); return result; } @@ -1885,11 +2424,49 @@ static int pack_context( struct cprof_opentelemetry_encoding_context *context, struct cprof *profile) { + encoder_internal_ctx_t internal_ctx; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict; + struct profile_encoding_state *states; + size_t state_count; + int result; + size_t i; + memset(context, 0, sizeof(struct cprof_opentelemetry_encoding_context)); context->inner_context = profile; - return pack_context_profiles(context, profile); + result = build_profiles_dictionary(profile, &dict, &states, &state_count); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + return result; + } + + internal_ctx.pub = context; + internal_ctx.encoding_states = states; + internal_ctx.encoding_states_count = state_count; + internal_ctx.current_profile_index = 0; + + result = pack_context_profiles(&internal_ctx, profile); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + for (i = 0; i < state_count; i++) { + free_profile_encoding_state(&states[i]); + } + free(states); + if (dict != NULL) { + destroy_profiles_dictionary(dict); + } + return result; + } + + if (internal_ctx.pub->export_service_request != NULL && dict != NULL) { + internal_ctx.pub->export_service_request->dictionary = dict; + } + + for (i = 0; i < state_count; i++) { + free_profile_encoding_state(&states[i]); + } + free(states); + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; } static cfl_sds_t render_opentelemetry_context_to_sds( From 87e1b08fbf49906ab22dcfa25866c5fe6f60ebcf Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 6 Feb 2026 12:34:08 -0600 Subject: [PATCH 6/8] encode_text: enhance text printing for otel v1.9.0 Signed-off-by: Eduardo Silva --- src/cprof_encode_text.c | 537 ++++++++++++++++++++++++++++++---------- 1 file changed, 411 insertions(+), 126 deletions(-) diff --git a/src/cprof_encode_text.c b/src/cprof_encode_text.c index 299af8a..f883ca2 100644 --- a/src/cprof_encode_text.c +++ b/src/cprof_encode_text.c @@ -127,6 +127,7 @@ static int encode_aggregation_temporality( static int encode_cprof_value_type( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_value_type *instance); static int encode_cprof_sample( @@ -135,18 +136,22 @@ static int encode_cprof_sample( static int encode_cprof_mapping( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_mapping *instance); static int encode_cprof_line( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_line *instance); static int encode_cprof_location( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_location *instance); static int encode_cprof_function( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_function *instance); static int encode_cprof_attribute_unit( @@ -318,6 +323,48 @@ static int encode_string( return CPROF_ENCODE_TEXT_SUCCESS; } +/* Section header with count for debugging (e.g. "Samples (3) :") */ +static int encode_section_header_with_count( + struct cprof_text_encoding_context *context, + const char *label, + size_t count) +{ + cfl_sds_t result; + + result = cfl_sds_printf(&context->output_buffer, + "%s%s (%zu) :\n", + context->indentation_buffer, + label, + count); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +/* Item subheader with index for debugging (e.g. "Sample #0 :") */ +static int encode_item_header( + struct cprof_text_encoding_context *context, + const char *label, + size_t index) +{ + cfl_sds_t result; + + result = cfl_sds_printf(&context->output_buffer, + "%s%s #%zu :\n", + context->indentation_buffer, + label, + index); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + static int encode_double( struct cprof_text_encoding_context *context, int indent, @@ -411,6 +458,105 @@ static int encode_int64_t( return CPROF_ENCODE_TEXT_SUCCESS; } +/* Resolve string_table index; returns pointer to string or NULL if invalid. */ +static const char *resolve_string_index(struct cprof_profile *profile, int64_t index) +{ + if (profile == NULL || profile->string_table == NULL) { + return NULL; + } + if (index < 0 || (size_t)index >= profile->string_table_count) { + return NULL; + } + if (profile->string_table[index] == NULL) { + return ""; + } + return profile->string_table[index]; +} + +/* Append string to buffer with double-quotes escaped as \". */ +static int append_escaped_string(cfl_sds_t *buf, const char *str) +{ + cfl_sds_t result; + const char *p; + + if (str == NULL) { + return CPROF_ENCODE_TEXT_SUCCESS; + } + for (p = str; *p != '\0'; p++) { + if (*p == '"') { + result = cfl_sds_cat(*buf, "\\\"", 2); + } + else { + result = cfl_sds_printf(buf, "%c", *p); + } + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + if (*p == '"') { + *buf = result; + } + } + return CPROF_ENCODE_TEXT_SUCCESS; +} + +/* Encode int64 (string_table index) with optional resolution for debugging. */ +static int encode_int64_string_ref( + struct cprof_text_encoding_context *context, + int indent, + const char *label, + int64_t value, + struct cprof_profile *profile) +{ + const char *resolved; + char *local_indentation; + cfl_sds_t result; + int append_result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s%s%" PRId64, + local_indentation, + label, + value); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + resolved = resolve_string_index(profile, value); + if (resolved != NULL) { + result = cfl_sds_cat(context->output_buffer, " → \"", 4); + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + context->output_buffer = result; + append_result = append_escaped_string(&context->output_buffer, resolved); + if (append_result != CPROF_ENCODE_TEXT_SUCCESS) { + return append_result; + } + result = cfl_sds_cat(context->output_buffer, "\"\n", 2); + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + context->output_buffer = result; + } + else { + result = cfl_sds_cat(context->output_buffer, "\n", 1); + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + context->output_buffer = result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + static int encode_bool( struct cprof_text_encoding_context *context, int indent, @@ -932,25 +1078,29 @@ static int encode_aggregation_temporality( static int encode_cprof_value_type( struct cprof_text_encoding_context *context, - struct cprof_value_type *instance) { - cfl_sds_t result; + struct cprof_profile *profile, + struct cprof_value_type *instance) +{ + int result; - result = cfl_sds_printf(&context->output_buffer, - "%s" "Type : %" PRId64 "\n", - context->indentation_buffer, - instance->type); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Type : ", + instance->type, + profile); - if (result == NULL) { - return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; } - result = cfl_sds_printf(&context->output_buffer, - "%s" "Unit : %" PRId64 "\n", - context->indentation_buffer, - instance->unit); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Unit : ", + instance->unit, + profile); - if (result == NULL) { - return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; } return encode_aggregation_temporality( @@ -1023,7 +1173,7 @@ static int encode_cprof_sample( result = encode_uint64_t(context, CFL_TRUE, - "Link : ", + "Link (link_table index) : ", "\n", instance->link); @@ -1056,7 +1206,9 @@ static int encode_cprof_sample( static int encode_cprof_mapping( struct cprof_text_encoding_context *context, - struct cprof_mapping *instance) { + struct cprof_profile *profile, + struct cprof_mapping *instance) +{ int result; result = encode_uint64_t(context, @@ -1100,11 +1252,11 @@ static int encode_cprof_mapping( } - result = encode_int64_t(context, - CFL_TRUE, - "Filename : ", - "\n", - instance->filename); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Filename : ", + instance->filename, + profile); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1171,14 +1323,16 @@ static int encode_cprof_mapping( static int encode_cprof_line( struct cprof_text_encoding_context *context, - struct cprof_line *instance) { + struct cprof_profile *profile, + struct cprof_line *instance) +{ int result; - result = encode_uint64_t(context, - CFL_TRUE, - "Function index : ", - "\n", - instance->function_index); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Function index : ", + (int64_t) instance->function_index, + profile); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1211,7 +1365,9 @@ static int encode_cprof_line( static int encode_cprof_location( struct cprof_text_encoding_context *context, - struct cprof_location *instance) { + struct cprof_profile *profile, + struct cprof_location *instance) +{ struct cfl_list *iterator; int result; struct cprof_line *line; @@ -1268,7 +1424,7 @@ static int encode_cprof_location( line = cfl_list_entry(iterator, struct cprof_line, _head); - result = encode_cprof_line(context, line); + result = encode_cprof_line(context, profile, line); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1302,7 +1458,9 @@ static int encode_cprof_location( static int encode_cprof_function( struct cprof_text_encoding_context *context, - struct cprof_function *instance) { + struct cprof_profile *profile, + struct cprof_function *instance) +{ int result; result = encode_uint64_t(context, @@ -1315,31 +1473,31 @@ static int encode_cprof_function( return result; } - result = encode_int64_t(context, - CFL_TRUE, - "Name : ", - "\n", - instance->name); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Name : ", + instance->name, + profile); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; } - result = encode_int64_t(context, - CFL_TRUE, - "System name : ", - "\n", - instance->system_name); + result = encode_int64_string_ref(context, + CFL_TRUE, + "System name : ", + instance->system_name, + profile); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; } - result = encode_int64_t(context, - CFL_TRUE, - "Filename : ", - "\n", - instance->filename); + result = encode_int64_string_ref(context, + CFL_TRUE, + "Filename : ", + instance->filename, + profile); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1490,11 +1648,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->sample_type)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Sample types :"); + result = encode_section_header_with_count(context, + "Sample types", + cfl_list_size(&instance->sample_type)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1512,7 +1668,7 @@ static int encode_cprof_profile( iterator, struct cprof_value_type, _head); - result = encode_cprof_value_type(context, sample_type); + result = encode_cprof_value_type(context, instance, sample_type); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1527,11 +1683,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->samples)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Samples :"); + result = encode_section_header_with_count(context, + "Samples", + cfl_list_size(&instance->samples)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1543,16 +1697,39 @@ static int encode_cprof_profile( return result; } - cfl_list_foreach(iterator, - &instance->samples) { - sample = cfl_list_entry( - iterator, - struct cprof_sample, _head); + { + size_t sample_index = 0; + cfl_list_foreach(iterator, + &instance->samples) { + sample = cfl_list_entry( + iterator, + struct cprof_sample, _head); - result = encode_cprof_sample(context, sample); + result = encode_item_header(context, "Sample", sample_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_sample(context, sample); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + sample_index++; } } @@ -1564,11 +1741,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->mappings)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Mappings :"); + result = encode_section_header_with_count(context, + "Mappings", + cfl_list_size(&instance->mappings)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1580,16 +1755,39 @@ static int encode_cprof_profile( return result; } - cfl_list_foreach(iterator, - &instance->mappings) { - mapping = cfl_list_entry( - iterator, - struct cprof_mapping, _head); + { + size_t mapping_index = 0; + cfl_list_foreach(iterator, + &instance->mappings) { + mapping = cfl_list_entry( + iterator, + struct cprof_mapping, _head); - result = encode_cprof_mapping(context, mapping); + result = encode_item_header(context, "Mapping", mapping_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_mapping(context, instance, mapping); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + mapping_index++; } } @@ -1601,11 +1799,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->locations)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Locations :"); + result = encode_section_header_with_count(context, + "Locations", + cfl_list_size(&instance->locations)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1617,16 +1813,39 @@ static int encode_cprof_profile( return result; } - cfl_list_foreach(iterator, - &instance->locations) { - location = cfl_list_entry( - iterator, - struct cprof_location, _head); + { + size_t location_index = 0; + cfl_list_foreach(iterator, + &instance->locations) { + location = cfl_list_entry( + iterator, + struct cprof_location, _head); - result = encode_cprof_location(context, location); + result = encode_item_header(context, "Location", location_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_location(context, instance, location); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + location_index++; } } @@ -1650,11 +1869,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->functions)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Functions :"); + result = encode_section_header_with_count(context, + "Functions", + cfl_list_size(&instance->functions)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1666,16 +1883,39 @@ static int encode_cprof_profile( return result; } - cfl_list_foreach(iterator, - &instance->functions) { - function = cfl_list_entry( - iterator, - struct cprof_function, _head); + { + size_t function_index = 0; + cfl_list_foreach(iterator, + &instance->functions) { + function = cfl_list_entry( + iterator, + struct cprof_function, _head); - result = encode_cprof_function(context, function); + result = encode_item_header(context, "Function", function_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_function(context, instance, function); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + function_index++; } } @@ -1697,12 +1937,10 @@ static int encode_cprof_profile( return result; } - if (!cfl_list_is_empty(&instance->functions)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Attribute units :"); + if (!cfl_list_is_empty(&instance->attribute_units)) { + result = encode_section_header_with_count(context, + "Attribute units", + cfl_list_size(&instance->attribute_units)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1735,11 +1973,9 @@ static int encode_cprof_profile( } if (!cfl_list_is_empty(&instance->link_table)) { - result = encode_string(context, - CFL_TRUE, - "", - "\n", - "Links :"); + result = encode_section_header_with_count(context, + "Links", + cfl_list_size(&instance->link_table)); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1751,16 +1987,39 @@ static int encode_cprof_profile( return result; } - cfl_list_foreach(iterator, - &instance->link_table) { - link = cfl_list_entry( - iterator, - struct cprof_link, _head); + { + size_t link_index = 0; + cfl_list_foreach(iterator, + &instance->link_table) { + link = cfl_list_entry( + iterator, + struct cprof_link, _head); - result = encode_cprof_link(context, link); + result = encode_item_header(context, "Link", link_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_link(context, link); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + link_index++; } } @@ -1771,10 +2030,24 @@ static int encode_cprof_profile( } } + result = encode_section_header_with_count(context, + "String table", + instance->string_table_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + result = encode_string_array( context, CFL_TRUE, - "String table : [", + "[ ", ", ", " ]\n", (char **) instance->string_table, @@ -1784,6 +2057,16 @@ static int encode_cprof_profile( return result; } + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + result = encode_int64_t(context, CFL_TRUE, "Drop frames : ", @@ -1840,7 +2123,7 @@ static int encode_cprof_profile( return result; } - result = encode_cprof_value_type(context, &instance->period_type); + result = encode_cprof_value_type(context, instance, &instance->period_type); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -2193,7 +2476,8 @@ void print_profile(struct cprof_profile *profile) tmp = profile->string_table[location_idx]; if (tmp[0] == '\0') { printf(" [Empty String: No Function Name]\n"); - } else { + } + else { printf(" Function: %s\n", tmp); } } @@ -2216,7 +2500,8 @@ void print_profile(struct cprof_profile *profile) for (i = 0; i < sample->timestamps_count; ++i) { printf(" Timestamp %d: %" PRIu64 " ns\n", i, sample->timestamps_unix_nano[i]); } - } else { + } + else { printf(" [No Timestamps]\n"); } From c6574088efef469f5fa9910e336f1be9c89b2a3c Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 6 Feb 2026 12:34:27 -0600 Subject: [PATCH 7/8] tests: extend and adjust tests Signed-off-by: Eduardo Silva --- tests/CMakeLists.txt | 2 +- tests/opentelemetry_transcoder.c | 472 ++++++++++++++++++++ tests/{text_transcoder.c => text_encoder.c} | 3 + 3 files changed, 476 insertions(+), 1 deletion(-) rename tests/{text_transcoder.c => text_encoder.c} (99%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0da2028..d59f79e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,7 +2,7 @@ set(UNIT_TESTS_FILES profile.c msgpack_transcoder.c opentelemetry_transcoder.c - text_transcoder.c + text_encoder.c ) set(CPROF_TESTS_DATA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/data") diff --git a/tests/opentelemetry_transcoder.c b/tests/opentelemetry_transcoder.c index fa25e5d..e6861e7 100644 --- a/tests/opentelemetry_transcoder.c +++ b/tests/opentelemetry_transcoder.c @@ -22,10 +22,12 @@ #include #include #include +#include #include #include #include +#include /* * Build a minimal cprof (resource_profiles -> scope_profiles -> profile with one sample) @@ -277,8 +279,478 @@ static void test_decoder() cprof_encode_opentelemetry_destroy(otlp_result); } +/* + * Build a cprof with dictionary tables populated: one mapping, one function, + * one location (with one line), one sample referencing that location, and + * optionally one link. Exercises mapping_table, function_table, location_table, + * stack_table (with real location indices), and link_table. + */ +static struct cprof *create_cprof_with_dictionary_tables(void) +{ + struct cprof *cprof; + struct cprof_resource_profiles *resource_profiles; + struct cprof_resource *resource; + struct cprof_scope_profiles *scope_profiles; + struct cprof_profile *profile; + struct cprof_sample *sample; + struct cprof_mapping *mapping; + struct cprof_function *func; + struct cprof_location *loc; + struct cprof_line *line; + struct cprof_link *link; + struct cfl_kvlist *attrs; + size_t id_bin; + size_t id_foo; + int ret; + + cprof = cprof_create(); + if (cprof == NULL) { + return NULL; + } + + resource_profiles = cprof_resource_profiles_create(""); + if (resource_profiles == NULL) { + cprof_destroy(cprof); + return NULL; + } + + attrs = cfl_kvlist_create(); + if (attrs == NULL) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + resource = cprof_resource_create(attrs); + if (resource == NULL) { + cfl_kvlist_destroy(attrs); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + resource_profiles->resource = resource; + + scope_profiles = cprof_scope_profiles_create(resource_profiles, ""); + if (scope_profiles == NULL) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + scope_profiles->scope = cprof_instrumentation_scope_create("", "", NULL, 0); + if (scope_profiles->scope == NULL) { + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + profile = cprof_profile_create(); + if (profile == NULL) { + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + profile->time_nanos = 2000000000ULL; + profile->duration_nanos = 200000000ULL; + + cprof_sample_type_str_create(profile, "count", "", CPROF_AGGREGATION_TEMPORALITY_CUMULATIVE); + id_bin = cprof_profile_string_add(profile, "/bin/app", -1); + id_foo = cprof_profile_string_add(profile, "foo", -1); + if (id_bin == 0 || id_foo == 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + /* One mapping (dict mapping_table will have zero + this). */ + mapping = cprof_mapping_create(profile); + if (mapping == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + mapping->memory_start = 0x1000ULL; + mapping->memory_limit = 0x2000ULL; + mapping->file_offset = 0; + mapping->filename = (int64_t)id_bin; + + /* One function (dict function_table will have zero + this). */ + func = cprof_function_create(profile); + if (func == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + func->name = (int64_t)id_foo; + func->system_name = (int64_t)id_foo; + func->filename = 0; + func->start_line = 10; + + /* One location with one line (dict location_table will have zero + this). */ + loc = cprof_location_create(profile); + if (loc == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + loc->mapping_index = 0; + loc->address = 0x1000ULL; + line = cprof_line_create(loc); + if (line == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + line->function_index = 0; + line->line = 10; + line->column = 0; + + /* One link (dict link_table will have zero + this). */ + link = cprof_link_create(profile); + if (link == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + link->trace_id[0] = 0x01; + link->trace_id[15] = 0x0f; + link->span_id[0] = 0xaa; + link->span_id[7] = 0xbb; + + /* Sample with location_index 0 (first location) and link 0. */ + sample = cprof_sample_create(profile); + if (sample == NULL) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + ret = cprof_sample_add_location_index(sample, 0); + if (ret != 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + ret = cprof_sample_add_value(sample, 42); + if (ret != 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + sample->link = 0; /* index into profile->link_table (first link). */ + + cfl_list_add(&profile->_head, &scope_profiles->profiles); + + ret = cprof_resource_profiles_add(cprof, resource_profiles); + if (ret != 0) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + return cprof; +} + +/* Encode cprof with full dictionary (mappings, functions, locations, links) and check success. */ +static void test_encoder_dictionary_tables() +{ + cfl_sds_t otlp_result; + struct cprof *context; + int result; + + context = create_cprof_with_dictionary_tables(); + TEST_CHECK(context != NULL); + if (context == NULL) { + return; + } + + result = cprof_encode_opentelemetry_create(&otlp_result, context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + + if (result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS && otlp_result != NULL) { + cprof_encode_opentelemetry_destroy(otlp_result); + } + cprof_destroy(context); +} + +/* + * Verify decoded cprof matches the structure produced by create_cprof_with_dictionary_tables. + * Decoder may emit dictionary sentinel at index 0 plus our entry, so we require at least 1 + * and find the entry matching our data. This guards against breaking changes in encode/decode. + */ +static void verify_decoded_cprof_dictionary_tables(struct cprof *decoded) +{ + struct cprof_resource_profiles *rp; + struct cprof_scope_profiles *sp; + struct cprof_profile *profile; + struct cprof_mapping *mapping; + struct cprof_function *func; + struct cprof_location *loc; + struct cprof_line *line; + struct cprof_link *link; + struct cprof_sample *sample; + struct cfl_list *rp_iter; + struct cfl_list *sp_iter; + struct cfl_list *prof_iter; + struct cfl_list *map_iter; + struct cfl_list *func_iter; + struct cfl_list *loc_iter; + struct cfl_list *line_iter; + struct cfl_list *link_iter; + struct cfl_list *sample_iter; + size_t i; + int found_bin_app; + int found_foo; + int found_mapping; + int found_function; + int found_location; + int found_link; + + TEST_CHECK(decoded != NULL); + if (decoded == NULL) { + return; + } + + TEST_CHECK(cfl_list_size(&decoded->profiles) == 1); + if (cfl_list_size(&decoded->profiles) != 1) { + return; + } + + rp_iter = decoded->profiles.next; + rp = cfl_list_entry(rp_iter, struct cprof_resource_profiles, _head); + TEST_CHECK(cfl_list_size(&rp->scope_profiles) == 1); + if (cfl_list_size(&rp->scope_profiles) != 1) { + return; + } + + sp_iter = rp->scope_profiles.next; + sp = cfl_list_entry(sp_iter, struct cprof_scope_profiles, _head); + TEST_CHECK(cfl_list_size(&sp->profiles) == 1); + if (cfl_list_size(&sp->profiles) != 1) { + return; + } + + prof_iter = sp->profiles.next; + profile = cfl_list_entry(prof_iter, struct cprof_profile, _head); + + /* Profile metadata */ + TEST_CHECK(profile->time_nanos == 2000000000ULL); + TEST_CHECK(profile->duration_nanos == 200000000ULL); + + /* At least one mapping; find one with memory_start=0x1000, filename "/bin/app" */ + TEST_CHECK(cfl_list_size(&profile->mappings) >= 1); + found_mapping = 0; + for (map_iter = profile->mappings.next; map_iter != &profile->mappings; map_iter = map_iter->next) { + mapping = cfl_list_entry(map_iter, struct cprof_mapping, _head); + if (mapping->memory_start == 0x1000ULL && mapping->memory_limit == 0x2000ULL && + mapping->file_offset == 0) { + if (mapping->filename >= 0 && (size_t)mapping->filename < profile->string_table_count && + profile->string_table[mapping->filename] != NULL && + strcmp(profile->string_table[mapping->filename], "/bin/app") == 0) { + found_mapping = 1; + break; + } + } + } + TEST_CHECK(found_mapping && "decoded profile must have mapping with /bin/app"); + + /* At least one function; find one with start_line=10, name "foo" */ + TEST_CHECK(cfl_list_size(&profile->functions) >= 1); + found_function = 0; + for (func_iter = profile->functions.next; func_iter != &profile->functions; func_iter = func_iter->next) { + func = cfl_list_entry(func_iter, struct cprof_function, _head); + if (func->start_line == 10) { + if (func->name >= 0 && (size_t)func->name < profile->string_table_count && + profile->string_table[func->name] != NULL && + strcmp(profile->string_table[func->name], "foo") == 0) { + found_function = 1; + break; + } + } + } + TEST_CHECK(found_function && "decoded profile must have function \"foo\" with start_line 10"); + + /* At least one location; find one with address=0x1000, one line with line=10 */ + TEST_CHECK(cfl_list_size(&profile->locations) >= 1); + found_location = 0; + for (loc_iter = profile->locations.next; loc_iter != &profile->locations; loc_iter = loc_iter->next) { + loc = cfl_list_entry(loc_iter, struct cprof_location, _head); + if (loc->address == 0x1000ULL && cfl_list_size(&loc->lines) >= 1) { + line_iter = loc->lines.next; + line = cfl_list_entry(line_iter, struct cprof_line, _head); + if (line->line == 10) { + found_location = 1; + break; + } + } + } + TEST_CHECK(found_location && "decoded profile must have location at 0x1000 with line 10"); + + /* At least one link; find one with trace_id[0]=0x01, span_id[0]=0xaa */ + TEST_CHECK(cfl_list_size(&profile->link_table) >= 1); + found_link = 0; + for (link_iter = profile->link_table.next; link_iter != &profile->link_table; link_iter = link_iter->next) { + link = cfl_list_entry(link_iter, struct cprof_link, _head); + if (link->trace_id[0] == 0x01 && link->trace_id[15] == 0x0f && + link->span_id[0] == (uint8_t)0xaa && link->span_id[7] == (uint8_t)0xbb) { + found_link = 1; + break; + } + } + TEST_CHECK(found_link && "decoded profile must have link with expected trace_id/span_id"); + + /* Exactly one sample: value 42, at least one location_index, link index 0 or matching link */ + TEST_CHECK(cfl_list_size(&profile->samples) == 1); + if (cfl_list_size(&profile->samples) == 1) { + sample_iter = profile->samples.next; + sample = cfl_list_entry(sample_iter, struct cprof_sample, _head); + TEST_CHECK(sample->value_count == 1); + if (sample->value_count >= 1 && sample->values != NULL) { + TEST_CHECK(sample->values[0] == 42); + } + TEST_CHECK(sample->location_index_count >= 1 && "sample must have at least one location_index"); + if (sample->location_index_count >= 1 && sample->location_index != NULL) { + TEST_CHECK(sample->location_index[0] == 0 && "sample must reference first location"); + } + /* sample must reference a link; decoder may use dict index 0 or 1 (sentinel vs first real link) */ + TEST_CHECK(cfl_list_size(&profile->link_table) > 0 && "profile must have links"); + if ((size_t)sample->link < cfl_list_size(&profile->link_table)) { + link_iter = profile->link_table.next; + for (i = 0; i < (size_t)sample->link && link_iter != &profile->link_table; i++) { + link_iter = link_iter->next; + } + if (link_iter != &profile->link_table) { + link = cfl_list_entry(link_iter, struct cprof_link, _head); + TEST_CHECK(link->trace_id[0] == 0x01 && link->trace_id[15] == 0x0f && + link->span_id[0] == (uint8_t)0xaa && link->span_id[7] == (uint8_t)0xbb && + "sample must reference link with expected trace_id/span_id"); + } + } + } + + /* String table must contain "/bin/app" and "foo" (decoder may reorder) */ + found_bin_app = 0; + found_foo = 0; + for (i = 0; i < profile->string_table_count && profile->string_table != NULL; i++) { + if (profile->string_table[i] != NULL) { + if (strcmp(profile->string_table[i], "/bin/app") == 0) { + found_bin_app = 1; + } + if (strcmp(profile->string_table[i], "foo") == 0) { + found_foo = 1; + } + } + } + TEST_CHECK(found_bin_app && "string_table must contain \"/bin/app\""); + TEST_CHECK(found_foo && "string_table must contain \"foo\""); +} + +/* + * Encode cprof with dictionary, unpack the wire buffer, and assert the request + * contains a non-NULL dictionary with expected table counts. Catches encoder + * regressions (e.g. dictionary no longer emitted). + */ +static void test_wire_format_dictionary_present() +{ + cfl_sds_t otlp_result; + struct cprof *context; + int result; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *req; + + context = create_cprof_with_dictionary_tables(); + TEST_CHECK(context != NULL); + if (context == NULL) { + return; + } + + result = cprof_encode_opentelemetry_create(&otlp_result, context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + cprof_destroy(context); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS || otlp_result == NULL) { + return; + } + + req = opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__unpack( + NULL, + cfl_sds_len(otlp_result), + (const unsigned char *) otlp_result); + + TEST_CHECK(req != NULL && "unpack of encoded buffer must succeed"); + if (req != NULL) { + TEST_CHECK(req->dictionary != NULL && "encoded request must contain dictionary"); + if (req->dictionary != NULL) { + TEST_CHECK(req->dictionary->n_string_table >= 2 && "dictionary must have string table (e.g. \"/bin/app\", \"foo\")"); + TEST_CHECK(req->dictionary->n_mapping_table >= 1 && "dictionary must have at least one mapping"); + TEST_CHECK(req->dictionary->n_function_table >= 1 && "dictionary must have at least one function"); + TEST_CHECK(req->dictionary->n_location_table >= 1 && "dictionary must have at least one location"); + TEST_CHECK(req->dictionary->n_link_table >= 1 && "dictionary must have at least one link"); + TEST_CHECK(req->dictionary->n_stack_table >= 1 && "dictionary must have at least one stack"); + } + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked(req, NULL); + } + + cprof_encode_opentelemetry_destroy(otlp_result); +} + +/* Round-trip cprof with full dictionary tables; decode must succeed and match structure. */ +static void test_decoder_dictionary_tables() +{ + cfl_sds_t otlp_result; + struct cprof *encoded_context; + struct cprof *decoded_context; + int result; + size_t offset; + + encoded_context = create_cprof_with_dictionary_tables(); + TEST_CHECK(encoded_context != NULL); + if (encoded_context == NULL) { + return; + } + + result = cprof_encode_opentelemetry_create(&otlp_result, encoded_context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + cprof_destroy(encoded_context); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS || otlp_result == NULL) { + return; + } + + offset = 0; + decoded_context = NULL; + result = cprof_decode_opentelemetry_create(&decoded_context, + (unsigned char *) otlp_result, + cfl_sds_len(otlp_result), + &offset); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); + + if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && decoded_context != NULL) { + verify_decoded_cprof_dictionary_tables(decoded_context); + cprof_decode_opentelemetry_destroy(decoded_context); + } + cprof_encode_opentelemetry_destroy(otlp_result); +} + TEST_LIST = { {"encoder", test_encoder}, {"decoder", test_decoder}, + {"encoder_dictionary_tables", test_encoder_dictionary_tables}, + {"wire_format_dictionary_present", test_wire_format_dictionary_present}, + {"decoder_dictionary_tables", test_decoder_dictionary_tables}, { 0 } }; diff --git a/tests/text_transcoder.c b/tests/text_encoder.c similarity index 99% rename from tests/text_transcoder.c rename to tests/text_encoder.c index 32aa074..c1faf9e 100644 --- a/tests/text_transcoder.c +++ b/tests/text_encoder.c @@ -647,6 +647,9 @@ static void test_encoder() if (result == CPROF_ENCODE_TEXT_SUCCESS) { TEST_CHECK(cfl_sds_len(text_encoder_result) > 1); + printf("%s\n", text_encoder_result); + fflush(stdout); + cprof_encode_text_destroy(text_encoder_result); } From ca1db763e574d57c06d9067faf06e14ae71e30c7 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Mon, 9 Feb 2026 12:52:16 -0600 Subject: [PATCH 8/8] profiles: add configurable text render modes and harden OTLP profile decoding - Added text encoder render-mode support so callers can choose indexed/dictionary output or fully resolved human-readable output. - Updated text encoding to resolve mapping/location/function/attribute references safely and render values more consistently. - Hardened OTLP profiles decode paths with better null/resource/index handling and safer lookups. - Fixed attribute-table index alignment issues that caused missing/incorrect resolved attribute rendering. - Kept internal profile data msgpack-encodable while preserving index stability for resolved text output. Signed-off-by: Eduardo Silva --- include/cprofiles/cprof_encode_text.h | 7 +- src/cprof_decode_opentelemetry.c | 141 ++++- src/cprof_encode_text.c | 642 ++++++++++++++++++---- src/cprof_opentelemetry_variant_helpers.c | 50 +- src/cprof_profile.c | 10 +- src/cprof_sample.c | 7 +- tests/text_encoder.c | 4 +- 7 files changed, 712 insertions(+), 149 deletions(-) diff --git a/include/cprofiles/cprof_encode_text.h b/include/cprofiles/cprof_encode_text.h index 4cf12f5..f6dea8b 100644 --- a/include/cprofiles/cprof_encode_text.h +++ b/include/cprofiles/cprof_encode_text.h @@ -27,16 +27,21 @@ #define CPROF_ENCODE_TEXT_ALLOCATION_ERROR 1 #define CPROF_ENCODE_TEXT_INVALID_ARGUMENT_ERROR 2 +#define CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES 0 +#define CPROF_ENCODE_TEXT_RENDER_RESOLVED 1 + struct cprof_text_encoding_context { cfl_sds_t output_buffer; size_t indentation_level; cfl_sds_t indentation_buffer; size_t indentation_level_size; char indentation_character; + int render_mode; }; int cprof_encode_text_create(cfl_sds_t *result_buffer, - struct cprof *profile); + struct cprof *profile, + int render_mode); void cprof_encode_text_destroy(cfl_sds_t instance); diff --git a/src/cprof_decode_opentelemetry.c b/src/cprof_decode_opentelemetry.c index 5300f29..c2407ee 100644 --- a/src/cprof_decode_opentelemetry.c +++ b/src/cprof_decode_opentelemetry.c @@ -40,6 +40,14 @@ static struct cprof_resource * struct cfl_kvlist *attributes; int result; + if (input_resource == NULL) { + output_resource = cprof_resource_create(NULL); + if (output_resource != NULL) { + output_resource->dropped_attributes_count = 0; + } + return output_resource; + } + attributes = cfl_kvlist_create(); if (attributes == NULL) { @@ -106,6 +114,7 @@ static int decode_profile_sample_entry(struct cprof_sample *sample, Opentelemetry__Proto__Profiles__V1development__Sample *input_sample, Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { + int32_t location_index; int result; size_t index; Opentelemetry__Proto__Profiles__V1development__Stack *stack; @@ -117,8 +126,16 @@ static int decode_profile_sample_entry(struct cprof_sample *sample, stack = dictionary->stack_table[input_sample->stack_index]; if (stack != NULL && stack->location_indices != NULL) { for (index = 0; index < stack->n_location_indices; index++) { + location_index = stack->location_indices[index]; + + if (location_index < 0 || + (dictionary->location_table != NULL && + (size_t) location_index >= dictionary->n_location_table)) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = cprof_sample_add_location_index(sample, - (uint64_t)stack->location_indices[index]); + (uint64_t) location_index); if (result != 0) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -185,8 +202,16 @@ static int decode_mapping_entry(struct cprof_mapping *mapping, } static int decode_line_entry(struct cprof_line *line, - Opentelemetry__Proto__Profiles__V1development__Line *input_line) + Opentelemetry__Proto__Profiles__V1development__Line *input_line, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { + if (input_line->function_index < 0 || + (dictionary != NULL && + dictionary->function_table != NULL && + (size_t) input_line->function_index >= dictionary->n_function_table)) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + line->function_index = (uint64_t)input_line->function_index; line->line = input_line->line; line->column = input_line->column; @@ -195,12 +220,20 @@ static int decode_line_entry(struct cprof_line *line, } static int decode_location_entry(struct cprof_location *location, - Opentelemetry__Proto__Profiles__V1development__Location *input_location) + Opentelemetry__Proto__Profiles__V1development__Location *input_location, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { int result; size_t index; struct cprof_line *line; + if (input_location->mapping_index < 0 || + (dictionary != NULL && + dictionary->mapping_table != NULL && + (size_t) input_location->mapping_index >= dictionary->n_mapping_table)) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + location->id = 0; location->mapping_index = (uint64_t)input_location->mapping_index; location->address = input_location->address; @@ -211,7 +244,7 @@ static int decode_location_entry(struct cprof_location *location, if (line == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = decode_line_entry(line, input_location->lines[index]); + result = decode_line_entry(line, input_location->lines[index], dictionary); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -282,7 +315,7 @@ static int decode_profile_entry(struct cprof_profile *profile, Opentelemetry__Proto__Profiles__V1development__Profile *input_profile, Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { - size_t string_table_add_result; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *indexed_attribute_entry; struct cprof_attribute_unit *attribute_unit; struct cprof_value_type *sample_type; struct cprof_location *location; @@ -290,6 +323,10 @@ static int decode_profile_entry(struct cprof_profile *profile, struct cprof_mapping *mapping; struct cprof_sample *sample; struct cprof_link *link; + struct cfl_variant *indexed_attribute_value; + int32_t indexed_attribute_key_index; + int32_t indexed_attribute_table_index; + char *indexed_attribute_key; int result; size_t index; @@ -297,12 +334,35 @@ static int decode_profile_entry(struct cprof_profile *profile, if (dictionary != NULL) { /* String table */ if (dictionary->string_table != NULL) { - for (index = 0; index < dictionary->n_string_table; index++) { - const char *s = dictionary->string_table[index]; - string_table_add_result = cprof_profile_string_add(profile, - (char *)(uintptr_t)(s != NULL ? s : ""), s != NULL ? (int)strlen(s) : 0); - if (string_table_add_result == (size_t)-1) { - return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + size_t table_size; + + table_size = dictionary->n_string_table; + + if (table_size == 0) { + table_size = 1; + } + + profile->string_table = calloc(table_size, sizeof(cfl_sds_t)); + if (profile->string_table == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + profile->string_table_size = table_size; + profile->string_table_count = table_size; + + for (index = 0; index < table_size; index++) { + const char *s; + + if (index < dictionary->n_string_table) { + s = dictionary->string_table[index]; + } + else { + s = ""; + } + + profile->string_table[index] = cfl_sds_create(s != NULL ? s : ""); + if (profile->string_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } } } @@ -328,7 +388,9 @@ static int decode_profile_entry(struct cprof_profile *profile, if (location == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = decode_location_entry(location, dictionary->location_table[index]); + result = decode_location_entry(location, + dictionary->location_table[index], + dictionary); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { return result; } @@ -385,6 +447,49 @@ static int decode_profile_entry(struct cprof_profile *profile, } } } + + /* Profile attributes reference dictionary.attribute_table by index */ + if (input_profile->attribute_indices != NULL && + input_profile->n_attribute_indices > 0) { + for (index = 0; index < input_profile->n_attribute_indices; index++) { + indexed_attribute_table_index = input_profile->attribute_indices[index]; + + if (indexed_attribute_table_index < 0 || + (size_t) indexed_attribute_table_index >= dictionary->n_attribute_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + indexed_attribute_entry = dictionary->attribute_table[indexed_attribute_table_index]; + + if (indexed_attribute_entry == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + indexed_attribute_key = ""; + indexed_attribute_key_index = indexed_attribute_entry->key_strindex; + + if (dictionary->string_table != NULL && + indexed_attribute_key_index >= 0 && + (size_t) indexed_attribute_key_index < dictionary->n_string_table && + dictionary->string_table[indexed_attribute_key_index] != NULL) { + indexed_attribute_key = dictionary->string_table[indexed_attribute_key_index]; + } + + indexed_attribute_value = clone_variant(indexed_attribute_entry->value); + + if (indexed_attribute_value == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + if (cfl_kvlist_insert(profile->attributes, + indexed_attribute_key, + indexed_attribute_value) != 0) { + cfl_variant_destroy(indexed_attribute_value); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + } } /* Profile sample_type (single ValueType in new proto) */ @@ -508,7 +613,6 @@ static int decode_scope_profiles_entry(struct cprof_resource_profiles *resource_ } if (profiles->scope == NULL) { - cprof_scope_profiles_destroy(profiles); return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } @@ -517,7 +621,6 @@ static int decode_scope_profiles_entry(struct cprof_resource_profiles *resource_ result = decode_profile_into_scope(profiles, input_scope_profiles->profiles[index], dictionary); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - cprof_scope_profiles_destroy(profiles); return result; } } @@ -611,6 +714,11 @@ int cprof_decode_opentelemetry_create(struct cprof **result_context, int result; result = CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + context = NULL; + + if (result_context != NULL) { + *result_context = NULL; + } service_request = opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__unpack( NULL, @@ -633,6 +741,9 @@ int cprof_decode_opentelemetry_create(struct cprof **result_context, if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS) { *result_context = context; } + else if (context != NULL) { + cprof_destroy(context); + } return result; } @@ -642,4 +753,4 @@ void cprof_decode_opentelemetry_destroy(struct cprof *context) if (context != NULL) { cprof_destroy(context); } -} \ No newline at end of file +} diff --git a/src/cprof_encode_text.c b/src/cprof_encode_text.c index f883ca2..08203a5 100644 --- a/src/cprof_encode_text.c +++ b/src/cprof_encode_text.c @@ -132,6 +132,7 @@ static int encode_cprof_value_type( static int encode_cprof_sample( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_sample *instance); static int encode_cprof_mapping( @@ -473,6 +474,106 @@ static const char *resolve_string_index(struct cprof_profile *profile, int64_t i return profile->string_table[index]; } +static struct cprof_mapping *resolve_mapping_index(struct cprof_profile *profile, uint64_t index) +{ + struct cfl_list *iterator; + struct cprof_mapping *mapping; + uint64_t current_index; + + if (profile == NULL) { + return NULL; + } + + current_index = 0; + + cfl_list_foreach(iterator, &profile->mappings) { + mapping = cfl_list_entry(iterator, struct cprof_mapping, _head); + + if (current_index == index) { + return mapping; + } + + current_index++; + } + + return NULL; +} + +static struct cprof_location *resolve_location_index(struct cprof_profile *profile, uint64_t index) +{ + struct cfl_list *iterator; + struct cprof_location *location; + uint64_t current_index; + + if (profile == NULL) { + return NULL; + } + + current_index = 0; + + cfl_list_foreach(iterator, &profile->locations) { + location = cfl_list_entry(iterator, struct cprof_location, _head); + + if (current_index == index) { + return location; + } + + current_index++; + } + + return NULL; +} + +static struct cprof_function *resolve_function_index(struct cprof_profile *profile, uint64_t index) +{ + struct cfl_list *iterator; + struct cprof_function *function; + uint64_t current_index; + + if (profile == NULL) { + return NULL; + } + + current_index = 0; + + cfl_list_foreach(iterator, &profile->functions) { + function = cfl_list_entry(iterator, struct cprof_function, _head); + + if (current_index == index) { + return function; + } + + current_index++; + } + + return NULL; +} + +static struct cfl_kvpair *resolve_attribute_index(struct cprof_profile *profile, uint64_t index) +{ + struct cfl_list *iterator; + struct cfl_kvpair *entry; + uint64_t current_index; + + if (profile == NULL || profile->attribute_table == NULL) { + return NULL; + } + + current_index = 0; + + cfl_list_foreach(iterator, &profile->attribute_table->list) { + entry = cfl_list_entry(iterator, struct cfl_kvpair, _head); + + if (current_index == index) { + return entry; + } + + current_index++; + } + + return NULL; +} + /* Append string to buffer with double-quotes escaped as \". */ static int append_escaped_string(cfl_sds_t *buf, const char *str) { @@ -519,6 +620,36 @@ static int encode_int64_string_ref( local_indentation = (char *) ""; } + resolved = resolve_string_index(profile, value); + + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_RESOLVED && + resolved != NULL) { + result = cfl_sds_printf(&context->output_buffer, + "%s%s\"", + local_indentation, + label); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + context->output_buffer = result; + + append_result = append_escaped_string(&context->output_buffer, resolved); + if (append_result != CPROF_ENCODE_TEXT_SUCCESS) { + return append_result; + } + + result = cfl_sds_cat(context->output_buffer, "\"\n", 2); + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + context->output_buffer = result; + + return CPROF_ENCODE_TEXT_SUCCESS; + } + result = cfl_sds_printf(&context->output_buffer, "%s%s%" PRId64, local_indentation, @@ -529,9 +660,10 @@ static int encode_int64_string_ref( return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; } - resolved = resolve_string_index(profile, value); if (resolved != NULL) { - result = cfl_sds_cat(context->output_buffer, " → \"", 4); + result = cfl_sds_cat(context->output_buffer, + " → \"", + strlen(" → \"")); if (result == NULL) { return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; } @@ -720,6 +852,206 @@ static int encode_uint64_t_array( return CPROF_ENCODE_TEXT_SUCCESS; } +static int encode_attribute_index_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cprof_profile *profile, + uint64_t *data_list, + size_t data_length) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + struct cfl_kvpair *attribute; + int placeholder_attribute; + + if (context->render_mode != CPROF_ENCODE_TEXT_RENDER_RESOLVED) { + return encode_uint64_t_array(context, indent, prefix, separator, suffix, + data_list, data_length); + } + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + sds_result = cfl_sds_printf(&context->output_buffer, + "%s" "%s", + local_indentation, + prefix); + + if (sds_result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + for (index = 0; index < data_length; index++) { + attribute = resolve_attribute_index(profile, data_list[index]); + placeholder_attribute = CFL_FALSE; + + if (attribute != NULL && + attribute->key != NULL && + attribute->key[0] == '\0' && + attribute->val != NULL && + attribute->val->type == CFL_VARIANT_STRING && + attribute->val->data.as_string != NULL && + cfl_sds_len(attribute->val->data.as_string) == 0) { + placeholder_attribute = CFL_TRUE; + } + + if (attribute == NULL || attribute->val == NULL || placeholder_attribute) { + result = encode_uint64_t(context, + CFL_FALSE, + "", + data_list[index]); + } + else { + result = encode_string(context, + CFL_FALSE, + "\"", + "\": ", + attribute->key); + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cfl_variant(context, + CFL_FALSE, + "", + "", + attribute->val); + } + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (index + 1 < data_length) { + result = encode_string(context, + CFL_FALSE, + "", + "", + separator); + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + } + + sds_result = cfl_sds_printf(&context->output_buffer, "%s", suffix); + if (sds_result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_location_reference_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cprof_profile *profile, + uint64_t *data_list, + size_t data_length) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + struct cprof_location *location; + struct cprof_line *line; + struct cprof_function *function; + const char *resolved; + + if (context->render_mode != CPROF_ENCODE_TEXT_RENDER_RESOLVED) { + return encode_uint64_t_array(context, indent, prefix, separator, suffix, + data_list, data_length); + } + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + sds_result = cfl_sds_printf(&context->output_buffer, + "%s" "%s", + local_indentation, + prefix); + if (sds_result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + for (index = 0; index < data_length; index++) { + location = resolve_location_index(profile, data_list[index]); + + if (location == NULL || cfl_list_is_empty(&location->lines)) { + result = encode_uint64_t(context, + CFL_FALSE, + "", + "", + data_list[index]); + } + else { + line = cfl_list_entry_first(&location->lines, struct cprof_line, _head); + function = resolve_function_index(profile, line->function_index); + + if (function != NULL) { + resolved = resolve_string_index(profile, function->name); + } + else { + resolved = NULL; + } + + if (resolved == NULL) { + result = encode_uint64_t(context, + CFL_FALSE, + "", + "", + data_list[index]); + } + else { + result = encode_string(context, + CFL_FALSE, + "\"", + "\"", + (char *) resolved); + } + } + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (index + 1 < data_length) { + result = encode_string(context, + CFL_FALSE, + "", + "", + separator); + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + } + + sds_result = cfl_sds_printf(&context->output_buffer, "%s", suffix); + if (sds_result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + static int encode_int64_t_array( struct cprof_text_encoding_context *context, int indent, @@ -855,6 +1187,8 @@ static int encode_cfl_kvlist( struct cfl_kvpair *last_entry; cfl_sds_t sds_result; struct cfl_list *iterator; + char *value_prefix; + char *value_suffix; int result; struct cfl_kvpair *entry; @@ -893,10 +1227,20 @@ static int encode_cfl_kvlist( return result; } + value_prefix = ""; + value_suffix = ""; + + if (entry->val != NULL && + (entry->val->type == CFL_VARIANT_STRING || + entry->val->type == CFL_VARIANT_BYTES)) { + value_prefix = "\""; + value_suffix = "\""; + } + result = encode_cfl_variant(context, CFL_FALSE, - "\"", - "\"", + value_prefix, + value_suffix, entry->val); if (result != CPROF_ENCODE_TEXT_SUCCESS) { @@ -1112,16 +1456,18 @@ static int encode_cprof_value_type( static int encode_cprof_sample( struct cprof_text_encoding_context *context, + struct cprof_profile *profile, struct cprof_sample *instance) { int result; - result = encode_uint64_t_array(context, - CFL_TRUE, - "Location index : [ ", - ", ", - "]\n", - instance->location_index, - instance->location_index_count); + result = encode_location_reference_array(context, + CFL_TRUE, + "Location index : [ ", + ", ", + "]\n", + profile, + instance->location_index, + instance->location_index_count); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1159,13 +1505,14 @@ static int encode_cprof_sample( return result; } - result = encode_uint64_t_array(context, - CFL_TRUE, - "Attributes : [ ", - ", ", - "]\n", - instance->attributes, - instance->attributes_count); + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1262,13 +1609,14 @@ static int encode_cprof_mapping( return result; } - result = encode_uint64_t_array(context, - CFL_TRUE, - "Attributes : [ ", - ", ", - "]\n", - instance->attributes, - instance->attributes_count); + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1326,13 +1674,42 @@ static int encode_cprof_line( struct cprof_profile *profile, struct cprof_line *instance) { + struct cprof_function *function; + const char *resolved; int result; - result = encode_int64_string_ref(context, - CFL_TRUE, - "Function index : ", - (int64_t) instance->function_index, - profile); + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_RESOLVED) { + function = resolve_function_index(profile, instance->function_index); + + if (function != NULL) { + resolved = resolve_string_index(profile, function->name); + } + else { + resolved = NULL; + } + + if (resolved != NULL) { + result = encode_string(context, + CFL_TRUE, + "Function : ", + "\n", + (char *) resolved); + } + else { + result = encode_uint64_t(context, + CFL_TRUE, + "Function index : ", + "\n", + instance->function_index); + } + } + else { + result = encode_uint64_t(context, + CFL_TRUE, + "Function index : ", + "\n", + instance->function_index); + } if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1369,6 +1746,8 @@ static int encode_cprof_location( struct cprof_location *instance) { struct cfl_list *iterator; + struct cprof_mapping *mapping; + const char *resolved; int result; struct cprof_line *line; @@ -1382,11 +1761,38 @@ static int encode_cprof_location( return result; } - result = encode_uint64_t(context, - CFL_TRUE, - "Mapping index : ", - "\n", - instance->mapping_index); + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_RESOLVED) { + mapping = resolve_mapping_index(profile, instance->mapping_index); + + if (mapping != NULL) { + resolved = resolve_string_index(profile, mapping->filename); + } + else { + resolved = NULL; + } + + if (resolved != NULL) { + result = encode_string(context, + CFL_TRUE, + "Mapping : ", + "\n", + (char *) resolved); + } + else { + result = encode_uint64_t(context, + CFL_TRUE, + "Mapping index : ", + "\n", + instance->mapping_index); + } + } + else { + result = encode_uint64_t(context, + CFL_TRUE, + "Mapping index : ", + "\n", + instance->mapping_index); + } if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1438,13 +1844,14 @@ static int encode_cprof_location( } } - result = encode_uint64_t_array(context, - CFL_TRUE, - "Attributes : [ ", - ", ", - "]\n", - instance->attributes, - instance->attributes_count); + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1717,7 +2124,7 @@ static int encode_cprof_profile( return result; } - result = encode_cprof_sample(context, sample); + result = encode_cprof_sample(context, instance, sample); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; @@ -1856,16 +2263,18 @@ static int encode_cprof_profile( } } - result = encode_int64_t_array(context, - CFL_TRUE, - "Location indices : [ ", - ", ", - "]\n", - instance->location_indices, - instance->location_indices_count); + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES) { + result = encode_int64_t_array(context, + CFL_TRUE, + "Location indices : [ ", + ", ", + "]\n", + instance->location_indices, + instance->location_indices_count); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } } if (!cfl_list_is_empty(&instance->functions)) { @@ -1926,49 +2335,51 @@ static int encode_cprof_profile( } } - result = encode_cfl_kvlist(context, - CFL_TRUE, - "Attribute table : {", - ", ", - " }\n", - instance->attribute_table); - - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } - - if (!cfl_list_is_empty(&instance->attribute_units)) { - result = encode_section_header_with_count(context, - "Attribute units", - cfl_list_size(&instance->attribute_units)); + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES) { + result = encode_cfl_kvlist(context, + CFL_TRUE, + "Attribute table : {", + ", ", + " }\n", + instance->attribute_table); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; } - result = increment_indentation_level(context); + if (!cfl_list_is_empty(&instance->attribute_units)) { + result = encode_section_header_with_count(context, + "Attribute units", + cfl_list_size(&instance->attribute_units)); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } - - cfl_list_foreach(iterator, - &instance->attribute_units) { - attribute_unit = cfl_list_entry( - iterator, - struct cprof_attribute_unit, _head); + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - result = encode_cprof_attribute_unit(context, attribute_unit); + result = increment_indentation_level(context); if (result != CPROF_ENCODE_TEXT_SUCCESS) { return result; } - } - result = decrement_indentation_level(context); + cfl_list_foreach(iterator, + &instance->attribute_units) { + attribute_unit = cfl_list_entry( + iterator, + struct cprof_attribute_unit, _head); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + result = encode_cprof_attribute_unit(context, attribute_unit); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } } } @@ -2030,41 +2441,43 @@ static int encode_cprof_profile( } } - result = encode_section_header_with_count(context, - "String table", - instance->string_table_count); + if (context->render_mode == CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES) { + result = encode_section_header_with_count(context, + "String table", + instance->string_table_count); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - result = increment_indentation_level(context); + result = increment_indentation_level(context); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - result = encode_string_array( - context, - CFL_TRUE, - "[ ", - ", ", - " ]\n", - (char **) instance->string_table, - instance->string_table_count); + result = encode_string_array( + context, + CFL_TRUE, + "[ ", + ", ", + " ]\n", + (char **) instance->string_table, + instance->string_table_count); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - result = decrement_indentation_level(context); + result = decrement_indentation_level(context); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } } result = encode_int64_t(context, @@ -2518,7 +2931,8 @@ void print_profile(struct cprof_profile *profile) int cprof_encode_text_create(cfl_sds_t *result_buffer, - struct cprof *profile) + struct cprof *profile, + int render_mode) { int result; struct cprof_text_encoding_context context; @@ -2527,6 +2941,11 @@ int cprof_encode_text_create(cfl_sds_t *result_buffer, memset(&context, 0, sizeof(context)); + if (render_mode != CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES && + render_mode != CPROF_ENCODE_TEXT_RENDER_RESOLVED) { + return CPROF_ENCODE_TEXT_INVALID_ARGUMENT_ERROR; + } + context.output_buffer = cfl_sds_create_size(128); if (context.output_buffer == NULL) { @@ -2535,7 +2954,7 @@ int cprof_encode_text_create(cfl_sds_t *result_buffer, context.indentation_buffer = cfl_sds_create_size(256); - if (context.output_buffer == NULL) { + if (context.indentation_buffer == NULL) { cfl_sds_destroy(context.output_buffer); return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; @@ -2547,6 +2966,7 @@ int cprof_encode_text_create(cfl_sds_t *result_buffer, context.indentation_level_size = 4; context.indentation_character = ' '; + context.render_mode = render_mode; if (!cfl_list_is_empty(&profile->profiles)) { diff --git a/src/cprof_opentelemetry_variant_helpers.c b/src/cprof_opentelemetry_variant_helpers.c index d0304d7..27f3990 100644 --- a/src/cprof_opentelemetry_variant_helpers.c +++ b/src/cprof_opentelemetry_variant_helpers.c @@ -30,10 +30,10 @@ static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyVa int result; if (source == NULL) { - return NULL; + return cfl_variant_create_from_string(""); } if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { - result_instance = cfl_variant_create_from_string(source->string_value); + result_instance = cfl_variant_create_from_string(source->string_value != NULL ? source->string_value : ""); } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) { result_instance = cfl_variant_create_from_bool(source->bool_value); @@ -45,6 +45,10 @@ static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyVa result_instance = cfl_variant_create_from_double(source->double_value); } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE) { + if (source->kvlist_value == NULL) { + return cfl_variant_create_from_string(""); + } + new_child_kvlist = cfl_kvlist_create(); if (new_child_kvlist == NULL) { return NULL; @@ -66,6 +70,10 @@ static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyVa } } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE) { + if (source->array_value == NULL) { + return cfl_variant_create_from_string(""); + } + new_child_array = cfl_array_create(source->array_value->n_values); if (new_child_array == NULL) { @@ -90,6 +98,9 @@ static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyVa result_instance = cfl_variant_create_from_bytes((char *) source->bytes_value.data, source->bytes_value.len, CFL_FALSE); } + else { + result_instance = cfl_variant_create_from_string(""); + } return result_instance; } @@ -147,7 +158,7 @@ static int clone_kvlist(struct cfl_kvlist *target, result = clone_kvlist_entry(target, source->values[index]); } - return 0; + return result; } static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, @@ -174,6 +185,13 @@ static int clone_kvlist_entry(struct cfl_kvlist *target, { struct cfl_variant *new_child_instance; int result; + char *key; + + if (source == NULL) { + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; + } + + key = source->key != NULL ? source->key : ""; new_child_instance = clone_variant(source->value); @@ -181,7 +199,7 @@ static int clone_kvlist_entry(struct cfl_kvlist *target, return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = cfl_kvlist_insert(target, source->key, new_child_instance); + result = cfl_kvlist_insert(target, key, new_child_instance); if (result) { cfl_variant_destroy(new_child_instance); @@ -201,22 +219,32 @@ static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, size_t index; const char *key; struct cfl_variant *val; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry; for (index = 0; index < source_length; index++) { - Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry = source[index]; - - if (entry == NULL) { - continue; - } + entry = source[index]; key = ""; - if (string_table != NULL && entry->key_strindex >= 0 && + if (entry != NULL && + string_table != NULL && entry->key_strindex >= 0 && (size_t)entry->key_strindex < string_table_len && string_table[entry->key_strindex] != NULL) { key = string_table[entry->key_strindex]; } - val = clone_variant(entry->value); + /* + * Preserve positional alignment with OTLP attribute table indexes. + * Even null/sentinel source entries get a placeholder null value, + * so downstream index-based resolution remains stable. + */ + if (entry == NULL || entry->value == NULL || + entry->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE__NOT_SET) { + val = cfl_variant_create_from_string(""); + } + else { + val = clone_variant(entry->value); + } + if (val == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } diff --git a/src/cprof_profile.c b/src/cprof_profile.c index 2c833a4..608aedd 100644 --- a/src/cprof_profile.c +++ b/src/cprof_profile.c @@ -230,6 +230,7 @@ size_t cprof_profile_string_add(struct cprof_profile *profile, char *str, int st int alloc_size = 64; size_t id; size_t new_size; + cfl_sds_t *new_table; if (!str) { return -1; @@ -257,11 +258,12 @@ size_t cprof_profile_string_add(struct cprof_profile *profile, char *str, int st /* check there is enough room for a new entry */ if (profile->string_table_count >= profile->string_table_size) { new_size = profile->string_table_size + alloc_size; - profile->string_table = realloc(profile->string_table, new_size * sizeof(cfl_sds_t)); - if (!profile->string_table) { + new_table = realloc(profile->string_table, new_size * sizeof(cfl_sds_t)); + if (!new_table) { return -1; } - profile->string_table_size = alloc_size; + profile->string_table = new_table; + profile->string_table_size = new_size; } id = profile->string_table_count; @@ -307,4 +309,4 @@ int cprof_profile_add_comment(struct cprof_profile *profile, int64_t comment) profile->comments_count++; return 0; -} \ No newline at end of file +} diff --git a/src/cprof_sample.c b/src/cprof_sample.c index fb54df5..e066ca4 100644 --- a/src/cprof_sample.c +++ b/src/cprof_sample.c @@ -40,18 +40,13 @@ int cprof_sample_add_location_index(struct cprof_sample *sample, uint64_t locati uint64_t *reallocated_location_index; if (sample->location_index == NULL) { - /* - * if location index is NULL, assign a default location. We set an empty string to index 0 - * since that's the way for protobuf differentiate between unset or NULL - */ sample->location_index = calloc(1, alloc_slots * sizeof(uint64_t)); if (sample->location_index == NULL) { return -1; } - sample->location_index[0] = 0; /* an empty string */ - sample->location_index_count = 1; + sample->location_index_count = 0; sample->location_index_size = alloc_slots; } diff --git a/tests/text_encoder.c b/tests/text_encoder.c index c1faf9e..433bbc3 100644 --- a/tests/text_encoder.c +++ b/tests/text_encoder.c @@ -640,7 +640,9 @@ static void test_encoder() TEST_CHECK(offset == sizeof(serialized_data)); if (result == CPROF_DECODE_MSGPACK_SUCCESS) { - result = cprof_encode_text_create(&text_encoder_result, context); + result = cprof_encode_text_create(&text_encoder_result, + context, + CPROF_ENCODE_TEXT_RENDER_DICTIONARIES_AND_INDEXES); TEST_CHECK(result == CPROF_ENCODE_TEXT_SUCCESS);