diff --git a/lib/cmetrics/.github/workflows/packages.yaml b/lib/cmetrics/.github/workflows/packages.yaml index ee4fd0a4145..e646b5544f3 100644 --- a/lib/cmetrics/.github/workflows/packages.yaml +++ b/lib/cmetrics/.github/workflows/packages.yaml @@ -39,7 +39,7 @@ jobs: echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.format }}-arm64 path: | @@ -64,7 +64,7 @@ jobs: echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.format }}-amd64 path: | @@ -91,7 +91,7 @@ jobs: echo ${{ matrix.config.format }} | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.config.format }}-${{matrix.config.arch}} path: | @@ -109,7 +109,7 @@ jobs: contents: write steps: - name: Download all artefacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: artifacts/ diff --git a/lib/cmetrics/CMakeLists.txt b/lib/cmetrics/CMakeLists.txt index 894d6f24aa8..c3913c1cd81 100644 --- a/lib/cmetrics/CMakeLists.txt +++ b/lib/cmetrics/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # CMetrics Version set(CMT_VERSION_MAJOR 2) set(CMT_VERSION_MINOR 0) -set(CMT_VERSION_PATCH 4) +set(CMT_VERSION_PATCH 5) set(CMT_VERSION_STR "${CMT_VERSION_MAJOR}.${CMT_VERSION_MINOR}.${CMT_VERSION_PATCH}") # Include helpers diff --git a/lib/cmetrics/include/cmetrics/cmt_compat.h b/lib/cmetrics/include/cmetrics/cmt_compat.h index 004c0aa88dd..9fc90a386a3 100644 --- a/lib/cmetrics/include/cmetrics/cmt_compat.h +++ b/lib/cmetrics/include/cmetrics/cmt_compat.h @@ -22,7 +22,13 @@ #include #ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #include +#undef WIN32_LEAN_AND_MEAN +#else +#include +#endif #endif static inline struct tm *cmt_platform_gmtime_r(const time_t *timep, struct tm *result) diff --git a/lib/cmetrics/src/cmt_atomic_msvc.c b/lib/cmetrics/src/cmt_atomic_msvc.c index 3fd66dbf799..3da37a75545 100644 --- a/lib/cmetrics/src/cmt_atomic_msvc.c +++ b/lib/cmetrics/src/cmt_atomic_msvc.c @@ -18,7 +18,13 @@ */ #include +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #include +#undef WIN32_LEAN_AND_MEAN +#else +#include +#endif /* This allows cmt_atomic_initialize to be automatically called * as soon as the program starts if enabled. diff --git a/lib/cmetrics/src/cmt_cat.c b/lib/cmetrics/src/cmt_cat.c index 49f44a60acf..bd01fc01f81 100644 --- a/lib/cmetrics/src/cmt_cat.c +++ b/lib/cmetrics/src/cmt_cat.c @@ -689,9 +689,6 @@ int cmt_cat_counter(struct cmt *cmt, struct cmt_counter *counter, return -1; } - c->aggregation_type = counter->aggregation_type; - c->allow_reset = counter->allow_reset; - if (filtered_map != NULL) { ret = cmt_cat_copy_map(&c->opts, c->map, filtered_map); if (ret == -1) { @@ -840,8 +837,6 @@ int cmt_cat_histogram(struct cmt *cmt, struct cmt_histogram *histogram, return -1; } - hist->aggregation_type = histogram->aggregation_type; - if (filtered_map != NULL) { ret = cmt_cat_copy_map(&hist->opts, hist->map, filtered_map); if (ret == -1) { diff --git a/lib/cmetrics/src/cmt_decode_opentelemetry.c b/lib/cmetrics/src/cmt_decode_opentelemetry.c index 4ed1e13a711..299d0a421f0 100644 --- a/lib/cmetrics/src/cmt_decode_opentelemetry.c +++ b/lib/cmetrics/src/cmt_decode_opentelemetry.c @@ -71,6 +71,9 @@ static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyVa if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { result_instance = cfl_variant_create_from_string(source->string_value); } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX) { + result_instance = cfl_variant_create_from_string(""); + } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) { result_instance = cfl_variant_create_from_bool(source->bool_value); } @@ -154,6 +157,11 @@ static int clone_array_entry(struct cfl_array *target, struct cfl_variant *new_child_instance; int result; + if (source != NULL && + source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX) { + return CMT_DECODE_OPENTELEMETRY_SUCCESS; + } + new_child_instance = clone_variant(source); if (new_child_instance == NULL) { return CMT_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -183,7 +191,7 @@ static int clone_kvlist(struct cfl_kvlist *target, result = clone_kvlist_entry(target, source->values[index]); } - return 0; + return result; } static int clone_kvlist_entry(struct cfl_kvlist *target, @@ -192,6 +200,15 @@ static int clone_kvlist_entry(struct cfl_kvlist *target, struct cfl_variant *new_child_instance; int result; + if (source == NULL) { + return CMT_DECODE_OPENTELEMETRY_SUCCESS; + } + + if (source->value != NULL && + source->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX) { + return CMT_DECODE_OPENTELEMETRY_SUCCESS; + } + new_child_instance = clone_variant(source->value); if (new_child_instance == NULL) { @@ -625,6 +642,10 @@ static int decode_data_point_labels(struct cmt *cmt, if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { result = append_new_metric_label_value(metric, attribute->value->string_value, 0); } + else if (attribute->value->value_case == + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX) { + result = append_new_metric_label_value(metric, "", 0); + } else if (attribute->value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) { result = append_new_metric_label_value(metric, (char *) attribute->value->bytes_value.data, diff --git a/lib/cmetrics/tests/cat.c b/lib/cmetrics/tests/cat.c index 397125277bd..3ef59fca3e1 100644 --- a/lib/cmetrics/tests/cat.c +++ b/lib/cmetrics/tests/cat.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -612,99 +611,6 @@ void test_histogram_populated_to_empty() cmt_destroy(cmt2); } -void test_cat_preserves_aggregation_metadata() -{ - int ret; - uint64_t ts; - uint64_t positive_counts[2] = {3, 4}; - struct cmt *src; - struct cmt *dst; - struct cmt_counter *src_counter; - struct cmt_counter *dst_counter; - struct cmt_histogram *src_histogram; - struct cmt_histogram *dst_histogram; - struct cmt_exp_histogram *src_exp_histogram; - struct cmt_exp_histogram *dst_exp_histogram; - struct cmt_histogram_buckets *buckets; - - src = cmt_create(); - TEST_CHECK(src != NULL); - - dst = cmt_create(); - TEST_CHECK(dst != NULL); - - buckets = cmt_histogram_buckets_create(3, 1.0, 5.0, 10.0); - TEST_CHECK(buckets != NULL); - - src_counter = cmt_counter_create(src, "otlp", "cat", "counter", "counter", - 0, NULL); - TEST_CHECK(src_counter != NULL); - - src_histogram = cmt_histogram_create(src, "otlp", "cat", "histogram", "histogram", - buckets, 0, NULL); - TEST_CHECK(src_histogram != NULL); - - src_exp_histogram = cmt_exp_histogram_create(src, "otlp", "cat", "exp_histogram", - "exp histogram", 0, NULL); - TEST_CHECK(src_exp_histogram != NULL); - - ts = cfl_time_now(); - cmt_counter_allow_reset(src_counter); - src_counter->aggregation_type = CMT_AGGREGATION_TYPE_DELTA; - cmt_counter_add(src_counter, ts, 4.0, 0, NULL); - - src_histogram->aggregation_type = CMT_AGGREGATION_TYPE_DELTA; - cmt_histogram_observe(src_histogram, ts, 2.5, 0, NULL); - - src_exp_histogram->aggregation_type = CMT_AGGREGATION_TYPE_DELTA; - ret = cmt_exp_histogram_set_default(src_exp_histogram, - ts, - 2, - 1, - 0.0, - 0, - 2, - positive_counts, - 0, - 0, - NULL, - CMT_TRUE, - 7.5, - 7, - 0, - NULL); - TEST_CHECK(ret == 0); - - ret = cmt_cat(dst, src); - TEST_CHECK(ret == 0); - - TEST_CHECK(cfl_list_size(&dst->counters) == 1); - dst_counter = cfl_list_entry_first(&dst->counters, struct cmt_counter, _head); - TEST_CHECK(dst_counter != NULL); - if (dst_counter != NULL) { - TEST_CHECK(dst_counter->allow_reset == CMT_TRUE); - TEST_CHECK(dst_counter->aggregation_type == CMT_AGGREGATION_TYPE_DELTA); - } - - TEST_CHECK(cfl_list_size(&dst->histograms) == 1); - dst_histogram = cfl_list_entry_first(&dst->histograms, struct cmt_histogram, _head); - TEST_CHECK(dst_histogram != NULL); - if (dst_histogram != NULL) { - TEST_CHECK(dst_histogram->aggregation_type == CMT_AGGREGATION_TYPE_DELTA); - } - - TEST_CHECK(cfl_list_size(&dst->exp_histograms) == 1); - dst_exp_histogram = cfl_list_entry_first(&dst->exp_histograms, - struct cmt_exp_histogram, _head); - TEST_CHECK(dst_exp_histogram != NULL); - if (dst_exp_histogram != NULL) { - TEST_CHECK(dst_exp_histogram->aggregation_type == CMT_AGGREGATION_TYPE_DELTA); - } - - cmt_destroy(src); - cmt_destroy(dst); -} - TEST_LIST = { {"cat", test_cat}, {"duplicate_metrics", test_duplicate_metrics}, @@ -712,6 +618,5 @@ TEST_LIST = { {"histogram_mismatched_buckets", test_histogram_mismatched_buckets}, {"histogram_empty_to_populated", test_histogram_empty_to_populated}, {"histogram_populated_to_empty", test_histogram_populated_to_empty}, - {"cat_preserves_aggregation_metadata", test_cat_preserves_aggregation_metadata}, { 0 } }; diff --git a/lib/cmetrics/tests/prometheus_remote_write_payload.bin b/lib/cmetrics/tests/prometheus_remote_write_payload.bin index 60b7f2b52cc..ceff6756c53 100644 Binary files a/lib/cmetrics/tests/prometheus_remote_write_payload.bin and b/lib/cmetrics/tests/prometheus_remote_write_payload.bin differ diff --git a/lib/cprofiles/.github/workflows/build.yaml b/lib/cprofiles/.github/workflows/build.yaml index 710f1fab612..4143bf146e5 100644 --- a/lib/cprofiles/.github/workflows/build.yaml +++ b/lib/cprofiles/.github/workflows/build.yaml @@ -42,9 +42,11 @@ jobs: sed -i -e "s/^mirrorlist=http:\/\/mirrorlist.centos.org/#mirrorlist=http:\/\/mirrorlist.centos.org/g" /etc/yum.repos.d/CentOS-Base.repo sed -i -e "s/^#baseurl=http:\/\/mirror.centos.org/baseurl=http:\/\/vault.centos.org/g" /etc/yum.repos.d/CentOS-Base.repo yum -y update && \ - yum install -y ca-certificates cmake curl-devel gcc gcc-c++ git make wget && \ - yum install -y epel-release - yum install -y cmake3 + yum install -y ca-certificates curl-devel gcc gcc-c++ git make wget && \ + yum install -y epel-release && \ + yum install -y python3 python3-pip + python3 -m pip install --user --upgrade "cmake>=3.20,<4" + echo "$(python3 -m site --user-base)/bin" >> "$GITHUB_PATH" shell: bash - name: Clone repo without submodules (1.8.3 version of Git) @@ -69,7 +71,7 @@ jobs: - name: Run compilation run: | - cmake3 -DCPROF_DEV=on ../ + cmake -DCPROF_DEV=on ../ make shell: bash working-directory: cprofiles/build @@ -82,7 +84,9 @@ jobs: - name: Set up base image dependencies run: | apt-get update - apt-get install -y build-essential cmake make git + apt-get install -y build-essential git make python3 python3-pip + python3 -m pip install --user --upgrade "cmake>=3.20,<4" + echo "$(python3 -m site --user-base)/bin" >> "$GITHUB_PATH" shell: bash - uses: actions/checkout@v6 @@ -121,9 +125,12 @@ jobs: apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ - cmake \ file \ - make + make \ + python3 \ + python3-pip + python3 -m pip install --user --upgrade "cmake>=3.20,<4" + export PATH="$(python3 -m site --user-base)/bin:$PATH" export CC=${{ env.compiler }} cd build cmake -DCPROF_TESTS=On ../ diff --git a/lib/cprofiles/.github/workflows/packages.yaml b/lib/cprofiles/.github/workflows/packages.yaml index c423590747a..e0aa413fd45 100644 --- a/lib/cprofiles/.github/workflows/packages.yaml +++ b/lib/cprofiles/.github/workflows/packages.yaml @@ -39,7 +39,7 @@ jobs: echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.format }}-arm64 path: | @@ -63,7 +63,7 @@ jobs: echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.format }}-amd64 path: | @@ -90,7 +90,7 @@ jobs: echo ${{ matrix.config.format }} | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.config.format }}-${{matrix.config.arch}} path: | @@ -107,7 +107,7 @@ jobs: contents: write steps: - name: Download all artefacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: artifacts/ diff --git a/lib/cprofiles/CMakeLists.txt b/lib/cprofiles/CMakeLists.txt index 3f2fa83663c..881d875d0bc 100644 --- a/lib/cprofiles/CMakeLists.txt +++ b/lib/cprofiles/CMakeLists.txt @@ -1,368 +1,54 @@ -cmake_minimum_required(VERSION 3.20) -project(cprofiles C) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# CMetrics Version -set(CPROF_VERSION_MAJOR 0) -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 -include(cmake/macros.cmake) -include(CheckCSourceCompiles) -include(GNUInstallDirs) - -# Define macro to identify Windows system (without Cygwin) -if(CMAKE_SYSTEM_NAME MATCHES "Windows") - set(CPROF_SYSTEM_WINDOWS On) - add_definitions(-DCPROF_SYSTEM_WINDOWS) -endif() - -# Define macro to identify macOS system -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - set(CPROF_SYSTEM_MACOS On) - add_definitions(-DCPROF_SYSTEM_MACOS) -endif() - -if(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -endif() - -# Define __CPROF_FILENAME__ consistently across Operating Systems -if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__CPROF_FILENAME__='\"$$(subst ${CMAKE_SOURCE_DIR}/,,$$(abspath $$<))\"'") -else() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__CPROF_FILENAME__=__FILE__") -endif() - -# Configuration options -option(CPROF_DEV "Enable development mode" No) -option(CPROF_DEBUG "Enable debug mode" No) -option(CPROF_TESTS "Enable unit testing" No) -option(CPROF_INSTALL_TARGETS "Enable subdirectory library installations" Yes) - -if(CPROF_DEV) - set(CPROF_TESTS Yes) - set(CPROF_DEBUG Yes) -endif() - -if(CPROF_DEBUG) - set(CMAKE_BUILD_TYPE Debug) -endif() - -# Bundled libraries -include(cmake/libraries.cmake) -include(cmake/headers.cmake) - -# Include headers and dependency headers -include_directories( - src - include +set(src + cprofiles.c + cprof_attribute_unit.c + cprof_function.c + cprof_instrumentation_scope.c + cprof_line.c + cprof_link.c + cprof_location.c + cprof_mapping.c + cprof_profile.c + cprof_resource_profiles.c + cprof_resource.c + cprof_sample.c + cprof_scope_profiles.c + cprof_decode_opentelemetry.c + cprof_encode_opentelemetry.c + cprof_encode_text.c + cprof_encode_msgpack.c + cprof_decode_msgpack.c + cprof_mpack_utils.c ) -# timespec_get() support -check_c_source_compiles(" - #include - int main() { - struct tm tm; - return timespec_get(&tm, TIME_UTC); - }" CPROF_HAVE_TIMESPEC_GET) -if(CPROF_HAVE_TIMESPEC_GET) - CPROF_DEFINITION(CPROF_HAVE_TIMESPEC_GET) -endif() - -# gmtime_r() support -check_c_source_compiles(" - #include - int main() { - struct tm tm; - struct timespec tms; - return gmtime_r(&tms.tv_sec, &tm); - }" CPROF_HAVE_GMTIME_R) -if(CPROF_HAVE_GMTIME_R) - CPROF_DEFINITION(CPROF_HAVE_GMTIME_R) -endif() - -# gmtime_s() support -check_c_source_compiles(" - #include - int main() { - struct tm tm; - struct timespec tms; - return gmtime_s(&tm, &tms.tv_sec); - }" CPROF_HAVE_GMTIME_S) -if(CPROF_HAVE_GMTIME_S) - CPROF_DEFINITION(CPROF_HAVE_GMTIME_S) -endif() - -# clock_get_time() support for macOS. -check_c_source_compiles(" - #include - #include - int main() { - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - return mach_port_deallocate(mach_task_self(), cclock); - }" CPROF_HAVE_CLOCK_GET_TIME) -if(CPROF_HAVE_CLOCK_GET_TIME) - CPROF_DEFINITION(CPROF_HAVE_CLOCK_GET_TIME) -endif() - -# Check if 'C Floppy' library is available in the environment, if not, -# we will try to build a local copy at a later stage -check_c_source_compiles(" - #include - int main() { - return cfl_found(); - }" CPROF_HAVE_CFL) -if(CPROF_HAVE_CFL) - CPROF_DEFINITION(CPROF_HAVE_CFL) - message(STATUS "CFL found in the system. OK") -endif() - -# Check if fluent-otel-proto library is available in the environment, if not, -# we will try to build a local copy at a later stage -check_c_source_compiles(" - #include - int main() { - return fluent_otel_found(); - }" CPROF_HAVE_FLUENT_OTEL_PROTO) -if(CPROF_HAVE_FLUENT_OTEL_PROTO) - CPROF_DEFINITION(CPROF_HAVE_FLUENT_OTEL_PROTO) -endif() +set(src + ${src} + ) -# Configure header files +# Configure generated public headers used by cprofiles.h configure_file( - "${PROJECT_SOURCE_DIR}/include/cprofiles/cprof_info.h.in" - "${PROJECT_BINARY_DIR}/include/cprofiles/cprof_info.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/cprofiles/cprof_info.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/cprofiles/cprof_info.h" ) configure_file( - "${PROJECT_SOURCE_DIR}/include/cprofiles/cprof_version.h.in" - "${PROJECT_BINARY_DIR}/include/cprofiles/cprof_version.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/cprofiles/cprof_version.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/cprofiles/cprof_version.h" ) -include_directories("${PROJECT_BINARY_DIR}/include/") +# Static Library +add_library(cprofiles-static STATIC ${src}) +target_link_libraries(cprofiles-static mpack-static cfl-static fluent-otel-proto) -# Installation Directories -# ======================== -if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(CPROF_INSTALL_LIBDIR "lib") - set(CPROF_INSTALL_INCLUDEDIR "include") +# Install Library +if(MSVC) + # Rename the output for Windows environment to avoid naming issues + set_target_properties(cprofiles-static PROPERTIES OUTPUT_NAME libcprofiles) else() - set(CPROF_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") - set(CPROF_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") -endif() - -# mpack -if(NOT TARGET mpack-static) - include_directories(lib/mpack/src/) - add_subdirectory(lib/mpack EXCLUDE_FROM_ALL) - - if (CPROF_INSTALL_TARGETS) - install(TARGETS mpack-static - RUNTIME DESTINATION ${CPROF_INSTALL_BINDIR} - LIBRARY DESTINATION ${CPROF_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CPROF_INSTALL_LIBDIR} - COMPONENT library) - - install(FILES lib/mpack/src/mpack/mpack.h - DESTINATION ${CPROF_INSTALL_INCLUDEDIR}/mpack - COMPONENT headers - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - endif() -endif() - -# C Floppy -if (NOT CPROF_HAVE_CFL) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${CPROF_PATH_LIB_CFL}/include) - add_subdirectory(lib/cfl) - CPROF_DEFINITION(CPROF_HAVE_CFL) - CPROF_DEFINITION(CPROF_HAVE_CFL_INTERNAL) - if (CPROF_INSTALL_TARGETS) - file(GLOB bundledCFLHeaders "lib/cfl/include/cfl/*.h") - install(FILES ${bundledCFLHeaders} - DESTINATION ${CPROF_INSTALL_INCLUDEDIR}/cfl - COMPONENT headers - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - - install(TARGETS cfl-static - RUNTIME DESTINATION ${CPROF_INSTALL_BINDIR} - LIBRARY DESTINATION ${CPROF_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CPROF_INSTALL_LIBDIR} - COMPONENT library) - - # xxHash - install(FILES lib/cfl/lib/xxhash/xxh3.h - DESTINATION ${CPROF_INSTALL_INCLUDEDIR} - COMPONENT headers - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - install(FILES lib/cfl/lib/xxhash/xxhash.h - DESTINATION ${CPROF_INSTALL_INCLUDEDIR} - COMPONENT headers - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - - install(TARGETS xxhash - RUNTIME DESTINATION ${CPROF_INSTALL_BINDIR} - LIBRARY DESTINATION ${CPROF_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CPROF_INSTALL_LIBDIR} - COMPONENT library) - endif() -endif() - -# fluent-otel-proto -if (NOT CPROF_HAVE_FLUENT_OTEL_PROTO) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${CPROF_PATH_LIB_FLUENT_OTEL_PROTO}/include) - CPROF_OPTION(FLUENT_PROTO_PROFILES "on") - CPROF_OPTION(FLUENT_PROTO_EXAMPLES "off") - add_subdirectory(lib/fluent-otel-proto) - CPROF_DEFINITION(CPROF_HAVE_FLUENT_OTEL_PROTO) - CPROF_DEFINITION(CPROF_HAVE_FLUENT_OTEL_PROTO_INTERNAL) - if (CPROF_INSTALL_TARGETS) - file(GLOB bundledOTELProtoHeaders "lib/fluent-otel-proto/include/fluent-otel-proto/*.h") - install(FILES ${bundledOTELProtoHeaders} - DESTINATION ${CPROF_INSTALL_INCLUDEDIR}/fluent-otel-proto - COMPONENT headers - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - - install(TARGETS fluent-otel-proto - RUNTIME DESTINATION ${CPROF_INSTALL_BINDIR} - LIBRARY DESTINATION ${CPROF_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CPROF_INSTALL_LIBDIR} - COMPONENT library) - endif() -endif() - -# Source code -add_subdirectory(include) -add_subdirectory(src) - -# Tests -if(CPROF_TESTS) - enable_testing() - add_subdirectory(tests) -endif() - -# Installer Generation (Cpack) -# ============================ - -set(CPACK_PACKAGE_VERSION ${CPROFILES_VERSION_STR}) -set(CPACK_PACKAGE_NAME "cprofiles") -set(CPACK_PACKAGE_RELEASE 1) -set(CPACK_PACKAGE_CONTACT "Eduardo Silva ") -set(CPACK_PACKAGE_VENDOR "Calyptia") -set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") -set(CPACK_PACKAGING_INSTALL_PREFIX "/") - -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}") - -if(CPROF_SYSTEM_WINDOWS) - set(CPACK_GENERATOR "ZIP") - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-win64") - else() - set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-win32") - endif() -endif() - - -# Enable components -set(CPACK_DEB_COMPONENT_INSTALL ON) -set(CPACK_RPM_COMPONENT_INSTALL ON) -set(CPACK_productbuild_COMPONENT_INSTALL ON) -set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} binary library headers) -set(CPACK_COMPONENTS_GROUPING "ONE_PER_GROUP") - -set(CPACK_COMPONENT_BINARY_GROUP "RUNTIME") -set(CPACK_COMPONENT_LIBRARY_GROUP "RUNTIME") - -# Debian package setup and name sanitizer -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - -find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") -if(DPKG_PROGRAM) - execute_process( - COMMAND ${DPKG_PROGRAM} --print-architecture - OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - set(CPACK_DEBIAN_HEADERS_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}-headers.deb") - set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") - set(CPACK_DEBIAN_RUNTIME_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") - set(CPACK_DEBIAN_RUNTIME_PACKAGE_CONTROL_EXTRA - ${CMAKE_CURRENT_SOURCE_DIR}/debian/conffiles - ) -endif() - -# RPM Generation information -set(CPACK_RPM_PACKAGE_GROUP "System Environment/Daemons") -set(CPACK_RPM_PACKAGE_LICENSE "Apache v2.0") -set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE}) -set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/cpack/description") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A standalone library to create and manipulate metrics in C") -set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#") -set(CPACK_RPM_USER_FILELIST - "%ignore /lib" - "%ignore /lib64" - "%ignore /lib64/pkgconfig" - "%ignore /usr/local" - "%ignore /usr/local/bin") - -set(CPACK_RPM_PACKAGE_AUTOREQ ON) -set(CPACK_RPM_RUNTIME_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") -set(CPACK_RPM_HEADERS_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}-headers.rpm") -set(CPACK_RPM_RUNTIME_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}.rpm") - -# CPack: DEB -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - -# CPack: Windows System -if(CPACK_GENERATOR MATCHES "ZIP") - set(CPACK_MONOLITHIC_INSTALL 1) - set(CPACK_PACKAGE_INSTALL_DIRECTORY "cprofiles") -endif() - -# CPack: macOS w/ productbuild -if(CPROF_SYSTEM_MACOS) - # Determine the platform suffix - execute_process( - COMMAND uname -m - RESULT_VARIABLE UNAME_M_RESULT - OUTPUT_VARIABLE UNAME_ARCH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if (UNAME_M_RESULT EQUAL 0 AND UNAME_ARCH STREQUAL "arm64") - set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CPROF_VERSION_STR}-apple) - elseif(UNAME_M_RESULT EQUAL 0 AND UNAME_ARCH STREQUAL "x86_64") - set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CPROF_VERSION_STR}-intel) - else() - set(CMETRICS_PKG ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CPROF_VERSION_STR}-${UNAME_ARCH}) - endif() - - if (CPACK_GENERATOR MATCHES "productbuild") - set(CPACK_SET_DESTDIR "ON") - configure_file(LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt) - find_program(CONVERTER textutil) - if (NOT CONVERTER) - message(FATAL_ERROR "textutil not found.") - endif() - if (CONVERTER) - execute_process(COMMAND ${CONVERTER} -convert html "${CMAKE_CURRENT_SOURCE_DIR}/README.md" -output "${CMAKE_CURRENT_SOURCE_DIR}/README.html") - endif() - set(CPACK_PACKAGE_FILE_NAME "${CMETRICS_PKG}") - set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt) - set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.html) - set(CPACK_PRODUCTBUILD_IDENTIFIER "com.calyptia.${CPACK_PACKAGE_NAME}") - endif() -endif() - -# Create tarball -add_custom_target(cprofiles_tarball COMMAND "bash" "${CMAKE_CURRENT_SOURCE_DIR}/create-submoduled-tarball.sh" "cprofiles-${CPROF_VERSION_STR}") - -include(CPack) + set_target_properties(cprofiles-static PROPERTIES OUTPUT_NAME cprofiles) +endif(MSVC) + +install(TARGETS cprofiles-static + RUNTIME DESTINATION ${CPROF_INSTALL_BINDIR} + LIBRARY DESTINATION ${CPROF_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CPROF_INSTALL_LIBDIR} + COMPONENT library) diff --git a/lib/cprofiles/cprof_attribute_unit.c b/lib/cprofiles/cprof_attribute_unit.c new file mode 100644 index 00000000000..aee9ab39872 --- /dev/null +++ b/lib/cprofiles/cprof_attribute_unit.c @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_attribute_unit *cprof_attribute_unit_create(struct cprof_profile *profile) +{ + struct cprof_attribute_unit *instance; + + instance = calloc(1, sizeof(struct cprof_attribute_unit)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_add(&instance->_head, &profile->attribute_units); + + return instance; +} + +void cprof_attribute_unit_destroy(struct cprof_attribute_unit *instance) +{ + if (instance != NULL) { + free(instance); + } +} diff --git a/lib/cprofiles/cprof_decode_msgpack.c b/lib/cprofiles/cprof_decode_msgpack.c new file mode 100644 index 00000000000..5ba805dfbbe --- /dev/null +++ b/lib/cprofiles/cprof_decode_msgpack.c @@ -0,0 +1,2234 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include + +static int unpack_context_header(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_map(reader, + callbacks, + user_data); +} + +static int unpack_resource_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_resource *resource; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + resource = (struct cprof_resource *) user_data; + + if (resource->attributes != NULL) { + cfl_kvlist_destroy(resource->attributes); + + resource->attributes = NULL; + } + + result = unpack_cfl_kvlist(reader, &resource->attributes); + + if (result != 0) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return CPROF_DECODE_MSGPACK_SUCCESS; +} + +static int unpack_resource_dropped_attribute_count(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_resource *resource; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + resource = (struct cprof_resource *) user_data; + + return cprof_mpack_consume_uint32_tag(reader, &resource->dropped_attributes_count); +} + +static int unpack_resource_profiles_entry_resource(mpack_reader_t *reader, size_t index, void *user_data) +{ + int result; + struct cprof_resource *resource; + struct cprof_resource_profiles *profiles; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"attributes", unpack_resource_attributes}, + {"dropped_attribute_count", unpack_resource_dropped_attribute_count}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profiles = (struct cprof_resource_profiles *) user_data; + + resource = cprof_resource_create(NULL); + + if (resource == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) resource); + + if (result == CPROF_DECODE_MSGPACK_SUCCESS) { + if (profiles->resource != NULL) { + cprof_resource_destroy(profiles->resource); + } + + profiles->resource = resource; + } + else { + cprof_resource_destroy(resource); + } + + return result; +} + +static int unpack_instrumentation_scope_name(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_instrumentation_scope *instrumentation_scope; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + instrumentation_scope = (struct cprof_instrumentation_scope *) user_data; + + if (instrumentation_scope->name != NULL) { + cfl_sds_destroy(instrumentation_scope->name); + + instrumentation_scope->name = NULL; + } + + return cprof_mpack_consume_string_or_nil_tag(reader, &instrumentation_scope->name); +} + +static int unpack_instrumentation_scope_version(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_instrumentation_scope *instrumentation_scope; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + instrumentation_scope = (struct cprof_instrumentation_scope *) user_data; + + if (instrumentation_scope->version != NULL) { + cfl_sds_destroy(instrumentation_scope->version); + + instrumentation_scope->version = NULL; + } + + return cprof_mpack_consume_string_or_nil_tag(reader, &instrumentation_scope->version); +} + +static int unpack_instrumentation_scope_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_instrumentation_scope *instrumentation_scope; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + instrumentation_scope = (struct cprof_instrumentation_scope *) user_data; + + if (instrumentation_scope->attributes != NULL) { + cfl_kvlist_destroy(instrumentation_scope->attributes); + + instrumentation_scope->attributes = NULL; + } + + result = unpack_cfl_kvlist(reader, &instrumentation_scope->attributes); + + if (result != 0) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return CPROF_DECODE_MSGPACK_SUCCESS; +} + +static int unpack_instrumentation_scope_dropped_attribute_count(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_instrumentation_scope *instrumentation_scope; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + instrumentation_scope = (struct cprof_instrumentation_scope *) user_data; + + return cprof_mpack_consume_uint32_tag(reader, &instrumentation_scope->dropped_attributes_count); +} + +static int unpack_scope_profiles_entry_instrumentation_scope(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_instrumentation_scope *instrumentation_scope; + struct cprof_scope_profiles *scope_profiles; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"name", unpack_instrumentation_scope_name}, + {"version", unpack_instrumentation_scope_version}, + {"attributes", unpack_instrumentation_scope_attributes}, + {"dropped_attribute_count", unpack_instrumentation_scope_dropped_attribute_count}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + scope_profiles = (struct cprof_scope_profiles *) user_data; + + instrumentation_scope = cprof_instrumentation_scope_create(NULL, NULL, NULL, 0); + + if (instrumentation_scope == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) instrumentation_scope); + + if (result == CPROF_DECODE_MSGPACK_SUCCESS) { + if (scope_profiles->scope != NULL) { + cprof_instrumentation_scope_destroy(scope_profiles->scope); + } + + scope_profiles->scope = instrumentation_scope; + } + + if (result != CPROF_DECODE_MSGPACK_SUCCESS) { + cprof_instrumentation_scope_destroy(instrumentation_scope); + } + + return result; +} + +static int unpack_profile_profile_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + int result; + cfl_sds_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + result = cprof_mpack_consume_binary_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + if (cfl_sds_len(value) != sizeof(profile->profile_id)) { + cfl_sds_destroy(value); + + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + memcpy(profile->profile_id, + value, + sizeof(profile->profile_id)); + + cfl_sds_destroy(value); + } + + return result; +} + +static int unpack_profile_start_time_unix_nano(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->start_time_unix_nano); +} + +static int unpack_profile_end_time_unix_nano(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->end_time_unix_nano); +} + +static int unpack_profile_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (profile->attributes != NULL) { + cfl_kvlist_destroy(profile->attributes); + + profile->attributes = NULL; + } + + result = unpack_cfl_kvlist(reader, &profile->attributes); + + if (result != 0) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return CPROF_DECODE_MSGPACK_SUCCESS; +} + +static int unpack_profile_dropped_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_uint32_tag(reader, &profile->dropped_attributes_count); +} + +static int unpack_value_type_type(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_value_type *sample_type; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample_type = (struct cprof_value_type *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &sample_type->type); +} + +static int unpack_value_type_unit(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_value_type *sample_type; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample_type = (struct cprof_value_type *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &sample_type->unit); +} + +static int unpack_value_type_aggregation_temporality(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_value_type *sample_type; + int result; + uint64_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample_type = (struct cprof_value_type *) user_data; + + result = cprof_mpack_consume_uint64_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + sample_type->aggregation_temporality = (int) value; + } + + return result; +} + +static int unpack_profile_sample_types_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_value_type *sample_type; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"type", unpack_value_type_type}, + {"unit", unpack_value_type_unit}, + {"aggregation_temporality", unpack_value_type_aggregation_temporality}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + sample_type = cprof_sample_type_create(profile, 0, 0, 0); + + if (sample_type == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) sample_type); + + /* cprof_sample_type_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_sample_types(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_types_entry, + user_data); +} + +static int unpack_profile_sample_location_index_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (index >= sample->location_index_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_uint64_tag(reader, &sample->location_index[index]); +} + +static int unpack_profile_sample_location_index(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (sample->location_index != NULL) { + free(sample->location_index); + + sample->location_index = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + sample->location_index = calloc(array_length, sizeof(uint64_t)); + + if (sample->location_index == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + sample->location_index_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_location_index_entry, + user_data); +} + +static int unpack_profile_sample_locations_start_index(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &sample->locations_start_index); +} + +static int unpack_profile_sample_locations_length(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &sample->locations_length); +} + +static int unpack_profile_sample_values_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (index >= sample->value_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_int64_tag(reader, &sample->values[index]); +} + +static int unpack_profile_sample_values(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (sample->values != NULL) { + free(sample->values); + + sample->values = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + sample->values = calloc(array_length, sizeof(int64_t)); + + if (sample->values == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + sample->value_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_values_entry, + user_data); +} + +static int unpack_profile_sample_attributes_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (index >= sample->attributes_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_uint64_tag(reader, &sample->attributes[index]); +} + +static int unpack_profile_sample_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (sample->attributes != NULL) { + free(sample->attributes); + + sample->attributes = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + sample->attributes = calloc(array_length, sizeof(uint64_t)); + + if (sample->attributes == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + sample->attributes_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_attributes_entry, + user_data); +} + +static int unpack_profile_sample_link(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &sample->link); +} + +static int unpack_profile_sample_timestamps_unix_nano_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (index >= sample->timestamps_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_uint64_tag(reader, &sample->timestamps_unix_nano[index]); +} + +static int unpack_profile_sample_timestamps_unix_nano(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_sample *sample; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + sample = (struct cprof_sample *) user_data; + + if (sample->timestamps_unix_nano != NULL) { + free(sample->timestamps_unix_nano); + + sample->timestamps_unix_nano = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + sample->timestamps_unix_nano = calloc(array_length, sizeof(uint64_t)); + + if (sample->timestamps_unix_nano == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + sample->timestamps_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_timestamps_unix_nano_entry, + user_data); +} + +static int unpack_profile_sample_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_sample *sample; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"location_index", unpack_profile_sample_location_index}, + {"locations_start_index", unpack_profile_sample_locations_start_index}, + {"locations_length", unpack_profile_sample_locations_length}, + {"values", unpack_profile_sample_values}, + {"attributes", unpack_profile_sample_attributes}, + {"link", unpack_profile_sample_link}, + {"timestamps_unix_nano", unpack_profile_sample_timestamps_unix_nano}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + sample = cprof_sample_create(profile); + + if (sample == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) sample); + + /* cprof_sample_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_sample(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_sample_entry, + user_data); +} + + + + + +static int unpack_profile_mapping_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &mapping->id); +} + +static int unpack_profile_mapping_memory_start(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &mapping->memory_start); +} + +static int unpack_profile_mapping_memory_limit(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &mapping->memory_limit); +} + +static int unpack_profile_mapping_file_offset(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &mapping->file_offset); +} + +static int unpack_profile_mapping_filename(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &mapping->filename); +} + +static int unpack_profile_mapping_attributes_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + if (index >= mapping->attributes_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_uint64_tag(reader, &mapping->attributes[index]); +} + +static int unpack_profile_mapping_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_mapping *mapping; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + if (mapping->attributes != NULL) { + free(mapping->attributes); + + mapping->attributes = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + mapping->attributes = calloc(array_length, sizeof(uint64_t)); + + if (mapping->attributes == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + mapping->attributes_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_mapping_attributes_entry, + user_data); +} + +static int unpack_profile_mapping_has_functions(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + int result; + uint64_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + result = cprof_mpack_consume_uint64_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + mapping->has_functions = (bool) value; + } + + return result; +} + +static int unpack_profile_mapping_has_filenames(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + int result; + uint64_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + result = cprof_mpack_consume_uint64_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + mapping->has_filenames = (bool) value; + } + + return result; +} + +static int unpack_profile_mapping_has_line_numbers(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + int result; + uint64_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + result = cprof_mpack_consume_uint64_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + mapping->has_line_numbers = (bool) value; + } + + return result; +} + +static int unpack_profile_mapping_has_inline_frames(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + int result; + uint64_t value; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + mapping = (struct cprof_mapping *) user_data; + + result = cprof_mpack_consume_uint64_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + mapping->has_inline_frames = (bool) value; + } + + return result; +} + +static int unpack_profile_mappings_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_mapping *mapping; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"id", unpack_profile_mapping_id}, + {"memory_start", unpack_profile_mapping_memory_start}, + {"memory_limit", unpack_profile_mapping_memory_limit}, + {"file_offset", unpack_profile_mapping_file_offset}, + {"filename", unpack_profile_mapping_filename}, + {"attributes", unpack_profile_mapping_attributes}, + {"has_functions", unpack_profile_mapping_has_functions}, + {"has_filenames", unpack_profile_mapping_has_filenames}, + {"has_line_numbers", unpack_profile_mapping_has_line_numbers}, + {"has_inline_frames", unpack_profile_mapping_has_inline_frames}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + mapping = cprof_mapping_create(profile); + + if (mapping == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) mapping); + + /* cprof_mapping_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_mappings(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_mappings_entry, + user_data); +} + + + + + + +static int unpack_location_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &location->id); +} + +static int unpack_location_mapping_index(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &location->mapping_index); +} + +static int unpack_location_address(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &location->address); +} + + + + + + + +static int unpack_line_function_index(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_line *line; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + line = (struct cprof_line *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &line->function_index); +} + +static int unpack_line_line(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_line *line; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + line = (struct cprof_line *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &line->line); +} + +static int unpack_line_column(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_line *line; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + line = (struct cprof_line *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &line->column); +} + +static int unpack_location_lines_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + struct cprof_line *line; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"function_index", unpack_line_function_index}, + {"line", unpack_line_line}, + {"column", unpack_line_column}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + line = cprof_line_create(location); + + if (line == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) line); + + /* cprof_line_create automatically attaches the newly created + * instance to the parent location instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_location_lines(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_location_lines_entry, + user_data); +} + +static int unpack_location_attributes_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + if (index >= location->attributes_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_uint64_tag(reader, &location->attributes[index]); +} + +static int unpack_location_attributes(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_location *location; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + location = (struct cprof_location *) user_data; + + if (location->attributes != NULL) { + free(location->attributes); + + location->attributes = NULL; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + location->attributes = calloc(array_length, sizeof(uint64_t)); + + if (location->attributes == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + location->attributes_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_location_attributes_entry, + user_data); +} + +static int unpack_profile_locations_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_location *location; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"id", unpack_location_id}, + {"mapping_index", unpack_location_mapping_index}, + {"address", unpack_location_address}, + {"lines", unpack_location_lines}, + {"attributes", unpack_location_attributes}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + location = cprof_location_create(profile); + + if (location == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) location); + + /* cprof_location_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_locations(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_locations_entry, + user_data); +} + + + +static int unpack_profile_location_indices_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (index >= profile->location_indices_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_int64_tag(reader, &profile->location_indices[index]); +} + +static int unpack_profile_location_indices(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (profile->location_indices != NULL) { + free(profile->location_indices); + + profile->location_indices = NULL; + profile->location_indices_count = 0; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + profile->location_indices = calloc(array_length, sizeof(int64_t)); + + if (profile->location_indices == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + profile->location_indices_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_location_indices_entry, + user_data); +} + + + + + + +static int unpack_function_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + function = (struct cprof_function *) user_data; + + return cprof_mpack_consume_uint64_tag(reader, &function->id); +} + +static int unpack_function_name(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + function = (struct cprof_function *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &function->name); +} + +static int unpack_function_system_name(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + function = (struct cprof_function *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &function->system_name); +} + +static int unpack_function_filename(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + function = (struct cprof_function *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &function->filename); +} + +static int unpack_function_start_line(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + function = (struct cprof_function *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &function->start_line); +} + +static int unpack_profile_functions_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_function *function; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"id", unpack_function_id}, + {"name", unpack_function_name}, + {"system_name", unpack_function_system_name}, + {"filename", unpack_function_filename}, + {"start_line", unpack_function_start_line}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + function = cprof_function_create(profile); + + if (function == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) function); + + /* cprof_function_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_functions(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_functions_entry, + user_data); +} + +static int unpack_profile_attribute_table(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (profile->attribute_table != NULL) { + cfl_kvlist_destroy(profile->attribute_table); + + profile->attribute_table = NULL; + } + + result = unpack_cfl_kvlist(reader, &profile->attribute_table); + + if (result != 0) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return CPROF_DECODE_MSGPACK_SUCCESS; +} + + + + + + + + +static int unpack_profile_attribute_unit_attribute_key(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_attribute_unit *attribute_unit; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + attribute_unit = (struct cprof_attribute_unit *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &attribute_unit->attribute_key); +} + +static int unpack_profile_attribute_unit_unit(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_attribute_unit *attribute_unit; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + attribute_unit = (struct cprof_attribute_unit *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &attribute_unit->unit); +} + +static int unpack_profile_attribute_units_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_attribute_unit *attribute_unit; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"attribute_key", unpack_profile_attribute_unit_attribute_key}, + {"unit", unpack_profile_attribute_unit_unit}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + attribute_unit = cprof_attribute_unit_create(profile); + + if (attribute_unit == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) attribute_unit); + + /* cprof_attribute_unit_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_attribute_units(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_attribute_units_entry, + user_data); +} + + +static int unpack_profile_link_trace_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_link *link; + cfl_sds_t value; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + link = (struct cprof_link *) user_data; + + result = cprof_mpack_consume_binary_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + if (cfl_sds_len(value) != sizeof(link->trace_id)) { + cfl_sds_destroy(value); + + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + memcpy(link->trace_id, + value, + sizeof(link->trace_id)); + + cfl_sds_destroy(value); + } + + return result; +} + +static int unpack_profile_link_span_id(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_link *link; + cfl_sds_t value; + int result; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + link = (struct cprof_link *) user_data; + + result = cprof_mpack_consume_binary_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + if (cfl_sds_len(value) != sizeof(link->span_id)) { + cfl_sds_destroy(value); + + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + memcpy(link->span_id, + value, + sizeof(link->span_id)); + + cfl_sds_destroy(value); + } + + return result; +} + +static int unpack_profile_link_table_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_link *link; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"trace_id", unpack_profile_link_trace_id}, + {"span_id", unpack_profile_link_span_id}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + link = cprof_link_create(profile); + + if (link == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) link); + + /* cprof_link_create automatically attaches the newly created + * instance to the parent profile instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_profile_link_table(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_link_table_entry, + user_data); +} + + + + + + + + + + + +static int unpack_profile_string_table_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (index >= profile->string_table_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_string_tag(reader, &profile->string_table[index]); +} + +static int unpack_profile_string_table(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (profile->string_table != NULL) { + free(profile->string_table); + + profile->string_table = NULL; + profile->string_table_count = 0; + profile->string_table_size = 0; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + profile->string_table = calloc(array_length, sizeof(char *)); + + if (profile->string_table == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + profile->string_table_count = (size_t) array_length; + profile->string_table_size = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_string_table_entry, + user_data); +} + + + +static int unpack_profile_drop_frames(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->drop_frames); +} + +static int unpack_profile_keep_frames(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->keep_frames); +} + +static int unpack_profile_time_nanos(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->time_nanos); +} + +static int unpack_profile_duration_nanos(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->duration_nanos); +} + + +static int unpack_profile_period_type(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"type", unpack_value_type_type}, + {"unit", unpack_value_type_unit}, + {"aggregation_temporality", unpack_value_type_aggregation_temporality}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_unpack_map(reader, + callbacks, + (void *) &profile->period_type); +} + + +static int unpack_profile_period(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->period); +} + + + + +static int unpack_profile_comments_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (index >= profile->comments_count) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_consume_int64_tag(reader, &profile->comments[index]); +} + +static int unpack_profile_comments(mpack_reader_t *reader, size_t index, void *user_data) +{ + int array_length; + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + if (profile->comments != NULL) { + free(profile->comments); + + profile->comments = NULL; + profile->comments_count = 0; + } + + array_length = cprof_mpack_peek_array_length(reader); + + if (array_length > 0) { + profile->comments = calloc(array_length, sizeof(int64_t)); + + if (profile->comments == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + profile->comments_count = (size_t) array_length; + } + + return cprof_mpack_unpack_array(reader, + unpack_profile_comments_entry, + user_data); +} + +static int unpack_profile_default_sample_type(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_profile *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_profile *) user_data; + + return cprof_mpack_consume_int64_tag(reader, &profile->default_sample_type); +} + + + + +static int unpack_scope_profiles_entry_profiles_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_scope_profiles *scope_profiles; + struct cprof_profile *profile; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"profile_id", unpack_profile_profile_id}, + {"start_time_unix_nano", unpack_profile_start_time_unix_nano}, + {"end_time_unix_nano", unpack_profile_end_time_unix_nano}, + {"attributes", unpack_profile_attributes}, + {"dropped_attributes", unpack_profile_dropped_attributes}, + {"sample_types", unpack_profile_sample_types}, + {"sample", unpack_profile_sample}, + {"mappings", unpack_profile_mappings}, + {"locations", unpack_profile_locations}, + {"location_indices", unpack_profile_location_indices}, + {"functions", unpack_profile_functions}, + {"attribute_table", unpack_profile_attribute_table}, + {"attribute_units", unpack_profile_attribute_units}, + {"link_table", unpack_profile_link_table}, + {"string_table", unpack_profile_string_table}, + {"drop_frames", unpack_profile_drop_frames}, + {"keep_frames", unpack_profile_keep_frames}, + {"time_nanos", unpack_profile_time_nanos}, + {"duration_nanos", unpack_profile_duration_nanos}, + {"period_type", unpack_profile_period_type}, + {"period", unpack_profile_period}, + {"comments", unpack_profile_comments}, + {"default_sample_type", unpack_profile_default_sample_type}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + scope_profiles = (struct cprof_scope_profiles *) user_data; + + profile = cprof_profile_create(); + + if (profile == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) profile); + + if (result == CPROF_DECODE_MSGPACK_SUCCESS) { + cfl_list_add(&profile->_head, &scope_profiles->profiles); + } + else { + cprof_profile_destroy(profile); + } + + return result; +} + +static int unpack_scope_profiles_entry_profiles(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_scope_profiles_entry_profiles_entry, + user_data); +} + + +static int unpack_scope_profiles_entry_schema_url(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_scope_profiles *scope_profiles; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + scope_profiles = (struct cprof_scope_profiles *) user_data; + + if (scope_profiles->schema_url != NULL) { + cfl_sds_destroy(scope_profiles->schema_url); + + scope_profiles->schema_url = NULL; + } + + return cprof_mpack_consume_string_or_nil_tag(reader, &scope_profiles->schema_url); +} + +static int unpack_resource_profiles_entry_scope_profiles_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_resource_profiles *resource_profiles; + struct cprof_scope_profiles *scope_profiles; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"instrumentation_scope", unpack_scope_profiles_entry_instrumentation_scope}, + {"profiles", unpack_scope_profiles_entry_profiles}, + {"schema_url", unpack_scope_profiles_entry_schema_url}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + resource_profiles = (struct cprof_resource_profiles *) user_data; + + scope_profiles = cprof_scope_profiles_create(resource_profiles, ""); + + if (scope_profiles == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) scope_profiles); + + /* cprof_scope_profiles_create automatically attaches the newly created + * instance to the parent resource cprof profiles instance, because of + * that in case of failure we just let the parent destructor take care of + * it. + */ + + return result; +} + +static int unpack_resource_profiles_entry_scope_profiles(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_resource_profiles_entry_scope_profiles_entry, + user_data); +} + +static int unpack_resource_profiles_entry_schema_url(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_resource_profiles *profile; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + profile = (struct cprof_resource_profiles *) user_data; + + if (profile->schema_url != NULL) { + cfl_sds_destroy(profile->schema_url); + + profile->schema_url = NULL; + } + + return cprof_mpack_consume_string_or_nil_tag(reader, &profile->schema_url); +} + +static int unpack_cprof_resource_profiles_entry(mpack_reader_t *reader, size_t index, void *user_data) +{ + struct cprof_resource_profiles *profiles; + struct cprof *context; + int result; + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"resource", unpack_resource_profiles_entry_resource}, + {"scope_profiles", unpack_resource_profiles_entry_scope_profiles}, + {"schema_url", unpack_resource_profiles_entry_schema_url}, + {NULL, NULL} + }; + + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + context = (struct cprof *) user_data; + + profiles = cprof_resource_profiles_create(""); + + if (profiles == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + result = cprof_mpack_unpack_map(reader, + callbacks, + (void *) profiles); + + if (result == CPROF_DECODE_MSGPACK_SUCCESS) { + result = cprof_resource_profiles_add(context, profiles); + + if (result != 0) { + result = CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + } + + if (result != CPROF_DECODE_MSGPACK_SUCCESS) { + cprof_resource_profiles_destroy(profiles); + } + + return result; +} + +static int unpack_context_profiles(mpack_reader_t *reader, size_t index, void *user_data) +{ + if (reader == NULL || + user_data == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_array(reader, + unpack_cprof_resource_profiles_entry, + user_data); +} + + +int unpack_context(struct crof_msgpack_decode_context *context) +{ + struct cprof_mpack_map_entry_callback_t callbacks[] = \ + { + {"meta", unpack_context_header}, + {"profiles", unpack_context_profiles}, + {NULL, NULL} + }; + + if (context == NULL) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + return cprof_mpack_unpack_map(&context->reader, + callbacks, + (void *) context->inner_context); +} + +int cprof_decode_msgpack_create(struct cprof **result_context, + unsigned char *in_buf, + size_t in_size, + size_t *offset) +{ + int result; + struct crof_msgpack_decode_context context; + size_t remainder; + + if (result_context == NULL || + in_buf == NULL || + offset == NULL || + in_size < *offset ) { + return CPROF_DECODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + + if (in_size == 0 || + (in_size - *offset) == 0) { + return CPROF_DECODE_MSGPACK_INSUFFICIENT_DATA; + } + + memset(&context, 0, sizeof(struct crof_msgpack_decode_context)); + + context.inner_context = cprof_create(); + + if (context.inner_context == NULL) { + return CPROF_DECODE_MSGPACK_ALLOCATION_ERROR; + } + + in_size -= *offset; + + mpack_reader_init_data(&context.reader, (const char *) &in_buf[*offset], in_size); + + result = unpack_context(&context); + + remainder = mpack_reader_remaining(&context.reader, NULL); + + *offset += in_size - remainder; + + mpack_reader_destroy(&context.reader); + + if (result != CPROF_DECODE_MSGPACK_SUCCESS) { + cprof_destroy(context.inner_context); + } + else { + *result_context = context.inner_context; + } + + return result; +} + +void cprof_decode_msgpack_destroy(struct cprof *context) +{ + if (context != NULL) { + cprof_destroy(context); + } +} diff --git a/lib/cprofiles/cprof_decode_opentelemetry.c b/lib/cprofiles/cprof_decode_opentelemetry.c new file mode 100644 index 00000000000..fb5dd240e05 --- /dev/null +++ b/lib/cprofiles/cprof_decode_opentelemetry.c @@ -0,0 +1,841 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include "cprof_opentelemetry_variant_helpers.c" + +static int decode_resource(struct cprof_resource **output_resource, + Opentelemetry__Proto__Resource__V1__Resource *input_resource, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) +{ + struct cfl_kvlist *attributes; + int result; + + *output_resource = NULL; + + if (input_resource == NULL) { + *output_resource = cprof_resource_create(NULL); + if (*output_resource != NULL) { + (*output_resource)->dropped_attributes_count = 0; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; + } + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + attributes = cfl_kvlist_create(); + + if (attributes == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = convert_kvarray_to_kvlist(attributes, + input_resource->attributes, + input_resource->n_attributes, + dictionary != NULL ? dictionary->string_table : NULL, + dictionary != NULL ? dictionary->n_string_table : 0); + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_kvlist_destroy(attributes); + return result; + } + + *output_resource = cprof_resource_create(attributes); + + if (*output_resource == NULL) { + cfl_kvlist_destroy(attributes); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + (*output_resource)->dropped_attributes_count = input_resource->dropped_attributes_count; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + + +static int decode_instrumentation_scope( + struct cprof_instrumentation_scope **instrumentation_scope, + Opentelemetry__Proto__Common__V1__InstrumentationScope *input_instrumentation_scope, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) +{ + int result; + + *instrumentation_scope = NULL; + + *instrumentation_scope = cprof_instrumentation_scope_create( + input_instrumentation_scope->name, + input_instrumentation_scope->version, + NULL, + input_instrumentation_scope->dropped_attributes_count); + + if (*instrumentation_scope == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = convert_kvarray_to_kvlist((*instrumentation_scope)->attributes, + input_instrumentation_scope->attributes, + input_instrumentation_scope->n_attributes, + dictionary != NULL ? dictionary->string_table : NULL, + dictionary != NULL ? dictionary->n_string_table : 0); + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cprof_instrumentation_scope_destroy(*instrumentation_scope); + *instrumentation_scope = NULL; + + return result; + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + + +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; + + /* Resolve stack_index to location indices from dictionary.stack_table */ + if (input_sample->stack_index >= 0) { + if (dictionary == NULL || + dictionary->stack_table == NULL || + (size_t) input_sample->stack_index >= dictionary->n_stack_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + stack = dictionary->stack_table[input_sample->stack_index]; + if (stack == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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) location_index); + if (result != 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + } + } + + 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_attribute_indices; index++) { + if (dictionary == NULL || + dictionary->attribute_table == NULL || + input_sample->attribute_indices[index] < 0 || + (size_t) input_sample->attribute_indices[index] >= dictionary->n_attribute_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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]); + if (result != 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + + if (input_sample->link_index >= 0) { + if (dictionary == NULL || + dictionary->link_table == NULL || + (size_t) input_sample->link_index >= dictionary->n_link_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + sample->link = (uint64_t) input_sample->link_index; + } + else { + sample->link = 0; + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + + + + +static int decode_mapping_entry(struct cprof_mapping *mapping, + Opentelemetry__Proto__Profiles__V1development__Mapping *input_mapping) +{ + int result; + int32_t raw_attribute_index; + size_t 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++) { + raw_attribute_index = input_mapping->attribute_indices[index]; + + if (raw_attribute_index < 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + result = cprof_mapping_add_attribute(mapping, + (uint64_t) raw_attribute_index); + if (result != 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int decode_line_entry(struct cprof_line *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; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int decode_location_entry(struct cprof_location *location, + Opentelemetry__Proto__Profiles__V1development__Location *input_location, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) +{ + int result; + int32_t raw_attribute_index; + 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; + location->is_folded = 0; + + 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->lines[index], dictionary); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + + for (index = 0; index < input_location->n_attribute_indices; index++) { + raw_attribute_index = input_location->attribute_indices[index]; + + if (raw_attribute_index < 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + result = cprof_location_add_attribute(location, + (uint64_t) raw_attribute_index); + if (result != 0) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int decode_function_entry(struct cprof_function *function, + Opentelemetry__Proto__Profiles__V1development__Function *input_function) +{ + 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_keyvalueandunit_entry(struct cprof_attribute_unit *attribute_unit, + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *input_kv) +{ + attribute_unit->attribute_key = (int64_t)input_kv->key_strindex; + attribute_unit->unit = (int64_t)input_kv->unit_strindex; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int decode_link_table_entry(struct cprof_link *link, + Opentelemetry__Proto__Profiles__V1development__Link *input_link) +{ + if (input_link->trace_id.data != NULL || + input_link->trace_id.len > 0) { + if (input_link->trace_id.data == NULL || + input_link->trace_id.len != sizeof(link->trace_id)) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + memcpy(link->trace_id, + input_link->trace_id.data, + sizeof(link->trace_id)); + } + + if (input_link->span_id.data != NULL || + input_link->span_id.len > 0) { + if (input_link->span_id.data == NULL || + input_link->span_id.len != sizeof(link->span_id)) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + memcpy(link->span_id, + input_link->span_id.data, + sizeof(link->span_id)); + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + + +static int decode_profile_entry(struct cprof_profile *profile, + Opentelemetry__Proto__Profiles__V1development__Profile *input_profile, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) +{ + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *indexed_attribute_entry; + struct cprof_attribute_unit *attribute_unit; + struct cprof_value_type *sample_type; + struct cprof_location *location; + struct cprof_function *function; + 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; + + /* Copy dictionary tables into profile when dictionary is present */ + if (dictionary != NULL) { + /* String table */ + if (dictionary->string_table != NULL) { + 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; + } + } + } + + /* Mappings */ + if (dictionary->mapping_table != NULL) { + for (index = 0; index < dictionary->n_mapping_table; index++) { + if (dictionary->mapping_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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; + } + } + } + + /* Locations */ + if (dictionary->location_table != NULL) { + for (index = 0; index < dictionary->n_location_table; index++) { + if (dictionary->location_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + location = cprof_location_create(profile); + if (location == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + result = decode_location_entry(location, + dictionary->location_table[index], + dictionary); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } + } + + /* Functions */ + if (dictionary->function_table != NULL) { + for (index = 0; index < dictionary->n_function_table; index++) { + if (dictionary->function_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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; + } + } + } + + /* 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++) { + if (dictionary->attribute_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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; + } + } + } + + /* Link table */ + if (dictionary->link_table != NULL) { + for (index = 0; index < dictionary->n_link_table; index++) { + if (dictionary->link_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + 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; + } + } + } + + /* 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_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]; + } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + result = clone_variant(&indexed_attribute_value, + indexed_attribute_entry->value, + dictionary->string_table, + dictionary->n_string_table); + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + + 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) */ + 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; + } + } + + /* 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->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 = (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->default_sample_type = 0; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +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; + + profile = cprof_profile_create(); + if (profile == NULL) { + 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->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; + + 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->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, 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__ProfilesDictionary *dictionary) +{ + struct cprof_scope_profiles *profiles; + int result; + size_t index; + + profiles = cprof_scope_profiles_create( + resource_profiles, + input_scope_profiles->schema_url != NULL ? input_scope_profiles->schema_url : ""); + + if (profiles == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + if (input_scope_profiles->scope != NULL) { + result = decode_instrumentation_scope(&profiles->scope, + input_scope_profiles->scope, + dictionary); + } + else { + profiles->scope = cprof_instrumentation_scope_create(NULL, NULL, NULL, 0); + result = profiles->scope != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_list_del(&profiles->_head); + cprof_scope_profiles_destroy(profiles); + return result; + } + + 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) { + 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__ProfilesDictionary *dictionary) +{ + struct cprof_resource_profiles *profile; + int result; + size_t index; + + profile = cprof_resource_profiles_create( + resource_profile->schema_url != NULL ? resource_profile->schema_url : ""); + + if (profile == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = decode_resource(&profile->resource, resource_profile->resource, dictionary); + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cprof_resource_profiles_destroy(profile); + return result; + } + + result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; + + 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++) { + result = decode_scope_profiles_entry(profile, + resource_profile->scope_profiles[index], dictionary); + } + } + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cprof_resource_profiles_destroy(profile); + return result; + } + + result = cprof_resource_profiles_add(context, profile); + + if (result != 0) { + cprof_resource_profiles_destroy(profile); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int decode_service_request(struct cprof *context, + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *service_request) +{ + int result; + size_t index; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary; + + dictionary = service_request->dictionary; + result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; + + 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++) { + result = decode_resource_profiles_entry(context, + service_request->resource_profiles[index], dictionary); + } + } + + return result; +} + + +int cprof_decode_opentelemetry_create(struct cprof **result_context, + unsigned char *in_buf, size_t in_size, + size_t *offset) +{ + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *service_request; + struct cprof *context; + size_t remaining; + int result; + + result = CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + context = NULL; + + if (offset == NULL || + result_context == NULL || + in_buf == NULL || + *offset > in_size) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + *result_context = NULL; + + remaining = in_size - *offset; + + service_request = opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__unpack( + NULL, + remaining, + &in_buf[*offset]); + + if (service_request != NULL) { + context = cprof_create(); + + if (context != NULL) { + result = decode_service_request(context, service_request); + } + else { + result = CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked(service_request, NULL); + } + + if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + *result_context = context; + + if (offset != NULL) { + *offset = in_size; + } + } + else if (context != NULL) { + cprof_destroy(context); + } + + return result; +} + +void cprof_decode_opentelemetry_destroy(struct cprof *context) +{ + if (context != NULL) { + cprof_destroy(context); + } +} diff --git a/lib/cprofiles/cprof_encode_msgpack.c b/lib/cprofiles/cprof_encode_msgpack.c new file mode 100644 index 00000000000..e4c6d766ede --- /dev/null +++ b/lib/cprofiles/cprof_encode_msgpack.c @@ -0,0 +1,981 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +static inline void mpack_write_sds_or_nil(mpack_writer_t *writer, + cfl_sds_t value) +{ + if (value != NULL) { + mpack_write_str(writer, + value, + cfl_sds_len(value)); + } + else { + mpack_write_nil(writer); + } +} + +static int encode_string_array( + struct cprof_msgpack_encoding_context *context, + char **data_list, + size_t data_length); + +static int encode_uint64_t_array( + struct cprof_msgpack_encoding_context *context, + uint64_t *data_list, + size_t data_length); + +static int encode_int64_t_array( + struct cprof_msgpack_encoding_context *context, + int64_t *data_list, + size_t data_length); + +static int encode_cprof_value_type( + struct cprof_msgpack_encoding_context *context, + struct cprof_value_type *instance); + +static int encode_cprof_sample( + struct cprof_msgpack_encoding_context *context, + struct cprof_sample *instance); + +static int encode_cprof_mapping( + struct cprof_msgpack_encoding_context *context, + struct cprof_mapping *instance); + +static int encode_cprof_line( + struct cprof_msgpack_encoding_context *context, + struct cprof_line *instance); + +static int encode_cprof_location( + struct cprof_msgpack_encoding_context *context, + struct cprof_location *instance); + +static int encode_cprof_function( + struct cprof_msgpack_encoding_context *context, + struct cprof_function *instance); + +static int encode_cprof_attribute_unit( + struct cprof_msgpack_encoding_context *context, + struct cprof_attribute_unit *instance); + + +static int encode_cprof_link( + struct cprof_msgpack_encoding_context *context, + struct cprof_link *instance); + + +static int encode_cprof_profile( + struct cprof_msgpack_encoding_context *context, + struct cprof_profile *instance); + + +static int encode_cprof_resource_profiles( + struct cprof_msgpack_encoding_context *context, + struct cprof_resource_profiles *instance); + + +static int encode_cprof_instrumentation_scope( + struct cprof_msgpack_encoding_context *context, + struct cprof_instrumentation_scope *instance); + + +static int encode_cprof_resource( + struct cprof_msgpack_encoding_context *context, + struct cprof_resource *instance); + + +static int encode_cprof_scope_profiles( + struct cprof_msgpack_encoding_context *context, + struct cprof_scope_profiles *instance); + + +static int encode_string_array( + struct cprof_msgpack_encoding_context *context, + char **data_list, + size_t data_length) +{ + size_t index; + + mpack_start_array(&context->writer, + data_length); + + for (index = 0 ; index < data_length ; index++) { + mpack_write_cstr(&context->writer, data_list[index]); + } + + mpack_finish_array(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_uint64_t_array( + struct cprof_msgpack_encoding_context *context, + uint64_t *data_list, + size_t data_length) +{ + size_t index; + + mpack_start_array(&context->writer, + data_length); + + for (index = 0 ; index < data_length ; index++) { + mpack_write_u64(&context->writer, data_list[index]); + } + + mpack_finish_array(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_int64_t_array( + struct cprof_msgpack_encoding_context *context, + int64_t *data_list, + size_t data_length) +{ + size_t index; + + mpack_start_array(&context->writer, + data_length); + + for (index = 0 ; index < data_length ; index++) { + mpack_write_i64(&context->writer, data_list[index]); + } + + mpack_finish_array(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + +static int encode_cprof_value_type( + struct cprof_msgpack_encoding_context *context, + struct cprof_value_type *instance) { + mpack_start_map(&context->writer, 3); + + mpack_write_cstr(&context->writer, "type"); + mpack_write_i64(&context->writer, instance->type); + + mpack_write_cstr(&context->writer, "unit"); + mpack_write_i64(&context->writer, instance->unit); + + mpack_write_cstr(&context->writer, "aggregation_temporality"); + mpack_write_u64(&context->writer, instance->aggregation_temporality); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_cprof_sample( + struct cprof_msgpack_encoding_context *context, + struct cprof_sample *instance) { + int result; + + mpack_start_map(&context->writer, 7); + + mpack_write_cstr(&context->writer, "location_index"); + + result = encode_uint64_t_array(context, + instance->location_index, + instance->location_index_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "locations_start_index"); + mpack_write_u64(&context->writer, instance->locations_start_index); + + mpack_write_cstr(&context->writer, "locations_length"); + mpack_write_u64(&context->writer, instance->locations_length); + + mpack_write_cstr(&context->writer, "values"); + result = encode_int64_t_array(context, + instance->values, + instance->value_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "attributes"); + result = encode_uint64_t_array(context, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "link"); + mpack_write_u64(&context->writer, instance->link); + + mpack_write_cstr(&context->writer, "timestamps_unix_nano"); + result = encode_uint64_t_array(context, + instance->timestamps_unix_nano, + instance->timestamps_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_cprof_mapping( + struct cprof_msgpack_encoding_context *context, + struct cprof_mapping *instance) { + int result; + + mpack_start_map(&context->writer, 10); + + mpack_write_cstr(&context->writer, "id"); + mpack_write_u64(&context->writer, instance->id); + + mpack_write_cstr(&context->writer, "memory_start"); + mpack_write_u64(&context->writer, instance->memory_start); + + mpack_write_cstr(&context->writer, "memory_limit"); + mpack_write_u64(&context->writer, instance->memory_limit); + + mpack_write_cstr(&context->writer, "file_offset"); + mpack_write_u64(&context->writer, instance->file_offset); + + mpack_write_cstr(&context->writer, "filename"); + mpack_write_i64(&context->writer, instance->filename); + + mpack_write_cstr(&context->writer, "attributes"); + result = encode_uint64_t_array(context, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "has_functions"); + mpack_write_u64(&context->writer, instance->has_functions); + + mpack_write_cstr(&context->writer, "has_filenames"); + mpack_write_u64(&context->writer, instance->has_filenames); + + mpack_write_cstr(&context->writer, "has_line_numbers"); + mpack_write_u64(&context->writer, instance->has_line_numbers); + + mpack_write_cstr(&context->writer, "has_inline_frames"); + mpack_write_u64(&context->writer, instance->has_inline_frames); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + + + +static int encode_cprof_line( + struct cprof_msgpack_encoding_context *context, + struct cprof_line *instance) { + mpack_start_map(&context->writer, 3); + + mpack_write_cstr(&context->writer, "function_index"); + mpack_write_u64(&context->writer, instance->function_index); + + mpack_write_cstr(&context->writer, "line"); + mpack_write_i64(&context->writer, instance->line); + + mpack_write_cstr(&context->writer, "column"); + mpack_write_i64(&context->writer, instance->column); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + +static int encode_cprof_location( + struct cprof_msgpack_encoding_context *context, + struct cprof_location *instance) { + struct cfl_list *iterator; + int result; + struct cprof_line *line; + + mpack_start_map(&context->writer, 5); + + mpack_write_cstr(&context->writer, "id"); + mpack_write_u64(&context->writer, instance->id); + + mpack_write_cstr(&context->writer, "mapping_index"); + mpack_write_u64(&context->writer, instance->mapping_index); + + mpack_write_cstr(&context->writer, "address"); + mpack_write_u64(&context->writer, instance->address); + + mpack_write_cstr(&context->writer, "lines"); + mpack_start_array(&context->writer, cfl_list_size(&instance->lines)); + + if (!cfl_list_is_empty(&instance->lines)) { + cfl_list_foreach(iterator, + &instance->lines) { + line = cfl_list_entry(iterator, + struct cprof_line, _head); + + result = encode_cprof_line(context, line); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "attributes"); + + result = encode_uint64_t_array(context, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + + +static int encode_cprof_function( + struct cprof_msgpack_encoding_context *context, + struct cprof_function *instance) { + mpack_start_map(&context->writer, 5); + + mpack_write_cstr(&context->writer, "id"); + mpack_write_u64(&context->writer, instance->id); + + mpack_write_cstr(&context->writer, "name"); + mpack_write_i64(&context->writer, instance->name); + + mpack_write_cstr(&context->writer, "system_name"); + mpack_write_i64(&context->writer, instance->system_name); + + mpack_write_cstr(&context->writer, "filename"); + mpack_write_i64(&context->writer, instance->filename); + + mpack_write_cstr(&context->writer, "start_line"); + mpack_write_i64(&context->writer, instance->start_line); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + + +static int encode_cprof_attribute_unit( + struct cprof_msgpack_encoding_context *context, + struct cprof_attribute_unit *instance) { + mpack_start_map(&context->writer, 2); + + mpack_write_cstr(&context->writer, "attribute_key"); + mpack_write_i64(&context->writer, instance->attribute_key); + + mpack_write_cstr(&context->writer, "unit"); + mpack_write_i64(&context->writer, instance->unit); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_cprof_link( + struct cprof_msgpack_encoding_context *context, + struct cprof_link *instance) +{ + mpack_start_map(&context->writer, 2); + + mpack_write_cstr(&context->writer, "trace_id"); + mpack_write_bin(&context->writer, + (const char *) instance->trace_id, + sizeof(instance->trace_id)); + + mpack_write_cstr(&context->writer, "span_id"); + mpack_write_bin(&context->writer, + (const char *) instance->span_id, + sizeof(instance->span_id)); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + +static int encode_cprof_profile( + struct cprof_msgpack_encoding_context *context, + struct cprof_profile *instance) { + struct cprof_attribute_unit *attribute_unit; + struct cprof_value_type *sample_type; + struct cfl_list *iterator; + struct cprof_location *location; + struct cprof_function *function; + struct cprof_mapping *mapping; + struct cprof_sample *sample; + int result; + struct cprof_link *link; + + + mpack_start_map(&context->writer, 23); + + mpack_write_cstr(&context->writer, "profile_id"); + mpack_write_bin(&context->writer, + (const char *) instance->profile_id, + sizeof(instance->profile_id)); + + mpack_write_cstr(&context->writer, "start_time_unix_nano"); + mpack_write_i64(&context->writer, instance->start_time_unix_nano); + + mpack_write_cstr(&context->writer, "end_time_unix_nano"); + mpack_write_i64(&context->writer, instance->end_time_unix_nano); + + mpack_write_cstr(&context->writer, "attributes"); + + result = pack_cfl_variant_kvlist(&context->writer, + instance->attributes); + + if (result != 0) { + return -1; + } + + mpack_write_cstr(&context->writer, "dropped_attributes"); + mpack_write_u32(&context->writer, + instance->dropped_attributes_count); + + mpack_write_cstr(&context->writer, "sample_types"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->sample_type)); + + if (!cfl_list_is_empty(&instance->sample_type)) { + cfl_list_foreach(iterator, + &instance->sample_type) { + sample_type = cfl_list_entry( + iterator, + struct cprof_value_type, _head); + + result = encode_cprof_value_type(context, sample_type); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "sample"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->samples)); + + if (!cfl_list_is_empty(&instance->samples)) { + cfl_list_foreach(iterator, + &instance->samples) { + sample = cfl_list_entry( + iterator, + struct cprof_sample, _head); + + result = encode_cprof_sample(context, sample); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "mappings"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->mappings)); + + if (!cfl_list_is_empty(&instance->mappings)) { + cfl_list_foreach(iterator, + &instance->mappings) { + mapping = cfl_list_entry( + iterator, + struct cprof_mapping, _head); + + result = encode_cprof_mapping(context, mapping); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "locations"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->locations)); + + if (!cfl_list_is_empty(&instance->locations)) { + cfl_list_foreach(iterator, + &instance->locations) { + location = cfl_list_entry( + iterator, + struct cprof_location, _head); + + result = encode_cprof_location(context, location); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "location_indices"); + + result = encode_int64_t_array(context, + instance->location_indices, + instance->location_indices_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "functions"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->functions)); + + if (!cfl_list_is_empty(&instance->functions)) { + cfl_list_foreach(iterator, + &instance->functions) { + function = cfl_list_entry( + iterator, + struct cprof_function, _head); + + result = encode_cprof_function(context, function); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "attribute_table"); + + result = pack_cfl_variant_kvlist(&context->writer, + instance->attribute_table); + + if (result != 0) { + return -1; + } + + mpack_write_cstr(&context->writer, "attribute_units"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->attribute_units)); + + if (!cfl_list_is_empty(&instance->attribute_units)) { + cfl_list_foreach(iterator, + &instance->attribute_units) { + attribute_unit = cfl_list_entry( + iterator, + struct cprof_attribute_unit, _head); + + result = encode_cprof_attribute_unit(context, attribute_unit); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "link_table"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->link_table)); + + if (!cfl_list_is_empty(&instance->link_table)) { + cfl_list_foreach(iterator, + &instance->link_table) { + link = cfl_list_entry( + iterator, + struct cprof_link, _head); + + result = encode_cprof_link(context, link); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "string_table"); + + result = encode_string_array( + context, + (char **) instance->string_table, + instance->string_table_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "drop_frames"); + mpack_write_i64(&context->writer, instance->drop_frames); + + mpack_write_cstr(&context->writer, "keep_frames"); + mpack_write_i64(&context->writer, instance->keep_frames); + + mpack_write_cstr(&context->writer, "time_nanos"); + mpack_write_i64(&context->writer, instance->time_nanos); + + mpack_write_cstr(&context->writer, "duration_nanos"); + mpack_write_i64(&context->writer, instance->duration_nanos); + + mpack_write_cstr(&context->writer, "period_type"); + result = encode_cprof_value_type(context, &instance->period_type); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "period"); + mpack_write_i64(&context->writer, instance->period); + + mpack_write_cstr(&context->writer, "comments"); + result = encode_int64_t_array(context, + instance->comments, + instance->comments_count); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "default_sample_type"); + mpack_write_i64(&context->writer, instance->default_sample_type); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_cprof_resource_profiles( + struct cprof_msgpack_encoding_context *context, + struct cprof_resource_profiles *instance) { + int result; + struct cfl_list *iterator; + struct cprof_scope_profiles *scope_profile; + + mpack_start_map(&context->writer, 3); + mpack_write_cstr(&context->writer, "resource"); + + result = encode_cprof_resource(context, instance->resource); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "scope_profiles"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->scope_profiles)); + + if (!cfl_list_is_empty(&instance->scope_profiles)) { + cfl_list_foreach(iterator, + &instance->scope_profiles) { + scope_profile = cfl_list_entry( + iterator, + struct cprof_scope_profiles, _head); + + result = encode_cprof_scope_profiles(context, scope_profile); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "schema_url"); + + mpack_write_sds_or_nil(&context->writer, + instance->schema_url); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int encode_cprof_instrumentation_scope( + struct cprof_msgpack_encoding_context *context, + struct cprof_instrumentation_scope *instance) { + int result; + + mpack_start_map(&context->writer, 4); + + mpack_write_cstr(&context->writer, "name"); + mpack_write_sds_or_nil(&context->writer, + instance->name); + + + mpack_write_cstr(&context->writer, "version"); + mpack_write_sds_or_nil(&context->writer, + instance->version); + + + mpack_write_cstr(&context->writer, "attributes"); + + result = pack_cfl_variant_kvlist(&context->writer, + instance->attributes); + + if (result != 0) { + mpack_finish_map(&context->writer); + return -1; + } + + mpack_write_cstr(&context->writer, "dropped_attribute_count"); + mpack_write_u32(&context->writer, instance->dropped_attributes_count); + + mpack_finish_map(&context->writer); + + if (mpack_writer_error(&context->writer) != mpack_ok) { + return -1; + } + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + +static int encode_cprof_resource( + struct cprof_msgpack_encoding_context *context, + struct cprof_resource *instance) { + int result; + + mpack_start_map(&context->writer, 2); + + mpack_write_cstr(&context->writer, "attributes"); + + result = pack_cfl_variant_kvlist(&context->writer, + instance->attributes); + + if (result != 0) { + mpack_finish_map(&context->writer); + return -1; + } + + mpack_write_cstr(&context->writer, "dropped_attribute_count"); + mpack_write_u32(&context->writer, instance->dropped_attributes_count); + + mpack_finish_map(&context->writer); + + if (mpack_writer_error(&context->writer) != mpack_ok) { + return -1; + } + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + + + + + +static int encode_cprof_scope_profiles( + struct cprof_msgpack_encoding_context *context, + struct cprof_scope_profiles *instance) { + int result; + struct cfl_list *iterator; + struct cprof_profile *profile; + + mpack_start_map(&context->writer, 3); + mpack_write_cstr(&context->writer, "instrumentation_scope"); + + result = encode_cprof_instrumentation_scope(context, instance->scope); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + + mpack_write_cstr(&context->writer, "profiles"); + + mpack_start_array(&context->writer, cfl_list_size(&instance->profiles)); + + if (!cfl_list_is_empty(&instance->profiles)) { + cfl_list_foreach(iterator, + &instance->profiles) { + profile = cfl_list_entry( + iterator, + struct cprof_profile, _head); + + result = encode_cprof_profile(context, profile); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + mpack_write_cstr(&context->writer, "schema_url"); + + mpack_write_sds_or_nil(&context->writer, + instance->schema_url); + + mpack_finish_map(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int pack_context_header(struct cprof_msgpack_encoding_context *context, + struct cprof *profile) +{ + mpack_write_cstr(&context->writer, "meta"); + mpack_start_map(&context->writer, 0); + mpack_finish_map(&context->writer); + + return 0; +} + +static int pack_context_profiles(struct cprof_msgpack_encoding_context *context, + struct cprof *profile) +{ + int result; + struct cfl_list *iterator; + size_t profile_count; + struct cprof_resource_profiles *resource_profiles; + + profile_count = 0 ; + profile_count = cfl_list_size(&profile->profiles); + + mpack_write_cstr(&context->writer, "profiles"); + mpack_start_array(&context->writer, profile_count); + + if (!cfl_list_is_empty(&profile->profiles)) { + cfl_list_foreach(iterator, + &profile->profiles) { + resource_profiles = cfl_list_entry( + iterator, + struct cprof_resource_profiles, _head); + + result = encode_cprof_resource_profiles(context, resource_profiles); + + if (result != CPROF_ENCODE_MSGPACK_SUCCESS) { + return result; + } + } + } + + mpack_finish_array(&context->writer); + + return CPROF_ENCODE_MSGPACK_SUCCESS; +} + +static int pack_context(struct cprof_msgpack_encoding_context *context, + struct cprof *profile) +{ + int result; + + mpack_start_map(&context->writer, 2); + + result = pack_context_header(context, profile); + + if (result != 0) { + return -1; + } + + result = pack_context_profiles(context, profile); + + if (result != 0) { + return -2; + } + + mpack_finish_map(&context->writer); /* outermost context scope */ + + return 0; +} + +int cprof_encode_msgpack_create(cfl_sds_t *result_buffer, + struct cprof *profile) +{ + int result; + mpack_error_t writer_result; + struct cprof_msgpack_encoding_context context; + + *result_buffer = NULL; + + memset(&context, 0, sizeof(context)); + + mpack_writer_init_growable(&context.writer, + &context.output_buffer, + &context.output_size); + + + result = pack_context(&context, profile); + + writer_result = mpack_writer_destroy(&context.writer); + + if (writer_result != mpack_ok) { + fprintf(stderr, "An error occurred encoding the data!\n"); + + if (result == CPROF_ENCODE_MSGPACK_SUCCESS) { + result = CPROF_ENCODE_MSGPACK_INVALID_ARGUMENT_ERROR; + } + } + + if (result == CPROF_ENCODE_MSGPACK_SUCCESS && + writer_result == mpack_ok) { + *result_buffer = cfl_sds_create_len(context.output_buffer, context.output_size); + + if (*result_buffer == NULL) { + result = CPROF_ENCODE_MSGPACK_ALLOCATION_ERROR; + } + } + + if (context.output_buffer != NULL) { + free(context.output_buffer); + } + + return result; +} + +void cprof_encode_msgpack_destroy(cfl_sds_t instance) +{ + if (instance != NULL) { + cfl_sds_destroy(instance); + } +} diff --git a/lib/cprofiles/cprof_encode_opentelemetry.c b/lib/cprofiles/cprof_encode_opentelemetry.c new file mode 100644 index 00000000000..c3133b06076 --- /dev/null +++ b/lib/cprofiles/cprof_encode_opentelemetry.c @@ -0,0 +1,2764 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +static int is_string_releaseable(char *address) + { + return (address != NULL && + address != protobuf_c_empty_string); +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_to_otlp_any_value(struct cfl_variant *value); +static inline Opentelemetry__Proto__Common__V1__KeyValue *cfl_variant_kvpair_to_otlp_kvpair(struct cfl_kvpair *input_pair); +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_kvlist_to_otlp_any_value(struct cfl_variant *value); + +static inline void otlp_any_value_destroy(Opentelemetry__Proto__Common__V1__AnyValue *value); +static inline void otlp_kvpair_destroy(Opentelemetry__Proto__Common__V1__KeyValue *kvpair); +static inline void otlp_kvlist_destroy(Opentelemetry__Proto__Common__V1__KeyValueList *kvlist); +static inline void otlp_array_destroy(Opentelemetry__Proto__Common__V1__ArrayValue *array); + +static inline void otlp_kvpair_list_destroy(Opentelemetry__Proto__Common__V1__KeyValue **pair_list, size_t entry_count); + +static inline void otlp_kvpair_destroy(Opentelemetry__Proto__Common__V1__KeyValue *kvpair) +{ + if (kvpair != NULL) { + if (kvpair->key != NULL) { + free(kvpair->key); + } + + if (kvpair->value != NULL) { + otlp_any_value_destroy(kvpair->value); + } + + free(kvpair); + } +} + +static inline void otlp_kvlist_destroy(Opentelemetry__Proto__Common__V1__KeyValueList *kvlist) +{ + size_t index; + + if (kvlist != NULL) { + if (kvlist->values != NULL) { + for (index = 0 ; index < kvlist->n_values ; index++) { + otlp_kvpair_destroy(kvlist->values[index]); + } + + free(kvlist->values); + } + + free(kvlist); + } +} + +static inline void otlp_array_destroy(Opentelemetry__Proto__Common__V1__ArrayValue *array) +{ + size_t index; + + if (array != NULL) { + if (array->values != NULL) { + for (index = 0 ; index < array->n_values ; index++) { + otlp_any_value_destroy(array->values[index]); + } + + free(array->values); + } + + free(array); + } +} + +static inline void otlp_any_value_destroy(Opentelemetry__Proto__Common__V1__AnyValue *value) +{ + if (value != NULL) { + if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { + if (value->string_value != NULL) { + free(value->string_value); + } + } + else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE) { + if (value->array_value != NULL) { + otlp_array_destroy(value->array_value); + } + } + else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE) { + if (value->kvlist_value != NULL) { + otlp_kvlist_destroy(value->kvlist_value); + } + } + else if (value->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) { + if (value->bytes_value.data != NULL) { + free(value->bytes_value.data); + } + } + + free(value); + } +} + +static inline Opentelemetry__Proto__Common__V1__KeyValue **otlp_kvpair_list_initialize(size_t entry_count) +{ + Opentelemetry__Proto__Common__V1__KeyValue **result; + + result = \ + calloc(entry_count, sizeof(Opentelemetry__Proto__Common__V1__KeyValue *)); + + return result; +} + + +static Opentelemetry__Proto__Common__V1__ArrayValue *otlp_array_value_initialize(size_t entry_count) +{ + Opentelemetry__Proto__Common__V1__ArrayValue *value; + + value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__ArrayValue)); + + if (value != NULL) { + opentelemetry__proto__common__v1__array_value__init(value); + + if (entry_count > 0) { + value->values = \ + calloc(entry_count, + sizeof(Opentelemetry__Proto__Common__V1__AnyValue *)); + + if (value->values == NULL) { + free(value); + + value = NULL; + } + else { + value->n_values = entry_count; + } + } + } + + return value; +} + +static Opentelemetry__Proto__Common__V1__KeyValue *otlp_kvpair_value_initialize() +{ + Opentelemetry__Proto__Common__V1__KeyValue *value; + + value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__KeyValue)); + + if (value != NULL) { + opentelemetry__proto__common__v1__key_value__init(value); + } + + return value; +} + +static Opentelemetry__Proto__Common__V1__KeyValueList *otlp_kvlist_value_initialize(size_t entry_count) +{ + Opentelemetry__Proto__Common__V1__KeyValueList *value; + + value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__KeyValueList)); + + if (value != NULL) { + opentelemetry__proto__common__v1__key_value_list__init(value); + + if (entry_count > 0) { + value->values = \ + calloc(entry_count, + sizeof(Opentelemetry__Proto__Common__V1__KeyValue *)); + + if (value->values == NULL) { + free(value); + + value = NULL; + } + else { + value->n_values = entry_count; + } + } + } + + return value; +} + +static Opentelemetry__Proto__Common__V1__AnyValue *otlp_any_value_initialize(int data_type, size_t entry_count) +{ + Opentelemetry__Proto__Common__V1__AnyValue *value; + + value = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__AnyValue)); + + if (value == NULL) { + return NULL; + } + + opentelemetry__proto__common__v1__any_value__init(value); + + if (data_type == CFL_VARIANT_STRING) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE; + } + else if (data_type == CFL_VARIANT_BOOL) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE; + } + else if (data_type == CFL_VARIANT_INT) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE; + } + else if (data_type == CFL_VARIANT_DOUBLE) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE; + } + else if (data_type == CFL_VARIANT_ARRAY) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE; + + value->array_value = otlp_array_value_initialize(entry_count); + + if (value->array_value == NULL) { + free(value); + + value = NULL; + } + } + else if (data_type == CFL_VARIANT_KVLIST) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE; + + value->kvlist_value = otlp_kvlist_value_initialize(entry_count); + + if (value->kvlist_value == NULL) { + free(value); + + value = NULL; + } + } + else if (data_type == CFL_VARIANT_BYTES) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE; + } + else if (data_type == CFL_VARIANT_REFERENCE) { + value->value_case = OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE; + } + else { + free(value); + + value = NULL; + } + + return value; +} + +static inline Opentelemetry__Proto__Common__V1__KeyValue *cfl_variant_kvpair_to_otlp_kvpair(struct cfl_kvpair *input_pair) +{ + Opentelemetry__Proto__Common__V1__KeyValue *pair; + + pair = otlp_kvpair_value_initialize(); + + if (pair != NULL) { + pair->key = strdup(input_pair->key); + + if (pair->key != NULL) { + pair->value = cfl_variant_to_otlp_any_value(input_pair->val); + + if (pair->value == NULL) { + free(pair->key); + + pair->key = NULL; + } + } + + if (pair->key == NULL) { + free(pair); + + pair = NULL; + } + } + + return pair; +} + +static inline void otlp_kvpair_list_destroy(Opentelemetry__Proto__Common__V1__KeyValue **pair_list, size_t entry_count) +{ + size_t index; + + if (pair_list != NULL) { + for (index = 0 ; index < entry_count ; index++) { + otlp_kvpair_destroy(pair_list[index]); + } + + free(pair_list); + } +} + +static inline Opentelemetry__Proto__Common__V1__KeyValue **cfl_kvlist_to_otlp_kvpair_list(struct cfl_kvlist *kvlist) +{ + size_t entry_count; + Opentelemetry__Proto__Common__V1__KeyValue *keyvalue; + struct cfl_list *iterator; + Opentelemetry__Proto__Common__V1__KeyValue **result; + struct cfl_kvpair *kvpair; + size_t index; + + entry_count = cfl_kvlist_count(kvlist); + + result = otlp_kvpair_list_initialize(entry_count + 1); + + if (result != NULL) { + index = 0; + + cfl_list_foreach(iterator, &kvlist->list) { + kvpair = cfl_list_entry(iterator, struct cfl_kvpair, _head); + + keyvalue = cfl_variant_kvpair_to_otlp_kvpair(kvpair); + + if (keyvalue == NULL) { + otlp_kvpair_list_destroy(result, entry_count); + + result = NULL; + + break; + } + + result[index++] = keyvalue; + } + } + + return result; +} + + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_kvlist_to_otlp_any_value(struct cfl_variant *value) +{ + size_t entry_count; + Opentelemetry__Proto__Common__V1__KeyValue *keyvalue; + struct cfl_list *iterator; + Opentelemetry__Proto__Common__V1__AnyValue *result; + struct cfl_kvpair *kvpair; + struct cfl_kvlist *kvlist; + size_t index; + + + kvlist = value->data.as_kvlist; + + entry_count = cfl_kvlist_count(kvlist); + + result = otlp_any_value_initialize(CFL_VARIANT_KVLIST, entry_count); + + if (result != NULL) { + index = 0; + + cfl_list_foreach(iterator, &kvlist->list) { + kvpair = cfl_list_entry(iterator, struct cfl_kvpair, _head); + + keyvalue = cfl_variant_kvpair_to_otlp_kvpair(kvpair); + + if (keyvalue == NULL) { + otlp_any_value_destroy(result); + + result = NULL; + + break; + } + + result->kvlist_value->values[index++] = keyvalue; + } + } + + return result; +} + + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_array_to_otlp_any_value(struct cfl_variant *value) +{ + size_t entry_count; + Opentelemetry__Proto__Common__V1__AnyValue *entry_value; + Opentelemetry__Proto__Common__V1__AnyValue *result; + struct cfl_array *array; + size_t index; + + array = value->data.as_array; + + entry_count = array->entry_count; + + result = otlp_any_value_initialize(CFL_VARIANT_ARRAY, entry_count); + + if (result != NULL) { + index = 0; + + for (index = 0 ; index < entry_count ; index++) { + entry_value = cfl_variant_to_otlp_any_value(cfl_array_fetch_by_index(array, index)); + + if (entry_value == NULL) { + otlp_any_value_destroy(result); + + result = NULL; + + break; + } + + result->array_value->values[index] = entry_value; + } + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_string_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + result = otlp_any_value_initialize(CFL_VARIANT_STRING, 0); + + if (result != NULL) { + result->string_value = strdup(value->data.as_string); + + if (result->string_value == NULL) { + otlp_any_value_destroy(result); + + result = NULL; + } + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_boolean_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + result = otlp_any_value_initialize(CFL_VARIANT_BOOL, 0); + + if (result != NULL) { + result->bool_value = value->data.as_bool; + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_int64_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + result = otlp_any_value_initialize(CFL_VARIANT_INT, 0); + + if (result != NULL) { + result->int_value = value->data.as_int64; + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_double_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + result = otlp_any_value_initialize(CFL_VARIANT_DOUBLE, 0); + + if (result != NULL) { + result->double_value = value->data.as_double; + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_binary_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + result = otlp_any_value_initialize(CFL_VARIANT_BYTES, 0); + + if (result != NULL) { + result->bytes_value.len = cfl_sds_len(value->data.as_bytes); + result->bytes_value.data = calloc(result->bytes_value.len, sizeof(char)); + + if (result->bytes_value.data) { + memcpy(result->bytes_value.data, value->data.as_bytes, result->bytes_value.len); + } + else { + otlp_any_value_destroy(result); + result = NULL; + } + } + + return result; +} + +static inline Opentelemetry__Proto__Common__V1__AnyValue *cfl_variant_to_otlp_any_value(struct cfl_variant *value) +{ + Opentelemetry__Proto__Common__V1__AnyValue *result; + + if (value->type == CFL_VARIANT_STRING) { + result = cfl_variant_string_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_BOOL) { + result = cfl_variant_boolean_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_INT) { + result = cfl_variant_int64_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_DOUBLE) { + result = cfl_variant_double_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_ARRAY) { + result = cfl_variant_array_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_KVLIST) { + result = cfl_variant_kvlist_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_BYTES) { + result = cfl_variant_binary_to_otlp_any_value(value); + } + else if (value->type == CFL_VARIANT_REFERENCE) { + result = cfl_variant_string_to_otlp_any_value(value); + } + else { + result = NULL; + } + + return result; +} + + + + + + + +static void destroy_attribute( + Opentelemetry__Proto__Common__V1__KeyValue *attribute); + +static void destroy_attribute_list( + Opentelemetry__Proto__Common__V1__KeyValue **attribute_list); + +static Opentelemetry__Proto__Common__V1__KeyValue ** + initialize_attribute_list( + size_t element_count); + +static void destroy_attribute(Opentelemetry__Proto__Common__V1__KeyValue *attribute) +{ + if (attribute != NULL) { + if (attribute->value != NULL) { + if (attribute->value->value_case == \ + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { + if (is_string_releaseable(attribute->value->string_value)) { + free(attribute->value->string_value); + } + } + + free(attribute->value); + } + + if (is_string_releaseable(attribute->key)) { + free(attribute->key); + } + + free(attribute); + } +} + +static void destroy_attribute_list( + Opentelemetry__Proto__Common__V1__KeyValue **attribute_list) +{ + size_t element_index; + + if (attribute_list != NULL) { + for (element_index = 0 ; + attribute_list[element_index] != NULL ; + element_index++) { + destroy_attribute(attribute_list[element_index]); + + attribute_list[element_index] = NULL; + } + + free(attribute_list); + } +} + +static Opentelemetry__Proto__Common__V1__KeyValue ** + initialize_attribute_list( + size_t element_count) +{ + Opentelemetry__Proto__Common__V1__KeyValue **attribute_list; + + attribute_list = calloc(element_count + 1, + sizeof(Opentelemetry__Proto__Common__V1__KeyValue *)); + + return attribute_list; +} + + + + + +static void destroy_value_type( + Opentelemetry__Proto__Profiles__V1development__ValueType * + instance) +{ + if (instance != NULL) { + free(instance); + } +} + +static void destroy_sample( + Opentelemetry__Proto__Profiles__V1development__Sample * + instance) +{ + if (instance != NULL) { + if (instance->values != NULL) { + free(instance->values); + } + + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); + } + + if (instance->timestamps_unix_nano != NULL) { + free(instance->timestamps_unix_nano); + } + + free(instance); + } +} + + +static void destroy_mapping( + Opentelemetry__Proto__Profiles__V1development__Mapping * + instance) +{ + if (instance != NULL) { + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); + } + + free(instance); + } +} + + +static void destroy_resource( + Opentelemetry__Proto__Resource__V1__Resource * + instance) +{ + if (instance != NULL) { + destroy_attribute_list(instance->attributes); + + free(instance); + } +} + +static void destroy_line( + Opentelemetry__Proto__Profiles__V1development__Line * + instance) +{ + if (instance != NULL) { + free(instance); + } +} + +static void destroy_link( + Opentelemetry__Proto__Profiles__V1development__Link * + instance) +{ + if (instance != NULL) { + if (instance->trace_id.data != NULL) { + if (is_string_releaseable((cfl_sds_t) instance->trace_id.data)) { + cfl_sds_destroy((cfl_sds_t) instance->trace_id.data); + } + } + + if (instance->span_id.data != NULL) { + if (is_string_releaseable((cfl_sds_t) instance->span_id.data)) { + cfl_sds_destroy((cfl_sds_t) instance->span_id.data); + } + } + + free(instance); + } +} + + +static void destroy_location( + Opentelemetry__Proto__Profiles__V1development__Location * + instance) +{ + size_t index; + + if (instance != NULL) { + if (instance->lines != NULL) { + for (index = 0 ; index < instance->n_lines ; index++) { + destroy_line(instance->lines[index]); + } + + free(instance->lines); + } + + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); + } + + free(instance); + } +} + +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); + } +} + +static void destroy_function( + Opentelemetry__Proto__Profiles__V1development__Function * + instance) +{ + if (instance != NULL) { + free(instance); + } +} + +static void destroy_instrumentation_scope( + Opentelemetry__Proto__Common__V1__InstrumentationScope * + instance) +{ + if (instance != NULL) { + destroy_attribute_list(instance->attributes); + + if (instance->name != NULL) { + if (is_string_releaseable(instance->name)) { + cfl_sds_destroy(instance->name); + } + } + + if (instance->version != NULL) { + if (is_string_releaseable(instance->version)) { + cfl_sds_destroy(instance->version); + } + } + + free(instance); + } +} + +static void destroy_profile( + Opentelemetry__Proto__Profiles__V1development__Profile * + instance) +{ + size_t index; + + if (instance != NULL) { + if (instance->sample_type != NULL) { + destroy_value_type(instance->sample_type); + } + + if (instance->samples != NULL) { + for (index = 0 ; index < instance->n_samples ; index++) { + destroy_sample(instance->samples[index]); + } + free(instance->samples); + } + + if (instance->period_type != NULL) { + destroy_value_type(instance->period_type); + } + + if (instance->attribute_indices != NULL) { + free(instance->attribute_indices); + } + + if (instance->profile_id.data != NULL && is_string_releaseable((char *)instance->profile_id.data)) { + free(instance->profile_id.data); + } + + if (instance->original_payload_format != NULL && is_string_releaseable(instance->original_payload_format)) { + free(instance->original_payload_format); + } + + if (instance->original_payload.data != NULL && is_string_releaseable((char *)instance->original_payload.data)) { + free(instance->original_payload.data); + } + + free(instance); + } +} + +static void destroy_scope_profiles( + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles * + instance) +{ + size_t index; + + if (instance != NULL) { + if (instance->scope != NULL) { + destroy_instrumentation_scope(instance->scope); + } + + if (instance->profiles != NULL) { + for (index = 0 ; index < instance->n_profiles ; index++) { + destroy_profile(instance->profiles[index]); + } + + free(instance->profiles); + } + + if (instance->schema_url != NULL) { + if (is_string_releaseable(instance->schema_url)) { + cfl_sds_destroy(instance->schema_url); + } + } + + free(instance); + } +} +static void destroy_resource_profiles( + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles * + instance) +{ + size_t index; + + if (instance != NULL) { + if (instance->resource != NULL) { + destroy_resource(instance->resource); + } + + if (instance->scope_profiles != NULL) { + for (index = 0 ; index < instance->n_scope_profiles ; index++) { + destroy_scope_profiles(instance->scope_profiles[index]); + } + + free(instance->scope_profiles); + } + + if (instance->schema_url != NULL) { + if (is_string_releaseable(instance->schema_url)) { + cfl_sds_destroy(instance->schema_url); + } + } + + free(instance); + } +} + +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) { + cfl_sds_destroy((cfl_sds_t) 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) +{ + size_t index; + + if (instance != NULL) { + if (instance->resource_profiles != NULL) { + for (index = 0 ; index < instance->n_resource_profiles ; index++) { + destroy_resource_profiles(instance->resource_profiles[index]); + } + + free(instance->resource_profiles); + } + + if (instance->dictionary != NULL) { + destroy_profiles_dictionary(instance->dictionary); + } + + free(instance); + } +} + +/* + * 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 *attribute_map; /* profile attribute_units index -> dict attribute index */ + size_t attribute_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->attribute_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++; +} + +static struct cfl_variant *fetch_profile_attribute_value( + struct cprof_profile *profile, + struct cprof_attribute_unit *attribute_unit) +{ + char *attribute_key; + + if (profile == NULL || + attribute_unit == NULL || + profile->attributes == NULL || + profile->string_table == NULL || + attribute_unit->attribute_key < 0 || + (size_t) attribute_unit->attribute_key >= profile->string_table_count) { + return NULL; + } + + attribute_key = profile->string_table[attribute_unit->attribute_key]; + + if (attribute_key == NULL) { + return NULL; + } + + return cfl_kvlist_fetch(profile->attributes, attribute_key); +} + +static size_t count_profile_attribute_references( + struct cprof_profile *profile) +{ + struct cfl_list *iterator; + struct cprof_attribute_unit *attribute_unit; + size_t count; + + count = 0; + + cfl_list_foreach(iterator, &profile->attribute_units) { + attribute_unit = cfl_list_entry(iterator, struct cprof_attribute_unit, _head); + + if (fetch_profile_attribute_value(profile, attribute_unit) != NULL) { + count++; + } + } + + return count; +} + +static int append_profile_attribute_entry( + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dict, + struct cprof_profile *profile, + struct cprof_attribute_unit *attribute_unit, + const int32_t *string_map, + size_t string_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **table; + struct cfl_variant *attribute_value; + + entry = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit)); + if (entry == NULL) { + return -1; + } + + opentelemetry__proto__profiles__v1development__key_value_and_unit__init(entry); + + if (attribute_unit->attribute_key >= 0 && + (size_t) attribute_unit->attribute_key < string_map_count) { + entry->key_strindex = string_map[attribute_unit->attribute_key]; + } + + if (attribute_unit->unit >= 0 && + (size_t) attribute_unit->unit < string_map_count) { + entry->unit_strindex = string_map[attribute_unit->unit]; + } + + attribute_value = fetch_profile_attribute_value(profile, attribute_unit); + + if (attribute_value != NULL) { + entry->value = cfl_variant_to_otlp_any_value(attribute_value); + if (entry->value == NULL) { + destroy_keyvalueandunit(entry); + return -1; + } + } + + table = realloc(dict->attribute_table, + (dict->n_attribute_table + 1) * + sizeof(Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *)); + if (table == NULL) { + destroy_keyvalueandunit(entry); + return -1; + } + + dict->attribute_table = table; + dict->attribute_table[dict->n_attribute_table] = entry; + + return (int32_t) dict->n_attribute_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, + const int32_t *attribute_map, + size_t attribute_map_count) +{ + Opentelemetry__Proto__Profiles__V1development__Mapping *otlp; + size_t index; + + 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; + } + + if (m->attributes_count > 0) { + otlp->attribute_indices = calloc(m->attributes_count, sizeof(int32_t)); + if (otlp->attribute_indices == NULL) { + destroy_mapping(otlp); + return NULL; + } + + otlp->n_attribute_indices = m->attributes_count; + + for (index = 0; index < m->attributes_count; index++) { + if (m->attributes[index] >= attribute_map_count) { + destroy_mapping(otlp); + return NULL; + } + + otlp->attribute_indices[index] = attribute_map[m->attributes[index]]; + } + } + + 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, + const int32_t *attribute_map, + size_t attribute_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, + attribute_map, attribute_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, + const int32_t *attribute_map, + size_t attribute_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, loc->attributes_count); + 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++; + } + + for (idx = 0; idx < loc->attributes_count; idx++) { + if (loc->attributes[idx] >= attribute_map_count) { + destroy_location(otlp); + return NULL; + } + + otlp->attribute_indices[idx] = attribute_map[loc->attributes[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, + const int32_t *attribute_map, + size_t attribute_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, + attribute_map, attribute_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 *attribute_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; + } + } + + st->attribute_map_count = cfl_list_size(&profile->attribute_units); + if (st->attribute_map_count > 0) { + struct cprof_attribute_unit *attribute_unit; + + st->attribute_map = malloc(st->attribute_map_count * sizeof(int32_t)); + if (st->attribute_map == NULL) { + goto fail; + } + + i = 0; + cfl_list_foreach(attribute_iter, &profile->attribute_units) { + attribute_unit = cfl_list_entry(attribute_iter, + struct cprof_attribute_unit, + _head); + si = append_profile_attribute_entry(dict, + profile, + attribute_unit, + st->string_map, + st->string_map_count); + if (si < 0) { + goto fail; + } + + st->attribute_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, + st->attribute_map, st->attribute_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, + st->attribute_map, st->attribute_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 * + initialize_value_type() { + Opentelemetry__Proto__Profiles__V1development__ValueType *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__ValueType)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__value_type__init(instance); + + return instance; +} + + + +static + Opentelemetry__Proto__Profiles__V1development__Sample * + initialize_sample( + size_t value_count, + size_t attributes_count, + size_t timestamps_count) { + Opentelemetry__Proto__Profiles__V1development__Sample *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Sample)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__sample__init(instance); + + if (value_count > 0) { + instance->values = calloc(value_count, sizeof(int64_t)); + + if (instance->values == NULL) { + destroy_sample(instance); + + return NULL; + } + + instance->n_values = value_count; + } + + if (attributes_count > 0) { + instance->attribute_indices = calloc(attributes_count, sizeof(int32_t)); + + if (instance->attribute_indices == NULL) { + destroy_sample(instance); + + return NULL; + } + + instance->n_attribute_indices = attributes_count; + } + + if (timestamps_count > 0) { + instance->timestamps_unix_nano = calloc(timestamps_count, sizeof(uint64_t)); + + if (instance->timestamps_unix_nano == NULL) { + destroy_sample(instance); + + return NULL; + } + + instance->n_timestamps_unix_nano = timestamps_count; + } + + return instance; +} + + + + + + +static + Opentelemetry__Proto__Resource__V1__Resource * + initialize_resource(size_t attribute_count) { + Opentelemetry__Proto__Resource__V1__Resource *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Resource__V1__Resource)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__resource__v1__resource__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__Link * + initialize_link() { + Opentelemetry__Proto__Profiles__V1development__Link *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Link)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__link__init(instance); + + return instance; +} + +static + Opentelemetry__Proto__Profiles__V1development__Location * + initialize_location(size_t line_count, size_t attribute_count) { + Opentelemetry__Proto__Profiles__V1development__Location *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__Location)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__location__init(instance); + + if (line_count > 0) { + instance->lines = calloc(line_count, sizeof(void *)); + + if (instance->lines == NULL) { + destroy_location(instance); + + return NULL; + } + + instance->n_lines = line_count; + } + + if (attribute_count > 0) { + instance->attribute_indices = calloc(attribute_count, sizeof(int32_t)); + + if (instance->attribute_indices == NULL) { + destroy_location(instance); + + return NULL; + } + + instance->n_attribute_indices = attribute_count; + } + + return instance; +} + +static + Opentelemetry__Proto__Common__V1__InstrumentationScope * + initialize_instrumentation_scope(size_t attribute_count) { + Opentelemetry__Proto__Common__V1__InstrumentationScope *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Common__V1__InstrumentationScope)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__common__v1__instrumentation_scope__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__Profile * + 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)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__profile__init(instance); + + if (sample_count > 0) { + instance->samples = calloc(sample_count, sizeof(void *)); + + if (instance->samples == NULL) { + destroy_profile(instance); + + return NULL; + } + + instance->n_samples = sample_count; + } + + if (attribute_index_count > 0) { + instance->attribute_indices = calloc(attribute_index_count, sizeof(int32_t)); + + if (instance->attribute_indices == NULL) { + destroy_profile(instance); + + return NULL; + } + + instance->n_attribute_indices = attribute_index_count; + } + + return instance; +} + + +static + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles * + initialize_scope_profiles(size_t profiles_count) { + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__ScopeProfiles)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__scope_profiles__init(instance); + + instance->profiles = calloc(profiles_count, sizeof(void *)); + + if (instance->profiles == NULL) { + free(instance); + + return NULL; + } + + instance->n_profiles = profiles_count; + + return instance; +} + +static + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles * + initialize_resource_profiles(size_t scope_profiles_count) { + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Profiles__V1development__ResourceProfiles)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__profiles__v1development__resource_profiles__init(instance); + + instance->scope_profiles = calloc(scope_profiles_count, sizeof(void *)); + + if (instance->scope_profiles == NULL) { + free(instance); + + return NULL; + } + + instance->n_scope_profiles = scope_profiles_count; + + return instance; +} + + +static + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest * + initialize_export_profiles_service_request(size_t resource_profiles_count) { + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *instance; + + instance = calloc(1, sizeof(Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest)); + + if (instance == NULL) { + return NULL; + } + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__init(instance); + + instance->resource_profiles = calloc(resource_profiles_count, sizeof(void *)); + + if (instance->resource_profiles == NULL) { + free(instance); + + return NULL; + } + + instance->n_resource_profiles = resource_profiles_count; + + return instance; +} + + + + + +static int pack_cprof_resource( + Opentelemetry__Proto__Resource__V1__Resource **output_instance, + struct cprof_resource *input_instance) +{ + Opentelemetry__Proto__Resource__V1__Resource *otlp_resource; + + if (input_instance != NULL) { + otlp_resource = initialize_resource(0); + + if (otlp_resource == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + otlp_resource->attributes = cfl_kvlist_to_otlp_kvpair_list(input_instance->attributes); + + if (otlp_resource->attributes == NULL) { + destroy_resource(otlp_resource); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + otlp_resource->n_attributes = cfl_kvlist_count(input_instance->attributes); + + otlp_resource->dropped_attributes_count = \ + input_instance->dropped_attributes_count; + + *output_instance = otlp_resource; + } + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + +static int pack_cprof_instrumentation_scope( + Opentelemetry__Proto__Common__V1__InstrumentationScope **output_instance, + struct cprof_instrumentation_scope *input_instance) +{ + Opentelemetry__Proto__Common__V1__InstrumentationScope *otlp_instrumentation_scope; + + if (input_instance != NULL) { + otlp_instrumentation_scope = initialize_instrumentation_scope(0); + + if (otlp_instrumentation_scope == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + otlp_instrumentation_scope->attributes = cfl_kvlist_to_otlp_kvpair_list(input_instance->attributes); + + if (otlp_instrumentation_scope->attributes == NULL) { + destroy_instrumentation_scope(otlp_instrumentation_scope); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + otlp_instrumentation_scope->n_attributes = cfl_kvlist_count(input_instance->attributes); + + if (input_instance->name != NULL) { + otlp_instrumentation_scope->name = cfl_sds_create(input_instance->name); + + if (otlp_instrumentation_scope->name == NULL) { + destroy_instrumentation_scope(otlp_instrumentation_scope); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + if (input_instance->version != NULL) { + otlp_instrumentation_scope->version = cfl_sds_create(input_instance->version); + + if (otlp_instrumentation_scope->version == NULL) { + destroy_instrumentation_scope(otlp_instrumentation_scope); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + otlp_instrumentation_scope->dropped_attributes_count = \ + input_instance->dropped_attributes_count; + + *output_instance = otlp_instrumentation_scope; + } + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + +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) +{ + Opentelemetry__Proto__Profiles__V1development__ValueType *otlp_value_type; + + otlp_value_type = initialize_value_type(); + + if (otlp_value_type == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + 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; + + 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) +{ + Opentelemetry__Proto__Profiles__V1development__Sample *otlp_sample; + size_t index; + + otlp_sample = initialize_sample(input_instance->value_count, + input_instance->attributes_count, + input_instance->timestamps_count); + + if (otlp_sample == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + 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 ; + index++) { + otlp_sample->values[index] = input_instance->values[index]; + } + + for (index = 0 ; + index < input_instance->attributes_count ; + index++) { + if (encoding_state != NULL && + encoding_state->attribute_map != NULL && + input_instance->attributes[index] < encoding_state->attribute_map_count) { + otlp_sample->attribute_indices[index] = + encoding_state->attribute_map[input_instance->attributes[index]]; + } + else { + destroy_sample(otlp_sample); + return CPROF_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + } + + 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 ; + index++) { + otlp_sample->timestamps_unix_nano[index] = input_instance->timestamps_unix_nano[index]; + } + + *output_instance = otlp_sample; + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + + +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); + +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 profile_encoding_state *encoding_state) +{ + Opentelemetry__Proto__Profiles__V1development__Profile *otlp_profile; + struct cfl_list *iterator; + struct cprof_attribute_unit *attribute_unit; + struct cprof_sample *sample; + struct cprof_value_type *sample_type; + int result; + size_t attribute_count; + size_t index; + + attribute_count = count_profile_attribute_references(input_instance); + + otlp_profile = initialize_profile(cfl_list_size(&input_instance->samples), + attribute_count); + + if (otlp_profile == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + /* 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, encoding_state); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + destroy_profile(otlp_profile); + return result; + } + } + + otlp_profile->profile_id.data = + (uint8_t *) cfl_sds_create_len((char *) input_instance->profile_id, + sizeof(input_instance->profile_id)); + if (otlp_profile->profile_id.data == NULL) { + destroy_profile(otlp_profile); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + otlp_profile->profile_id.len = sizeof(input_instance->profile_id); + otlp_profile->dropped_attributes_count = input_instance->dropped_attributes_count; + + if (input_instance->original_payload_format != NULL) { + otlp_profile->original_payload_format = + cfl_sds_create(input_instance->original_payload_format); + + if (otlp_profile->original_payload_format == NULL) { + destroy_profile(otlp_profile); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + if (input_instance->original_payload != NULL) { + otlp_profile->original_payload.data = + (uint8_t *) cfl_sds_create_len(input_instance->original_payload, + cfl_sds_len(input_instance->original_payload)); + + if (otlp_profile->original_payload.data == NULL) { + destroy_profile(otlp_profile); + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + otlp_profile->original_payload.len = + cfl_sds_len(input_instance->original_payload); + } + + index = 0; + attribute_count = 0; + cfl_list_foreach(iterator, &input_instance->attribute_units) { + attribute_unit = cfl_list_entry(iterator, struct cprof_attribute_unit, _head); + + if (fetch_profile_attribute_value(input_instance, attribute_unit) == NULL) { + attribute_count++; + continue; + } + + if (encoding_state == NULL || + encoding_state->attribute_map == NULL || + attribute_count >= encoding_state->attribute_map_count || + index >= otlp_profile->n_attribute_indices) { + destroy_profile(otlp_profile); + return CPROF_ENCODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + otlp_profile->attribute_indices[index] = + encoding_state->attribute_map[attribute_count]; + index++; + attribute_count++; + } + + 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, encoding_state, index); + + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + destroy_profile(otlp_profile); + return result; + } + index++; + } + + 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, encoding_state); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + destroy_profile(otlp_profile); + return result; + } + + otlp_profile->period = input_instance->period; + + *output_instance = otlp_profile; + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + +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) +{ + 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; + + otlp_scope_profiles = initialize_scope_profiles(cfl_list_size(&input_instance->profiles)); + + if (otlp_scope_profiles == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + if (input_instance->scope != NULL) { + result = pack_cprof_instrumentation_scope(&otlp_scope_profiles->scope, input_instance->scope); + + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } + + index = 0; + cfl_list_foreach(iterator, + &input_instance->profiles) { + profile = cfl_list_entry( + 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, + encoding_state); + + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + destroy_scope_profiles(otlp_scope_profiles); + + return result; + } + + index++; + } + + if (input_instance->schema_url != NULL) { + otlp_scope_profiles->schema_url = cfl_sds_create(input_instance->schema_url); + + if (otlp_scope_profiles->schema_url == NULL) { + destroy_scope_profiles(otlp_scope_profiles); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + *output_instance = otlp_scope_profiles; + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + +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) +{ + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *otlp_resource_profiles; + struct cprof_scope_profiles *scope_profiles; + struct cfl_list *iterator; + int result; + size_t index; + + otlp_resource_profiles = initialize_resource_profiles(cfl_list_size(&input_instance->scope_profiles)); + + if (otlp_resource_profiles == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = pack_cprof_resource(&otlp_resource_profiles->resource, input_instance->resource); + + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + return result; + } + + index = 0; + cfl_list_foreach(iterator, + &input_instance->scope_profiles) { + scope_profiles = cfl_list_entry( + iterator, + struct cprof_scope_profiles, _head); + + result = pack_cprof_scope_profiles( + &otlp_resource_profiles->scope_profiles[index], + scope_profiles, + internal_ctx); + + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + destroy_resource_profiles(otlp_resource_profiles); + + return result; + } + + index++; + } + + if (input_instance->schema_url != NULL) { + otlp_resource_profiles->schema_url = cfl_sds_create(input_instance->schema_url); + + if (otlp_resource_profiles->schema_url == NULL) { + destroy_resource_profiles(otlp_resource_profiles); + + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + + *output_instance = otlp_resource_profiles; + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + + +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( + encoder_internal_ctx_t *internal_ctx, + struct cprof *profile) +{ + size_t index; + int result; + struct cfl_list *iterator; + struct cprof_resource_profiles *resource_profiles; + + internal_ctx->pub->export_service_request = \ + initialize_export_profiles_service_request(cfl_list_size(&profile->profiles)); + + if (internal_ctx->pub->export_service_request == NULL) { + return CPROF_ENCODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + index = 0; + cfl_list_foreach(iterator, + &profile->profiles) { + resource_profiles = cfl_list_entry( + iterator, + struct cprof_resource_profiles, _head); + + result = pack_cprof_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(internal_ctx->pub->export_service_request); + + return result; + } + + index++; + } + + return CPROF_ENCODE_OPENTELEMETRY_SUCCESS; +} + +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; + + 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( + struct cprof_opentelemetry_encoding_context *context) +{ + cfl_sds_t result_buffer; + size_t result_size; + + result_size = opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__get_packed_size( + context->export_service_request); + + result_buffer = cfl_sds_create_size(result_size); + + if(result_buffer != NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__pack( + context->export_service_request, + (uint8_t *) result_buffer); + + cfl_sds_set_len(result_buffer, result_size); + } + + return result_buffer; +} + +int cprof_encode_opentelemetry_create(cfl_sds_t *result_buffer, + struct cprof *profile) +{ + int result; + struct cprof_opentelemetry_encoding_context context; + + *result_buffer = NULL; + + result = pack_context(&context, profile); + + if (result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + *result_buffer = render_opentelemetry_context_to_sds(&context); + + if (*result_buffer == NULL) { + result = CPROF_ENCODE_OPENTELEMETRY_INTERNAL_ENCODER_ERROR; + } + + destroy_export_profiles_service_request(context.export_service_request); + } + + return result; +} + +void cprof_encode_opentelemetry_destroy(cfl_sds_t instance) +{ + if (instance != NULL) { + cfl_sds_destroy(instance); + } +} diff --git a/lib/cprofiles/cprof_encode_text.c b/lib/cprofiles/cprof_encode_text.c new file mode 100644 index 00000000000..93871fc0b36 --- /dev/null +++ b/lib/cprofiles/cprof_encode_text.c @@ -0,0 +1,3037 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +static int increment_indentation_level( + struct cprof_text_encoding_context *context); +static int decrement_indentation_level( + struct cprof_text_encoding_context *context); + +static int encode_bytes( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + uint8_t *value, + size_t length, + int hex_encode); + +static int encode_string( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + char *value); + +static int encode_double( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + double value); + +static int encode_uint64_t( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + uint64_t value); + +static int encode_int64_t( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + int64_t value); + +static int encode_bool( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + bool value); + +static int encode_string_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + char **data_list, + size_t data_length); + +static int encode_uint64_t_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + uint64_t *data_list, + size_t data_length); + +static int encode_int64_t_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + int64_t *data_list, + size_t data_length); + +static int encode_cfl_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cfl_array *value); + +static int encode_cfl_kvlist( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cfl_kvlist *data_list); + +static int encode_cfl_variant( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + struct cfl_variant *value); + + + +static int encode_aggregation_temporality( + struct cprof_text_encoding_context *context, + enum aggregation_temporality instance); + +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( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_sample *instance); + +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( + struct cprof_text_encoding_context *context, + struct cprof_attribute_unit *instance); + + +static int encode_cprof_link( + struct cprof_text_encoding_context *context, + struct cprof_link *instance); + + +static int encode_cprof_profile( + struct cprof_text_encoding_context *context, + struct cprof_profile *instance); + + +static int encode_cprof_resource_profiles( + struct cprof_text_encoding_context *context, + struct cprof_resource_profiles *instance); + + +static int encode_cprof_instrumentation_scope( + struct cprof_text_encoding_context *context, + struct cprof_instrumentation_scope *instance); + + +static int encode_cprof_resource( + struct cprof_text_encoding_context *context, + struct cprof_resource *instance); + + +static int encode_cprof_scope_profiles( + struct cprof_text_encoding_context *context, + struct cprof_scope_profiles *instance); + + +static int increment_indentation_level( + struct cprof_text_encoding_context *context) { + if (context->indentation_level >= 255) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + if (context->indentation_buffer[0] == '\0' && + context->indentation_buffer[255] == '\0') { + memset(context->indentation_buffer, + context->indentation_character, + 255); + } + + context->indentation_buffer[context->indentation_level] = context->indentation_character; + + context->indentation_level += context->indentation_level_size; + + context->indentation_buffer[context->indentation_level] = '\0'; + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int decrement_indentation_level( + struct cprof_text_encoding_context *context) { + if (context->indentation_level <= 0) { + return CPROF_ENCODE_TEXT_SUCCESS; + } + + context->indentation_buffer[context->indentation_level] = context->indentation_character; + + context->indentation_level -= context->indentation_level_size; + + context->indentation_buffer[context->indentation_level] = '\0'; + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_bytes( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + uint8_t *value, + size_t length, + int hex_encode) +{ + char *local_indentation; + cfl_sds_t result; + size_t index; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + if (!hex_encode) { + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%.*s" "%s", + local_indentation, + prefix, + length, + value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + } + else { + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" , + local_indentation, + prefix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + for (index = 0 ; index < length ; index++) { + result = cfl_sds_printf(&context->output_buffer, + "%02X", + value[index]); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + } + + result = cfl_sds_printf(&context->output_buffer, + "%s", + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_string( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + char *value) +{ + char *local_indentation; + cfl_sds_t result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%s" "%s", + local_indentation, + prefix, + value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + 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, + char *prefix, + char *suffix, + double value) +{ + char *local_indentation; + cfl_sds_t result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%0.4f" "%s", + local_indentation, + prefix, + value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_uint64_t( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + uint64_t value) +{ + char *local_indentation; + cfl_sds_t result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%"PRIu64 "%s", + local_indentation, + prefix, + value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_int64_t( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + int64_t value) +{ + char *local_indentation; + cfl_sds_t result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%"PRId64 "%s", + local_indentation, + prefix, + value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + 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]; +} + +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) +{ + 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 *) ""; + } + + 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, + label, + value); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + if (resolved != NULL) { + result = cfl_sds_cat(context->output_buffer, + " → \"", + strlen(" → \"")); + 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, + char *prefix, + char *suffix, + bool value) +{ + char *local_indentation; + char *local_value; + cfl_sds_t result; + + if (indent) { + local_indentation = (char *) context->indentation_buffer; + } + else { + local_indentation = (char *) ""; + } + + if (value) { + local_value = (char *) "True"; + } + else { + local_value = (char *) "False"; + } + + result = cfl_sds_printf(&context->output_buffer, + "%s" "%s" "%s" "%s", + local_indentation, + prefix, + local_value, + suffix); + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_string_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + char **data_list, + size_t data_length) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + + 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++) { + if (index < data_length - 1) { + result = encode_string(context, + CFL_FALSE, + "\"", + "\", ", + data_list[index]); + } + else { + result = encode_string(context, + CFL_FALSE, + "\"", + "\"", + data_list[index]); + } + + 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_uint64_t_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + uint64_t *data_list, + size_t data_length) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + + 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++) { + if (index < data_length - 1) { + result = encode_uint64_t(context, + CFL_FALSE, + "", + ", ", + data_list[index]); + } + else { + result = encode_uint64_t(context, + CFL_FALSE, + "", + "", + data_list[index]); + } + + 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_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, + char *prefix, + char *separator, + char *suffix, + int64_t *data_list, + size_t data_length) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + + 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++) { + if (index < data_length - 1) { + result = encode_int64_t(context, + CFL_FALSE, + "", + ", ", + data_list[index]); + } + else { + result = encode_int64_t(context, + CFL_FALSE, + "", + "", + data_list[index]); + } + + 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_cfl_array( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cfl_array *value) +{ + char *local_indentation; + cfl_sds_t sds_result; + int result; + size_t index; + + 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 < value->entry_count ; index++) { + if (index < value->entry_count - 1) { + result = encode_cfl_variant(context, + CFL_FALSE, + "", + ", ", + value->entries[index]); + } + else { + result = encode_cfl_variant(context, + CFL_FALSE, + "", + "", + value->entries[index]); + } + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + sds_result = cfl_sds_printf(&context->output_buffer, + "%s", + suffix); + + if (sds_result != NULL) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_cfl_kvlist( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *separator, + char *suffix, + struct cfl_kvlist *data_list) +{ + char *local_indentation; + 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; + + 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; + } + + last_entry = cfl_list_entry_last(&data_list->list, + struct cfl_kvpair, + _head); + + cfl_list_foreach(iterator, + &data_list->list) { + entry = cfl_list_entry(iterator, + struct cfl_kvpair, _head); + + result = encode_string(context, + CFL_FALSE, + "\"", + "\": ", + entry->key); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + 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) { + return result; + } + + if (entry != last_entry) { + 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_cfl_variant( + struct cprof_text_encoding_context *context, + int indent, + char *prefix, + char *suffix, + struct cfl_variant *value) +{ + int result; + + switch (value->type) { + case CFL_VARIANT_BOOL: + result = encode_bool(context, + indent, + prefix, + suffix, + value->data.as_bool); + break; + + case CFL_VARIANT_INT: + result = encode_int64_t(context, + indent, + prefix, + suffix, + value->data.as_int64); + break; + + case CFL_VARIANT_UINT: + result = encode_uint64_t(context, + indent, + prefix, + suffix, + value->data.as_uint64); + break; + + case CFL_VARIANT_DOUBLE: + result = encode_double(context, + indent, + prefix, + suffix, + value->data.as_double); + break; + + case CFL_VARIANT_NULL: + result = encode_string(context, + indent, + prefix, + suffix, + "NULL"); + break; + + case CFL_VARIANT_REFERENCE: + result = encode_string(context, + indent, + prefix, + suffix, + "Reference"); + break; + + case CFL_VARIANT_STRING: + result = encode_string(context, + indent, + prefix, + suffix, + value->data.as_string); + break; + + case CFL_VARIANT_BYTES: + result = encode_bytes(context, + indent, + prefix, + suffix, + (uint8_t *) value->data.as_bytes, + cfl_sds_len(value->data.as_bytes), + CFL_TRUE); + break; + + case CFL_VARIANT_ARRAY: + result = encode_cfl_array(context, + indent, + prefix, + ", ", + suffix, + value->data.as_array); + break; + + case CFL_VARIANT_KVLIST: + result = encode_cfl_kvlist(context, + indent, + prefix, + ", ", + suffix, + value->data.as_kvlist); + break; + + default: + result = CPROF_ENCODE_TEXT_INVALID_ARGUMENT_ERROR; + } + + return result; +} + + + + + + + + + +static int encode_aggregation_temporality( + struct cprof_text_encoding_context *context, + enum aggregation_temporality instance) { + cfl_sds_t result; + + switch (instance) { + case CPROF_AGGREGATION_TEMPORALITY_UNSPECIFIED: + result = cfl_sds_printf(&context->output_buffer, + "%s%s\n", + context->indentation_buffer, + "UNSPECIFIED"); + break; + + case CPROF_AGGREGATION_TEMPORALITY_DELTA: + result = cfl_sds_printf(&context->output_buffer, + "%s%s\n", + context->indentation_buffer, + "DELTA"); + break; + + case CPROF_AGGREGATION_TEMPORALITY_CUMULATIVE: + result = cfl_sds_printf(&context->output_buffer, + "%s%s\n", + context->indentation_buffer, + "CUMULATIVE"); + break; + + default: + result = cfl_sds_printf(&context->output_buffer, + "%s" "UNRECOGNIZED VALUE : %d\n", + context->indentation_buffer, + (int) instance); + } + + if (result == NULL) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_cprof_value_type( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_value_type *instance) +{ + int result; + + result = encode_int64_string_ref(context, + CFL_TRUE, + "Type : ", + instance->type, + profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_string_ref(context, + CFL_TRUE, + "Unit : ", + instance->unit, + profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return encode_aggregation_temporality( + context, + instance->aggregation_temporality); +} + + + +static int encode_cprof_sample( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_sample *instance) { + int result; + + 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; + } + + if (instance->location_index_count == 0 && + (instance->locations_start_index != 0 || + instance->locations_length != 0)) { + result = encode_int64_t(context, + CFL_TRUE, + "Locations start index : ", + "\n", + instance->locations_start_index); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Locations length : ", + "\n", + instance->locations_length); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = encode_int64_t_array(context, + CFL_TRUE, + "Values : [ ", + ", ", + "]\n", + instance->values, + instance->value_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Link (link_table index) : ", + "\n", + instance->link); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t_array(context, + CFL_TRUE, + "Timestamps : [ ", + ", ", + "]\n", + instance->timestamps_unix_nano, + instance->timestamps_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + + + + + + +static int encode_cprof_mapping( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_mapping *instance) +{ + int result; + + result = encode_uint64_t(context, + CFL_TRUE, + "Id : ", + "\n", + instance->id); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Memory start : ", + "\n", + instance->memory_start); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Memory limit : ", + "\n", + instance->memory_limit); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "File offset : ", + "\n", + instance->file_offset); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + + result = encode_int64_string_ref(context, + CFL_TRUE, + "Filename : ", + instance->filename, + profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_bool(context, + CFL_TRUE, + "Has functions : ", + "\n", + instance->has_functions); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_bool(context, + CFL_TRUE, + "Has filenames : ", + "\n", + instance->has_filenames); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_bool(context, + CFL_TRUE, + "Has line numbers : ", + "\n", + instance->has_line_numbers); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_bool(context, + CFL_TRUE, + "Has inline frames : ", + "\n", + instance->has_inline_frames); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + + +static int encode_cprof_line( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_line *instance) +{ + struct cprof_function *function; + const char *resolved; + int result; + + 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; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Line : ", + "\n", + instance->line); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Column : ", + "\n", + instance->column); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + +static int encode_cprof_location( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_location *instance) +{ + struct cfl_list *iterator; + struct cprof_mapping *mapping; + const char *resolved; + int result; + struct cprof_line *line; + + result = encode_uint64_t(context, + CFL_TRUE, + "Id : ", + "\n", + instance->id); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + 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; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Address : ", + "\n", + instance->address); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (!cfl_list_is_empty(&instance->lines)) { + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Lines :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + cfl_list_foreach(iterator, + &instance->lines) { + line = cfl_list_entry(iterator, + struct cprof_line, _head); + + result = encode_cprof_line(context, profile, line); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = encode_attribute_index_array(context, + CFL_TRUE, + "Attributes : [ ", + ", ", + "]\n", + profile, + instance->attributes, + instance->attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + +static int encode_cprof_function( + struct cprof_text_encoding_context *context, + struct cprof_profile *profile, + struct cprof_function *instance) +{ + int result; + + result = encode_uint64_t(context, + CFL_TRUE, + "Id : ", + "\n", + instance->id); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_string_ref(context, + CFL_TRUE, + "Name : ", + instance->name, + profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + 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_string_ref(context, + CFL_TRUE, + "Filename : ", + instance->filename, + profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Start line : ", + "\n", + instance->start_line); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + +static int encode_cprof_attribute_unit( + struct cprof_text_encoding_context *context, + struct cprof_attribute_unit *instance) { + int result; + + result = encode_int64_t(context, + CFL_TRUE, + "Attribute key : ", + "\n", + instance->attribute_key); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Unit : ", + "\n", + instance->unit); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_cprof_link( + struct cprof_text_encoding_context *context, + struct cprof_link *instance) +{ + int result; + + result = encode_bytes(context, + CFL_TRUE, + "Trace id : ", + "\n", + instance->trace_id, + sizeof(instance->trace_id), + CFL_TRUE); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_bytes(context, + CFL_TRUE, + "Span id : ", + "\n", + instance->span_id, + sizeof(instance->span_id), + CFL_TRUE); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + +static int encode_cprof_profile( + struct cprof_text_encoding_context *context, + struct cprof_profile *instance) { + struct cprof_attribute_unit *attribute_unit; + struct cprof_value_type *sample_type; + struct cfl_list *iterator; + struct cprof_location *location; + struct cprof_function *function; + struct cprof_mapping *mapping; + struct cprof_sample *sample; + int result; + struct cprof_link *link; + + result = encode_bytes(context, + CFL_TRUE, + "Profile id : ", + "\n", + instance->profile_id, + sizeof(instance->profile_id), + CFL_TRUE); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Start time unix nano: ", + "\n", + instance->start_time_unix_nano); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "End time unix nano: ", + "\n", + instance->end_time_unix_nano); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cfl_kvlist(context, + CFL_TRUE, + "Attributes: {", + ", ", + " }\n", + instance->attributes); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Dropped attributes: ", + "\n", + (uint64_t) instance->dropped_attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (!cfl_list_is_empty(&instance->sample_type)) { + result = encode_section_header_with_count(context, + "Sample types", + cfl_list_size(&instance->sample_type)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + cfl_list_foreach(iterator, + &instance->sample_type) { + sample_type = cfl_list_entry( + iterator, + struct cprof_value_type, _head); + + result = encode_cprof_value_type(context, instance, sample_type); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + if (!cfl_list_is_empty(&instance->samples)) { + result = encode_section_header_with_count(context, + "Samples", + cfl_list_size(&instance->samples)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + { + size_t sample_index = 0; + cfl_list_foreach(iterator, + &instance->samples) { + sample = cfl_list_entry( + iterator, + struct cprof_sample, _head); + + result = encode_item_header(context, "Sample", sample_index); + + 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, instance, sample); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + sample_index++; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + if (!cfl_list_is_empty(&instance->mappings)) { + result = encode_section_header_with_count(context, + "Mappings", + cfl_list_size(&instance->mappings)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + { + size_t mapping_index = 0; + cfl_list_foreach(iterator, + &instance->mappings) { + mapping = cfl_list_entry( + iterator, + struct cprof_mapping, _head); + + result = encode_item_header(context, "Mapping", mapping_index); + + 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++; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + if (!cfl_list_is_empty(&instance->locations)) { + result = encode_section_header_with_count(context, + "Locations", + cfl_list_size(&instance->locations)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + { + size_t location_index = 0; + cfl_list_foreach(iterator, + &instance->locations) { + location = cfl_list_entry( + iterator, + struct cprof_location, _head); + + result = encode_item_header(context, "Location", location_index); + + 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++; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + 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 (!cfl_list_is_empty(&instance->functions)) { + result = encode_section_header_with_count(context, + "Functions", + cfl_list_size(&instance->functions)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + { + size_t function_index = 0; + cfl_list_foreach(iterator, + &instance->functions) { + function = cfl_list_entry( + iterator, + struct cprof_function, _head); + + result = encode_item_header(context, "Function", function_index); + + 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++; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + 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; + } + + 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; + } + + result = increment_indentation_level(context); + + 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); + + 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; + } + } + } + + if (!cfl_list_is_empty(&instance->link_table)) { + result = encode_section_header_with_count(context, + "Links", + cfl_list_size(&instance->link_table)); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + { + size_t link_index = 0; + cfl_list_foreach(iterator, + &instance->link_table) { + link = cfl_list_entry( + iterator, + struct cprof_link, _head); + + result = encode_item_header(context, "Link", link_index); + + 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++; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + 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; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_string_array( + context, + CFL_TRUE, + "[ ", + ", ", + " ]\n", + (char **) instance->string_table, + instance->string_table_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + 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 : ", + "\n", + instance->drop_frames); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Keep frames : ", + "\n", + instance->keep_frames); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Time nanos : ", + "\n", + instance->time_nanos); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Duration nanos : ", + "\n", + instance->duration_nanos); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Period type :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_value_type(context, instance, &instance->period_type); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Period : ", + "\n", + instance->period); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t_array(context, + CFL_TRUE, + "Comments : [ ", + ", ", + "]\n", + instance->comments, + instance->comments_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_int64_t(context, + CFL_TRUE, + "Default sample type : ", + "\n", + instance->default_sample_type); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +static int encode_cprof_resource_profiles( + struct cprof_text_encoding_context *context, + struct cprof_resource_profiles *instance) { + int result; + struct cfl_list *iterator; + struct cprof_scope_profiles *scope_profile; + + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Resource :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_resource(context, instance->resource); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (!cfl_list_is_empty(&instance->scope_profiles)) { + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Scope profiles :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + cfl_list_foreach(iterator, + &instance->scope_profiles) { + scope_profile = cfl_list_entry( + iterator, + struct cprof_scope_profiles, _head); + + result = encode_cprof_scope_profiles(context, scope_profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = encode_string(context, + CFL_TRUE, + "Schema URL :", + "\n", + instance->schema_url); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + +static int encode_cprof_instrumentation_scope( + struct cprof_text_encoding_context *context, + struct cprof_instrumentation_scope *instance) { + int result; + + result = encode_string(context, + CFL_TRUE, + "Name : ", + "\n", + instance->name); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + + result = encode_string(context, + CFL_TRUE, + "Version : ", + "\n", + instance->version); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cfl_kvlist(context, + CFL_TRUE, + "Attributes: {", + ", ", + " }\n", + instance->attributes); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Dropped attribute count : ", + "\n", + (uint64_t) instance->dropped_attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + +static int encode_cprof_resource( + struct cprof_text_encoding_context *context, + struct cprof_resource *instance) { + int result; + + result = encode_cfl_kvlist(context, + CFL_TRUE, + "Attributes: {", + ", ", + " }\n", + instance->attributes); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_uint64_t(context, + CFL_TRUE, + "Dropped attribute count : ", + "\n", + (uint64_t) instance->dropped_attributes_count); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + + +static int encode_cprof_scope_profiles( + struct cprof_text_encoding_context *context, + struct cprof_scope_profiles *instance) { + int result; + struct cfl_list *iterator; + struct cprof_profile *profile; + + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Instrumentation scope :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = encode_cprof_instrumentation_scope(context, instance->scope); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + if (!cfl_list_is_empty(&instance->profiles)) { + result = encode_string(context, + CFL_TRUE, + "", + "\n", + "Profiles :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + result = increment_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + cfl_list_foreach(iterator, + &instance->profiles) { + profile = cfl_list_entry( + iterator, + struct cprof_profile, _head); + + result = encode_cprof_profile(context, profile); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = decrement_indentation_level(context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + } + + result = encode_string(context, + CFL_TRUE, + "Schema URL :", + "\n", + instance->schema_url); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } + + + return CPROF_ENCODE_TEXT_SUCCESS; +} + + + + + + + + + + + + + + + + + + + + +void print_profile(struct cprof_profile *profile) +{ + int i; + int sample_index = 0; + uint64_t location_idx; + char *tmp; + struct cfl_list *head; + struct cfl_list *type_head; + struct cprof_sample *sample; + struct cprof_value_type *sample_type; + + printf("\n"); + printf("--- profile debug\n"); + printf("Profile Duration: %" PRId64 " nanoseconds\n\n", profile->duration_nanos); + printf("Samples:\n"); + + cfl_list_foreach(head, &profile->samples) { + sample = cfl_list_entry(head, struct cprof_sample, _head); + + printf(" Sample #%d:\n", ++sample_index); + + printf(" Locations:\n"); + for (i = 0; i < sample->location_index_count; ++i) { + location_idx = sample->location_index[i]; + tmp = profile->string_table[location_idx]; + if (tmp[0] == '\0') { + printf(" [Empty String: No Function Name]\n"); + } + else { + printf(" Function: %s\n", tmp); + } + } + + printf(" Values:\n"); + size_t value_index = 0; + cfl_list_foreach(type_head, &profile->sample_type) { + sample_type = cfl_list_entry(type_head, struct cprof_value_type, _head); + if (value_index < sample->value_count) { + printf(" %s: %" PRId64 " %s\n", + profile->string_table[sample_type->type], + sample->values[value_index], + profile->string_table[sample_type->unit]); + } + value_index++; + } + + if (sample->timestamps_count > 0) { + printf(" Timestamps:\n"); + for (i = 0; i < sample->timestamps_count; ++i) { + printf(" Timestamp %d: %" PRIu64 " ns\n", i, sample->timestamps_unix_nano[i]); + } + } + else { + printf(" [No Timestamps]\n"); + } + + printf("\n"); // Add space between samples for readability + } + printf("String Table:\n"); + for (i = 0; i < profile->string_table_count; i++) { + printf(" %d: '%s'\n", i, profile->string_table[i]); + } + printf("\n"); +} + + + + +int cprof_encode_text_create(cfl_sds_t *result_buffer, + struct cprof *profile, + int render_mode) +{ + int result; + struct cprof_text_encoding_context context; + struct cfl_list *iterator; + struct cprof_resource_profiles *resource_profiles; + + 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) { + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + context.indentation_buffer = cfl_sds_create_size(256); + + if (context.indentation_buffer == NULL) { + cfl_sds_destroy(context.output_buffer); + + return CPROF_ENCODE_TEXT_ALLOCATION_ERROR; + } + + memset(context.indentation_buffer, + 0, + cfl_sds_alloc(context.indentation_buffer)); + + context.indentation_level_size = 4; + context.indentation_character = ' '; + context.render_mode = render_mode; + + + if (!cfl_list_is_empty(&profile->profiles)) { + result = encode_string(&context, + CFL_TRUE, + "", + "\n", + "Profiles :"); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + cfl_sds_destroy(context.indentation_buffer); + cfl_sds_destroy(context.output_buffer); + + return result; + } + + result = increment_indentation_level(&context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + cfl_sds_destroy(context.indentation_buffer); + cfl_sds_destroy(context.output_buffer); + + return result; + } + + cfl_list_foreach(iterator, + &profile->profiles) { + resource_profiles = cfl_list_entry( + iterator, + struct cprof_resource_profiles, _head); + + result = encode_cprof_resource_profiles(&context, resource_profiles); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + cfl_sds_destroy(context.indentation_buffer); + cfl_sds_destroy(context.output_buffer); + + return result; + } + } + + result = decrement_indentation_level(&context); + + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + cfl_sds_destroy(context.indentation_buffer); + cfl_sds_destroy(context.output_buffer); + + return result; + } + } + + cfl_sds_destroy(context.indentation_buffer); + + *result_buffer = context.output_buffer; + + return CPROF_ENCODE_TEXT_SUCCESS; +} + +void cprof_encode_text_destroy(cfl_sds_t instance) +{ + if (instance != NULL) { + cfl_sds_destroy(instance); + } +} diff --git a/lib/cprofiles/cprof_function.c b/lib/cprofiles/cprof_function.c new file mode 100644 index 00000000000..e586ed29911 --- /dev/null +++ b/lib/cprofiles/cprof_function.c @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_function *cprof_function_create(struct cprof_profile *profile) +{ + struct cprof_function *instance; + + instance = calloc(1, sizeof(struct cprof_function)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_add(&instance->_head, &profile->functions); + + return instance; +} + +void cprof_function_destroy(struct cprof_function *instance) +{ + if (instance != NULL) { + free(instance); + } +} diff --git a/lib/cprofiles/cprof_instrumentation_scope.c b/lib/cprofiles/cprof_instrumentation_scope.c new file mode 100644 index 00000000000..cd81d0d870c --- /dev/null +++ b/lib/cprofiles/cprof_instrumentation_scope.c @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_instrumentation_scope *cprof_instrumentation_scope_create( + char *name, + char *version, + struct cfl_kvlist *attributes, + uint32_t dropped_attributes_count) { + struct cprof_instrumentation_scope *instance; + + instance = calloc(1, sizeof(struct cprof_instrumentation_scope)); + + if (instance == NULL) { + return NULL; + } + + if (name != NULL) { + instance->name = cfl_sds_create(name); + + if (instance->name == NULL) { + cprof_instrumentation_scope_destroy(instance); + + return NULL; + } + } + + if (version != NULL) { + instance->version = cfl_sds_create(version); + + if (instance->version == NULL) { + cprof_instrumentation_scope_destroy(instance); + + return NULL; + } + } + + if (attributes != NULL) { + instance->attributes = attributes; + } + else { + instance->attributes = cfl_kvlist_create(); + + if (instance->attributes == NULL) { + cprof_instrumentation_scope_destroy(instance); + + return NULL; + } + } + + instance->dropped_attributes_count = dropped_attributes_count; + + return instance; +} + +void cprof_instrumentation_scope_destroy( + struct cprof_instrumentation_scope *instance) +{ + if (instance != NULL) { + if (instance->name != NULL) { + cfl_sds_destroy(instance->name); + } + + if (instance->version != NULL) { + cfl_sds_destroy(instance->version); + } + + if (instance->attributes != NULL) { + cfl_kvlist_destroy(instance->attributes); + } + + free(instance); + } +} diff --git a/lib/cprofiles/cprof_line.c b/lib/cprofiles/cprof_line.c new file mode 100644 index 00000000000..5ee4aa64f20 --- /dev/null +++ b/lib/cprofiles/cprof_line.c @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_line *cprof_line_create(struct cprof_location *location) +{ + struct cprof_line *instance; + + instance = calloc(1, sizeof(struct cprof_line)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_add(&instance->_head, &location->lines); + + return instance; +} + +void cprof_line_destroy(struct cprof_line *instance) +{ + if (instance != NULL) { + free(instance); + } +} diff --git a/lib/cprofiles/cprof_link.c b/lib/cprofiles/cprof_link.c new file mode 100644 index 00000000000..fd904b85a6f --- /dev/null +++ b/lib/cprofiles/cprof_link.c @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_link *cprof_link_create(struct cprof_profile *profile) +{ + struct cprof_link *instance; + + instance = calloc(1, sizeof(struct cprof_link)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_add(&instance->_head, &profile->link_table); + + return instance; +} + +void cprof_link_destroy(struct cprof_link *instance) +{ + if (instance != NULL) { + free(instance); + } +} \ No newline at end of file diff --git a/lib/cprofiles/cprof_location.c b/lib/cprofiles/cprof_location.c new file mode 100644 index 00000000000..4aaecdd74b4 --- /dev/null +++ b/lib/cprofiles/cprof_location.c @@ -0,0 +1,101 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_location *cprof_location_create(struct cprof_profile *profile) +{ + struct cprof_location *instance; + + instance = calloc(1, sizeof(struct cprof_location)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_init(&instance->lines); + + cfl_list_add(&instance->_head, &profile->locations); + + return instance; +} + +int cprof_location_add_attribute(struct cprof_location *location, uint64_t attribute) +{ + size_t new_size; + size_t alloc_slots = 32; + uint64_t *reallocated_attributes; + + if (location->attributes == NULL) { + location->attributes = calloc(alloc_slots, sizeof(uint64_t)); + + if (location->attributes == NULL) { + return -1; + } + + location->attributes_count = 0; + location->attributes_size = alloc_slots; + } + + if (location->attributes_count >= location->attributes_size) { + new_size = location->attributes_size + alloc_slots; + reallocated_attributes = realloc(location->attributes, new_size * sizeof(uint64_t)); + + if (reallocated_attributes == NULL) { + return -1; + } + + location->attributes = reallocated_attributes; + location->attributes_size = new_size; + } + + location->attributes[location->attributes_count] = attribute; + location->attributes_count++; + + return 0; +} + +void cprof_location_destroy(struct cprof_location *instance) +{ + struct cprof_line *line; + struct cfl_list *iterator; + struct cfl_list *iterator_backup; + + if (instance != NULL) { + if (instance->attributes != NULL) { + free(instance->attributes); + + instance->attributes = NULL; + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->lines) { + line = cfl_list_entry(iterator, + struct cprof_line, _head); + + cfl_list_del(&line->_head); + + cprof_line_destroy(line); + } + + free(instance); + } +} diff --git a/lib/cprofiles/cprof_mapping.c b/lib/cprofiles/cprof_mapping.c new file mode 100644 index 00000000000..936ea326af7 --- /dev/null +++ b/lib/cprofiles/cprof_mapping.c @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_mapping *cprof_mapping_create(struct cprof_profile *profile) +{ + struct cprof_mapping *instance; + + instance = calloc(1, sizeof(struct cprof_mapping)); + + if (instance == NULL) { + return NULL; + } + + cfl_list_add(&instance->_head, &profile->mappings); + + return instance; +} + +void cprof_mapping_destroy(struct cprof_mapping *instance) +{ + if (instance != NULL) { + if (instance->attributes != NULL) { + free(instance->attributes); + + instance->attributes = NULL; + } + + free(instance); + } +} + + +int cprof_mapping_add_attribute(struct cprof_mapping *mapping, uint64_t attribute) +{ + size_t new_size; + size_t alloc_slots = 32; + uint64_t *reallocated_attributes; + + if (mapping->attributes == NULL) { + mapping->attributes = calloc(alloc_slots, sizeof(uint64_t)); + + if (mapping->attributes == NULL) { + return -1; + } + + mapping->attributes_count = 0; + mapping->attributes_size = alloc_slots; + } + + if (mapping->attributes_count >= mapping->attributes_size) { + new_size = mapping->attributes_size + alloc_slots; + reallocated_attributes = realloc(mapping->attributes, new_size * sizeof(uint64_t)); + + if (reallocated_attributes == NULL) { + return -1; + } + + mapping->attributes = reallocated_attributes; + mapping->attributes_size = new_size; + } + + mapping->attributes[mapping->attributes_count] = attribute; + mapping->attributes_count++; + + return 0; +} \ No newline at end of file diff --git a/lib/cprofiles/cprof_mpack_utils.c b/lib/cprofiles/cprof_mpack_utils.c new file mode 100644 index 00000000000..4b40fcc4ddf --- /dev/null +++ b/lib/cprofiles/cprof_mpack_utils.c @@ -0,0 +1,504 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ======== + * Copyright 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +int cprof_mpack_consume_string_or_nil_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer) +{ + int result; + + if (output_buffer == NULL) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (cprof_mpack_peek_type(reader) == mpack_type_str) { + result = cprof_mpack_consume_string_tag(reader, output_buffer); + } + else if (cprof_mpack_peek_type(reader) == mpack_type_nil) { + result = cprof_mpack_consume_nil_tag(reader); + + if (result == CPROF_MPACK_SUCCESS) { + *output_buffer = NULL; + } + } + else { + result = CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + return result; +} + +int cprof_mpack_consume_binary_or_nil_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer) +{ + int result; + + if (output_buffer == NULL) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (cprof_mpack_peek_type(reader) == mpack_type_bin) { + result = cprof_mpack_consume_binary_tag(reader, output_buffer); + } + else if (cprof_mpack_peek_type(reader) == mpack_type_nil) { + result = cprof_mpack_consume_nil_tag(reader); + + if (result == CPROF_MPACK_SUCCESS) { + *output_buffer = NULL; + } + } + else { + result = CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + return result; +} + +int cprof_mpack_consume_nil_tag(mpack_reader_t *reader) +{ + mpack_tag_t tag; + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_nil != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_consume_double_tag(mpack_reader_t *reader, double *output_buffer) +{ + mpack_tag_t tag; + + if (NULL == output_buffer) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_double != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + *output_buffer = mpack_tag_double_value(&tag); + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_consume_uint_tag(mpack_reader_t *reader, uint64_t *output_buffer) +{ + mpack_tag_t tag; + + if (NULL == output_buffer) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_int == mpack_tag_type(&tag)) { + *output_buffer = (uint64_t) mpack_tag_int_value(&tag); + } + else if (mpack_type_uint == mpack_tag_type(&tag)) { + *output_buffer = (uint64_t) mpack_tag_uint_value(&tag); + } + else { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_consume_uint32_tag(mpack_reader_t *reader, uint32_t *output_buffer) +{ + int result; + uint64_t value; + + result = cprof_mpack_consume_uint_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + *output_buffer = (uint32_t) value; + } + + return result; +} + +int cprof_mpack_consume_uint64_tag(mpack_reader_t *reader, uint64_t *output_buffer) +{ + return cprof_mpack_consume_uint_tag(reader, output_buffer); +} + +int cprof_mpack_consume_int_tag(mpack_reader_t *reader, int64_t *output_buffer) +{ + mpack_tag_t tag; + + if (NULL == output_buffer) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_int == mpack_tag_type(&tag)) { + *output_buffer = (int64_t) mpack_tag_int_value(&tag); + } + else if (mpack_type_uint == mpack_tag_type(&tag)) { + *output_buffer = (int64_t) mpack_tag_uint_value(&tag); + } + else { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_consume_int32_tag(mpack_reader_t *reader, int32_t *output_buffer) +{ + int result; + int64_t value; + + result = cprof_mpack_consume_int_tag(reader, &value); + + if (result == CPROF_MPACK_SUCCESS) { + *output_buffer = (int32_t) value; + } + + return result; +} + +int cprof_mpack_consume_int64_tag(mpack_reader_t *reader, int64_t *output_buffer) +{ + return cprof_mpack_consume_int_tag(reader, output_buffer); +} + +int cprof_mpack_consume_string_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer) +{ + uint32_t string_length; + mpack_tag_t tag; + + if (NULL == output_buffer) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_str != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + string_length = mpack_tag_str_length(&tag); + + /* This validation only applies to cmetrics and its use cases, we know + * for a fact that our label names and values are not supposed to be really + * long so a huge value here probably means that the data stream got corrupted. + */ + + if (CPROF_MPACK_MAX_STRING_LENGTH < string_length) { + return CPROF_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + + *output_buffer = cfl_sds_create_size(string_length + 1); + + if (NULL == *output_buffer) { + return CPROF_MPACK_ALLOCATION_ERROR; + } + + cfl_sds_set_len(*output_buffer, string_length); + + mpack_read_cstr(reader, *output_buffer, string_length + 1, string_length); + + if (mpack_ok != mpack_reader_error(reader)) { + cfl_sds_destroy(*output_buffer); + + *output_buffer = NULL; + + return CPROF_MPACK_ENGINE_ERROR; + } + + mpack_done_str(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + cfl_sds_destroy(*output_buffer); + + *output_buffer = NULL; + + return CPROF_MPACK_ENGINE_ERROR; + } + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_consume_binary_tag(mpack_reader_t *reader, cfl_sds_t *output_buffer) +{ + uint32_t string_length; + mpack_tag_t tag; + + if (NULL == output_buffer) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + if (NULL == reader) { + return CPROF_MPACK_INVALID_ARGUMENT_ERROR; + } + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_bin != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + string_length = mpack_tag_bin_length(&tag); + + if (CPROF_MPACK_MAX_STRING_LENGTH < string_length) { + return CPROF_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + + *output_buffer = cfl_sds_create_size(string_length); + + if (NULL == *output_buffer) { + return CPROF_MPACK_ALLOCATION_ERROR; + } + + cfl_sds_set_len(*output_buffer, string_length); + + mpack_read_bytes(reader, *output_buffer, string_length); + + if (mpack_ok != mpack_reader_error(reader)) { + cfl_sds_destroy(*output_buffer); + + *output_buffer = NULL; + + return CPROF_MPACK_ENGINE_ERROR; + } + + mpack_done_bin(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + cfl_sds_destroy(*output_buffer); + + *output_buffer = NULL; + + return CPROF_MPACK_ENGINE_ERROR; + } + + return CPROF_MPACK_SUCCESS; +} + +int cprof_mpack_unpack_map(mpack_reader_t *reader, + struct cprof_mpack_map_entry_callback_t *callback_list, + void *context) +{ + struct cprof_mpack_map_entry_callback_t *callback_entry; + uint32_t entry_index; + uint32_t entry_count; + cfl_sds_t key_name; + int result; + mpack_tag_t tag; + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_map != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + entry_count = mpack_tag_map_count(&tag); + + /* This validation only applies to cmetrics and its use cases, we know + * how our schema looks and how many entries the different fields have and none + * of those exceed the number we set CPROF_MPACK_MAX_MAP_ENTRY_COUNT to which is 10. + * Making these sanity checks optional or configurable in runtime might be worth + * the itme and complexity cost but that's something I don't know at the moment. + */ + + if (CPROF_MPACK_MAX_MAP_ENTRY_COUNT < entry_count) { + return CPROF_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + + result = 0; + + for (entry_index = 0 ; 0 == result && entry_index < entry_count ; entry_index++) { + result = cprof_mpack_consume_string_tag(reader, &key_name); + + if (CPROF_MPACK_SUCCESS == result) { + callback_entry = callback_list; + result = CPROF_MPACK_UNEXPECTED_KEY_ERROR; + + while (CPROF_MPACK_UNEXPECTED_KEY_ERROR == result && + NULL != callback_entry->identifier) { + + if (0 == strcmp(callback_entry->identifier, key_name)) { + result = callback_entry->handler(reader, entry_index, context); + } + + callback_entry++; + } + + cfl_sds_destroy(key_name); + } + } + + if (CPROF_MPACK_SUCCESS == result) { + mpack_done_map(reader); + + if (mpack_ok != mpack_reader_error(reader)) + { + return CPROF_MPACK_PENDING_MAP_ENTRIES; + } + } + + return result; +} + +int cprof_mpack_unpack_array(mpack_reader_t *reader, + cprof_mpack_unpacker_entry_callback_fn_t entry_processor_callback, + void *context) +{ + uint32_t entry_index; + uint32_t entry_count; + mpack_tag_t tag; + int result; + + tag = mpack_read_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) + { + return CPROF_MPACK_ENGINE_ERROR; + } + + if (mpack_type_array != mpack_tag_type(&tag)) { + return CPROF_MPACK_UNEXPECTED_DATA_TYPE_ERROR; + } + + entry_count = mpack_tag_array_count(&tag); + + /* This validation only applies to cmetrics and its use cases, we know + * that in our schema we have the following arrays : + * label text dictionary (strings) + * dimension labels (indexes) + * metric values + * dimension values + * + * IMO none of these arrays should be huge so I think using 65535 as a limit + * gives us more than enough wiggle space (in reality I don't expect any of these + * arrays to hold more than 128 values but I could be wrong as that probably depends + * on the flush interval) + */ + + if (CPROF_MPACK_MAX_ARRAY_ENTRY_COUNT < entry_count) { + return CPROF_MPACK_CORRUPT_INPUT_DATA_ERROR; + } + + result = CPROF_MPACK_SUCCESS; + + for (entry_index = 0 ; + CPROF_MPACK_SUCCESS == result && entry_index < entry_count ; + entry_index++) { + result = entry_processor_callback(reader, entry_index, context); + } + + if (CPROF_MPACK_SUCCESS == result) { + mpack_done_array(reader); + + if (mpack_ok != mpack_reader_error(reader)) + { + return CPROF_MPACK_PENDING_ARRAY_ENTRIES; + } + } + + return result; +} + +int cprof_mpack_peek_array_length(mpack_reader_t *reader) +{ + mpack_tag_t tag; + + tag = mpack_peek_tag(reader); + + if (mpack_ok != mpack_reader_error(reader)) + { + return 0; + } + + if (mpack_type_array != mpack_tag_type(&tag)) { + return 0; + } + + return mpack_tag_array_count(&tag); +} + +mpack_type_t cprof_mpack_peek_type(mpack_reader_t *reader) +{ + mpack_tag_t tag; + + tag = mpack_peek_tag(reader); + + if (mpack_reader_error(reader) != mpack_ok) { + return mpack_type_missing; + } + + return mpack_tag_type(&tag); +} diff --git a/lib/cprofiles/cprof_opentelemetry_variant_helpers.c b/lib/cprofiles/cprof_opentelemetry_variant_helpers.c new file mode 100644 index 00000000000..5d37f482a02 --- /dev/null +++ b/lib/cprofiles/cprof_opentelemetry_variant_helpers.c @@ -0,0 +1,353 @@ +#include +#include + +static int clone_variant(struct cfl_variant **result_instance, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len); + +static int clone_array(struct cfl_array *target, + Opentelemetry__Proto__Common__V1__ArrayValue *source, + char **string_table, + size_t string_table_len); +static int clone_array_entry(struct cfl_array *target, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len); +static int clone_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValueList *source, + char **string_table, + size_t string_table_len); +static int clone_kvlist_entry(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValue *source, + char **string_table, + size_t string_table_len); +static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValue **source, + size_t source_length, + char **string_table, + size_t string_table_len); + +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 int clone_variant(struct cfl_variant **result_instance, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len) +{ + struct cfl_kvlist *new_child_kvlist; + struct cfl_array *new_child_array; + const char *resolved_string; + int result; + + *result_instance = NULL; + + if (source == NULL) { + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_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_STRING_VALUE_STRINDEX) { + if (string_table == NULL || + source->string_value_strindex < 0 || + (size_t) source->string_value_strindex >= string_table_len || + string_table[source->string_value_strindex] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + resolved_string = string_table[source->string_value_strindex]; + *result_instance = cfl_variant_create_from_string((char *) resolved_string); + } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) { + *result_instance = cfl_variant_create_from_bool(source->bool_value); + } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE) { + *result_instance = cfl_variant_create_from_int64(source->int_value); + } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE) { + *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) { + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + new_child_kvlist = cfl_kvlist_create(); + if (new_child_kvlist == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + *result_instance = cfl_variant_create_from_kvlist(new_child_kvlist); + + if (*result_instance == NULL) { + cfl_kvlist_destroy(new_child_kvlist); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = clone_kvlist(new_child_kvlist, + source->kvlist_value, + string_table, + string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_variant_destroy(*result_instance); + *result_instance = NULL; + + return result; + } + } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE) { + if (source->array_value == NULL) { + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + new_child_array = cfl_array_create(source->array_value->n_values); + + if (new_child_array == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + *result_instance = cfl_variant_create_from_array(new_child_array); + if (*result_instance == NULL) { + cfl_array_destroy(new_child_array); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + result = clone_array(new_child_array, + source->array_value, + string_table, + string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_variant_destroy(*result_instance); + *result_instance = NULL; + + return result; + } + } + else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) { + *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 != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; +} + +static int clone_array(struct cfl_array *target, + Opentelemetry__Proto__Common__V1__ArrayValue *source, + char **string_table, + size_t string_table_len) +{ + int result; + size_t index; + + result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; + + for (index = 0 ; + result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && + index < source->n_values ; + index++) { + result = clone_array_entry(target, + source->values[index], + string_table, + string_table_len); + } + + return result; +} + +static int clone_array_entry(struct cfl_array *target, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len) +{ + struct cfl_variant *new_child_instance; + int result; + + result = clone_variant(&new_child_instance, source, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + + result = cfl_array_append(target, new_child_instance); + if (result) { + cfl_variant_destroy(new_child_instance); + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; +} + +static int clone_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValueList *source, + char **string_table, + size_t string_table_len) +{ + int result; + size_t index; + + result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; + + for (index = 0 ; + result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && + index < source->n_values ; + index++) { + result = clone_kvlist_entry(target, + source->values[index], + string_table, + string_table_len); + } + + return result; +} + +static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValue **source, + size_t source_length, + char **string_table, + size_t string_table_len) +{ + int result; + size_t index; + + result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; + + for (index = 0 ; + result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && + index < source_length ; + index++) { + result = clone_kvlist_entry(target, + source[index], + string_table, + string_table_len); + } + + return result; +} + +static int clone_kvlist_entry(struct cfl_kvlist *target, + Opentelemetry__Proto__Common__V1__KeyValue *source, + char **string_table, + size_t string_table_len) +{ + struct cfl_variant *new_child_instance; + int result; + char *key; + const char *resolved_key; + + if (source == NULL) { + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; + } + + if (source->key != NULL && source->key[0] != '\0') { + resolved_key = source->key; + } + else if (string_table != NULL && + source->key_strindex >= 0 && + (size_t) source->key_strindex < string_table_len && + string_table[source->key_strindex] != NULL) { + resolved_key = string_table[source->key_strindex]; + } + else if (source->key != NULL) { + resolved_key = source->key; + } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + key = (char *) resolved_key; + + result = clone_variant(&new_child_instance, source->value, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + + result = cfl_kvlist_insert(target, key, new_child_instance); + + if (result) { + cfl_variant_destroy(new_child_instance); + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + 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; + int result; + const char *key; + struct cfl_variant *val; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry; + + for (index = 0; index < source_length; index++) { + entry = source[index]; + + key = ""; + if (entry == NULL) { + key = ""; + } + else 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]; + } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + /* + * 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(""); + if (val == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + } + else { + result = clone_variant(&val, entry->value, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } + } + + 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/lib/cprofiles/cprof_profile.c b/lib/cprofiles/cprof_profile.c new file mode 100644 index 00000000000..ec71422cc43 --- /dev/null +++ b/lib/cprofiles/cprof_profile.c @@ -0,0 +1,312 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_profile *cprof_profile_create() +{ + struct cprof_profile *profile; + + profile = calloc(1, sizeof(struct cprof_profile)); + + if (profile == NULL) { + return NULL; + } + + cfl_list_init(&profile->sample_type); + cfl_list_init(&profile->samples); + cfl_list_init(&profile->mappings); + cfl_list_init(&profile->locations); + cfl_list_init(&profile->functions); + cfl_list_init(&profile->attribute_units); + cfl_list_init(&profile->link_table); + + profile->attributes = cfl_kvlist_create(); + + if (profile->attributes == NULL) { + cprof_profile_destroy(profile); + + return NULL; + } + + profile->attribute_table = cfl_kvlist_create(); + + if (profile->attribute_table == NULL) { + cprof_profile_destroy(profile); + + return NULL; + } + + return profile; +} + +int cprof_profile_add_location_index(struct cprof_profile *profile, int64_t index) +{ + size_t new_size; + size_t alloc_slots = 32; + int64_t *reallocated_array; + + if (profile->location_indices == NULL) { + profile->location_indices = calloc(alloc_slots, sizeof(int64_t)); + + if (profile->location_indices == NULL) { + return -1; + } + + profile->location_indices_count = 0; + profile->location_indices_size = alloc_slots; + } + + if (profile->location_indices_count >= profile->location_indices_size) { + new_size = profile->location_indices_size + alloc_slots; + reallocated_array = realloc(profile->location_indices, new_size * sizeof(int64_t)); + + if (reallocated_array == NULL) { + return -1; + } + + profile->location_indices = reallocated_array; + profile->location_indices_size = new_size; + } + + profile->location_indices[profile->location_indices_count] = index; + profile->location_indices_count++; + + return 0; +} + + +void cprof_profile_destroy(struct cprof_profile *instance) +{ + struct cfl_list *iterator_backup; + struct cprof_attribute_unit *attribute_unit; + struct cprof_value_type *value_type; + struct cprof_location *location; + struct cprof_function *function; + struct cfl_list *iterator; + struct cprof_mapping *mapping; + struct cprof_sample *sample; + size_t index; + struct cprof_link *link; + + if (instance->attributes != NULL) { + cfl_kvlist_destroy(instance->attributes); + } + + if (instance->original_payload_format != NULL) { + cfl_sds_destroy(instance->original_payload_format); + } + + if (instance->original_payload != NULL) { + cfl_sds_destroy(instance->original_payload); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->sample_type) { + value_type = cfl_list_entry(iterator, + struct cprof_value_type, + _head); + + cfl_list_del(&value_type->_head); + + cprof_sample_type_destroy(value_type); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->samples) { + sample = cfl_list_entry(iterator, + struct cprof_sample, + _head); + + cfl_list_del(&sample->_head); + + cprof_sample_destroy(sample); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->mappings) { + mapping = cfl_list_entry(iterator, + struct cprof_mapping, + _head); + + cfl_list_del(&mapping->_head); + + cprof_mapping_destroy(mapping); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->locations) { + location = cfl_list_entry(iterator, + struct cprof_location, + _head); + + cfl_list_del(&location->_head); + + cprof_location_destroy(location); + } + + if (instance->location_indices != NULL) { + free(instance->location_indices); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->functions) { + function = cfl_list_entry(iterator, + struct cprof_function, + _head); + + cfl_list_del(&function->_head); + + cprof_function_destroy(function); + } + + if (instance->attribute_table != NULL) { + cfl_kvlist_destroy(instance->attribute_table); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->attribute_units) { + attribute_unit = cfl_list_entry(iterator, + struct cprof_attribute_unit, + _head); + + cfl_list_del(&attribute_unit->_head); + + cprof_attribute_unit_destroy(attribute_unit); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->link_table) { + link = cfl_list_entry(iterator, + struct cprof_link, + _head); + + cfl_list_del(&link->_head); + + cprof_link_destroy(link); + } + + if (instance->string_table != NULL) { + for (index = 0 ; index < instance->string_table_count ; index++) { + cfl_sds_destroy(instance->string_table[index]); + } + + free(instance->string_table); + } + + if (instance->comments != NULL) { + free(instance->comments); + } + + free(instance); +} + +size_t cprof_profile_string_add(struct cprof_profile *profile, char *str, int str_len) +{ + int alloc_size = 64; + size_t id; + size_t new_size; + cfl_sds_t *new_table; + + if (!str) { + return SIZE_MAX; + } + + if (str_len <= 0) { + str_len = strlen(str); + } + + if (!profile->string_table && str_len > 0) { + profile->string_table = malloc(alloc_size * sizeof(cfl_sds_t)); + if (!profile->string_table) { + return SIZE_MAX; + } + profile->string_table_size = alloc_size; + + /* string_table[0] must always be "" */ + profile->string_table[0] = cfl_sds_create_len("", 0); + if (!profile->string_table[0]) { + return -1; + } + profile->string_table_count = 1; + } + + /* 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; + new_table = realloc(profile->string_table, new_size * sizeof(cfl_sds_t)); + if (!new_table) { + return SIZE_MAX; + } + profile->string_table = new_table; + profile->string_table_size = new_size; + } + + id = profile->string_table_count; + profile->string_table[id] = cfl_sds_create_len(str, str_len); + if (!profile->string_table[id]) { + return SIZE_MAX; + } + profile->string_table_count++; + + return id; +} + +int cprof_profile_add_comment(struct cprof_profile *profile, int64_t comment) +{ + size_t new_size; + size_t alloc_slots = 32; + int64_t *reallocated_array; + + if (profile->comments == NULL) { + profile->comments = calloc(alloc_slots, sizeof(int64_t)); + + if (profile->comments == NULL) { + return -1; + } + + profile->comments_count = 0; + profile->comments_size = alloc_slots; + } + + if (profile->comments_count >= profile->comments_size) { + new_size = profile->comments_size + alloc_slots; + reallocated_array = realloc(profile->comments, new_size * sizeof(int64_t)); + + if (reallocated_array == NULL) { + return -1; + } + + profile->comments = reallocated_array; + profile->comments_size = new_size; + } + + profile->comments[profile->comments_count] = comment; + profile->comments_count++; + + return 0; +} diff --git a/lib/cprofiles/cprof_resource.c b/lib/cprofiles/cprof_resource.c new file mode 100644 index 00000000000..131c8fe7b48 --- /dev/null +++ b/lib/cprofiles/cprof_resource.c @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_resource *cprof_resource_create(struct cfl_kvlist *attributes) +{ + struct cprof_resource *resource; + + resource = calloc(1, sizeof(struct cprof_resource)); + + if (resource == NULL) { + return NULL; + } + + if (attributes == NULL) { + resource->attributes = cfl_kvlist_create(); + + if (resource->attributes == NULL) { + free(resource); + + return NULL; + } + } + else { + resource->attributes = attributes; + } + + return resource; +} + +void cprof_resource_destroy(struct cprof_resource *resource) +{ + if (resource->attributes != NULL) { + cfl_kvlist_destroy(resource->attributes); + } + + free(resource); +} + +int cprof_resource_profiles_add(struct cprof *context, + struct cprof_resource_profiles *resource_profiles) +{ + cfl_list_add(&resource_profiles->_head, &context->profiles); + + return 0; +} diff --git a/lib/cprofiles/cprof_resource_profiles.c b/lib/cprofiles/cprof_resource_profiles.c new file mode 100644 index 00000000000..0bad34adf08 --- /dev/null +++ b/lib/cprofiles/cprof_resource_profiles.c @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_resource_profiles *cprof_resource_profiles_create(char *schema_url) { + struct cprof_resource_profiles *instance; + cfl_sds_t schema_url_copy; + + instance = calloc(1, sizeof(struct cprof_resource_profiles)); + + if (instance != NULL) { + if (schema_url == NULL) { + free(instance); + + instance = NULL; + } + else { + schema_url_copy = cfl_sds_create(schema_url); + + if (schema_url_copy == NULL) { + free(instance); + + instance = NULL; + } + else { + instance->schema_url = schema_url_copy; + cfl_list_init(&instance->scope_profiles); + } + } + } + + return instance; +} + + +void cprof_resource_profiles_destroy(struct cprof_resource_profiles *instance) { + struct cfl_list *iterator; + struct cprof_scope_profiles *scope_profiles; + struct cfl_list *iterator_backup; + + if (instance != NULL) { + if (instance->schema_url != NULL) { + cfl_sds_destroy(instance->schema_url); + } + + if (instance->resource != NULL) { + cprof_resource_destroy(instance->resource); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->scope_profiles) { + scope_profiles = cfl_list_entry(iterator, + struct cprof_scope_profiles, _head); + + cfl_list_del(&scope_profiles->_head); + + cprof_scope_profiles_destroy(scope_profiles); + } + + free(instance); + } +} diff --git a/lib/cprofiles/cprof_sample.c b/lib/cprofiles/cprof_sample.c new file mode 100644 index 00000000000..b030d0aea5e --- /dev/null +++ b/lib/cprofiles/cprof_sample.c @@ -0,0 +1,281 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof_sample *cprof_sample_create(struct cprof_profile *profile) +{ + struct cprof_sample *sample; + + sample = calloc(1, sizeof(struct cprof_sample)); + if (!sample) { + return NULL; + } + + cfl_list_add(&sample->_head, &profile->samples); + return sample; +} + +int cprof_sample_add_location_index(struct cprof_sample *sample, uint64_t location_index) +{ + size_t new_size; + size_t alloc_slots = 32; + uint64_t *reallocated_location_index; + + if (sample->location_index == NULL) { + sample->location_index = calloc(1, alloc_slots * sizeof(uint64_t)); + + if (sample->location_index == NULL) { + return -1; + } + + sample->location_index_count = 0; + sample->location_index_size = alloc_slots; + } + + /* check if we have space in the location index by checking location_index_size */ + if (sample->location_index_count >= sample->location_index_size) { + new_size = sample->location_index_size + alloc_slots; + reallocated_location_index = realloc(sample->location_index, new_size * sizeof(uint64_t)); + + if (reallocated_location_index == NULL) { + return -1; + } + + sample->location_index = reallocated_location_index; + sample->location_index_size = new_size; + } + + /* add the location */ + sample->location_index[sample->location_index_count] = location_index; + sample->location_index_count++; + + return 0; +} + +int cprof_sample_add_value(struct cprof_sample *sample, int64_t value) +{ + size_t new_size; + size_t alloc_slots = 32; + int64_t *reallocated_values; + + if (sample->values == NULL) { + sample->values = calloc(alloc_slots, sizeof(int64_t)); + + if (sample->values == NULL) { + return -1; + } + + sample->value_count = 0; + sample->value_size = alloc_slots; + } + + if (sample->value_count >= sample->value_size) { + new_size = sample->value_size + alloc_slots; + reallocated_values = realloc(sample->values, new_size * sizeof(int64_t)); + + if (reallocated_values == NULL) { + return -1; + } + + sample->values = reallocated_values; + sample->value_size = new_size; + } + + sample->values[sample->value_count] = value; + sample->value_count++; + + return 0; +} + +int cprof_sample_add_attribute(struct cprof_sample *sample, uint64_t attribute) +{ + size_t new_size; + size_t alloc_slots = 32; + uint64_t *reallocated_attributes; + + if (sample->attributes == NULL) { + sample->attributes = calloc(alloc_slots, sizeof(uint64_t)); + + if (sample->attributes == NULL) { + return -1; + } + + sample->attributes_count = 0; + sample->attributes_size = alloc_slots; + } + + if (sample->attributes_count >= sample->attributes_size) { + new_size = sample->attributes_size + alloc_slots; + reallocated_attributes = realloc(sample->attributes, new_size * sizeof(uint64_t)); + + if (reallocated_attributes == NULL) { + return -1; + } + + sample->attributes = reallocated_attributes; + sample->attributes_size = new_size; + } + + sample->attributes[sample->attributes_count] = attribute; + sample->attributes_count++; + + return 0; +} + +int cprof_sample_add_timestamp(struct cprof_sample *sample, uint64_t timestamp) +{ + size_t new_size; + size_t alloc_slots = 32; + uint64_t *reallocated_timestamps; + + if (sample->timestamps_unix_nano == NULL) { + sample->timestamps_unix_nano = calloc(alloc_slots, sizeof(uint64_t)); + + if (sample->timestamps_unix_nano == NULL) { + return -1; + } + + sample->timestamps_count = 0; + sample->timestamps_size = alloc_slots; + } + + if (sample->timestamps_count >= sample->timestamps_size) { + new_size = sample->timestamps_size + alloc_slots; + reallocated_timestamps = realloc(sample->timestamps_unix_nano, new_size * sizeof(uint64_t)); + + if (reallocated_timestamps == NULL) { + return -1; + } + + sample->timestamps_unix_nano = reallocated_timestamps; + sample->timestamps_size = new_size; + } + + sample->timestamps_unix_nano[sample->timestamps_count] = timestamp; + sample->timestamps_count++; + + return 0; +} + +void cprof_sample_destroy(struct cprof_sample *sample) +{ + if (sample != NULL) { + if (sample->location_index) { + free(sample->location_index); + } + + if (sample->values != NULL) { + free(sample->values); + } + + if (sample->attributes != NULL) { + free(sample->attributes); + } + + if (sample->timestamps_unix_nano != NULL) { + free(sample->timestamps_unix_nano); + } + + free(sample); + return; + } +} + + +void cprof_sample_destroy_all(struct cprof_profile *profile) +{ + struct cfl_list *head; + struct cfl_list *tmp; + struct cprof_sample *sample; + + cfl_list_foreach_safe(head, tmp, &profile->samples) { + sample = cfl_list_entry(head, struct cprof_sample, _head); + cfl_list_del(&sample->_head); + cprof_sample_destroy(sample); + } +} + +struct cprof_value_type *cprof_sample_type_create(struct cprof_profile *profile, + int64_t type, int64_t unit, int aggregation_temporality) +{ + struct cprof_value_type *sample_type; + + sample_type = calloc(1, sizeof(struct cprof_value_type)); + if (!sample_type) { + return NULL; + } + + sample_type->type = type; + sample_type->unit = unit; + sample_type->aggregation_temporality = aggregation_temporality; + + cfl_list_add(&sample_type->_head, &profile->sample_type); + return sample_type; +} + +struct cprof_value_type *cprof_sample_type_str_create(struct cprof_profile *profile, char *type_str, char *unit_str, + int aggregation_temporality) +{ + int64_t type; + int64_t unit; + struct cprof_value_type *sample_type; + + if (!profile || !type_str || !unit_str) { + return NULL; + } + + type = cprof_profile_string_add(profile, type_str, -1); + if (type == SIZE_MAX) { + return NULL; + } + + unit = cprof_profile_string_add(profile, unit_str, -1); + if (unit == SIZE_MAX) { + return NULL; + } + + sample_type = cprof_sample_type_create(profile, type, unit, aggregation_temporality); + if (!sample_type) { + return NULL; + } + + return sample_type; +} + +void cprof_sample_type_destroy(struct cprof_value_type *sample_type) +{ + if (sample_type != NULL) { + free(sample_type); + } +} + +void cprof_sample_type_destroy_all(struct cprof_profile *profile) +{ + struct cfl_list *head; + struct cfl_list *tmp; + struct cprof_value_type *sample_type; + + cfl_list_foreach_safe(head, tmp, &profile->sample_type) { + sample_type = cfl_list_entry(head, struct cprof_value_type, _head); + cfl_list_del(&sample_type->_head); + cprof_sample_type_destroy(sample_type); + } +} diff --git a/lib/cprofiles/cprof_scope_profiles.c b/lib/cprofiles/cprof_scope_profiles.c new file mode 100644 index 00000000000..26ca5cd0ce7 --- /dev/null +++ b/lib/cprofiles/cprof_scope_profiles.c @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +/* Scope profiles */ +struct cprof_scope_profiles *cprof_scope_profiles_create( + struct cprof_resource_profiles *resource_profiles, + char *schema_url) { + struct cprof_scope_profiles *instance; + cfl_sds_t schema_url_copy; + + instance = calloc(1, sizeof(struct cprof_scope_profiles)); + + if (instance != NULL) { + if (schema_url == NULL) { + free(instance); + + instance = NULL; + } + else { + schema_url_copy = cfl_sds_create(schema_url); + + if (schema_url_copy == NULL) { + free(instance); + + instance = NULL; + } + else { + instance->schema_url = schema_url_copy; + cfl_list_init(&instance->profiles); + cfl_list_add(&instance->_head, &resource_profiles->scope_profiles); + } + } + } + + return instance; +} + + +void cprof_scope_profiles_destroy(struct cprof_scope_profiles *instance) { + struct cprof_profile *profile; + struct cfl_list *iterator; + struct cfl_list *iterator_backup; + + if (instance != NULL) { + if (instance->schema_url != NULL) { + cfl_sds_destroy(instance->schema_url); + } + + if (instance->scope != NULL) { + cprof_instrumentation_scope_destroy(instance->scope); + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &instance->profiles) { + profile = cfl_list_entry(iterator, + struct cprof_profile, _head); + + cfl_list_del(&profile->_head); + + cprof_profile_destroy(profile); + } + + free(instance); + } +} diff --git a/lib/cprofiles/cprofiles.c b/lib/cprofiles/cprofiles.c new file mode 100644 index 00000000000..b80365329e4 --- /dev/null +++ b/lib/cprofiles/cprofiles.c @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CProfiles + * ========= + * Copyright (C) 2024 The CProfiles Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +struct cprof *cprof_create() +{ + struct cprof *cprof; + + cprof = calloc(1, sizeof(struct cprof)); + if (!cprof) { + return NULL; + } + + cfl_list_init(&cprof->profiles); + + return cprof; +} + +void cprof_destroy(struct cprof *cprof) +{ + struct cprof_resource_profiles *resource_profile; + struct cfl_list *iterator_backup; + struct cfl_list *iterator; + + if (!cprof) { + return; + } + + cfl_list_foreach_safe(iterator, + iterator_backup, + &cprof->profiles) { + resource_profile = cfl_list_entry(iterator, + struct cprof_resource_profiles, + _head); + + cfl_list_del(&resource_profile->_head); + + cprof_resource_profiles_destroy(resource_profile); + } + + free(cprof); +} + +char *cprof_version() +{ + return CPROF_VERSION_STR; +} diff --git a/lib/cprofiles/include/CMakeLists.txt b/lib/cprofiles/include/CMakeLists.txt index 6f9ee5855d7..43f0b15b4ff 100644 --- a/lib/cprofiles/include/CMakeLists.txt +++ b/lib/cprofiles/include/CMakeLists.txt @@ -1,5 +1,12 @@ file(GLOB cprofilesHeaders "cprofiles/*.h") install(FILES ${cprofilesHeaders} - DESTINATION ${CMT_INSTALL_INCLUDEDIR}/cprofiles + DESTINATION ${CPROF_INSTALL_INCLUDEDIR}/cprofiles + COMPONENT headers + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/cprofiles/cprof_info.h" + "${CMAKE_CURRENT_BINARY_DIR}/cprofiles/cprof_version.h" + DESTINATION ${CPROF_INSTALL_INCLUDEDIR}/cprofiles COMPONENT headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/lib/cprofiles/src/cprof_decode_opentelemetry.c b/lib/cprofiles/src/cprof_decode_opentelemetry.c index c2407eeaf56..afd80fce5b0 100644 --- a/lib/cprofiles/src/cprof_decode_opentelemetry.c +++ b/lib/cprofiles/src/cprof_decode_opentelemetry.c @@ -32,81 +32,90 @@ static inline size_t minimum_size(size_t first_size, size_t second_size) return second_size; } -static struct cprof_resource * - decode_resource( - Opentelemetry__Proto__Resource__V1__Resource *input_resource) +static int decode_resource(struct cprof_resource **output_resource, + Opentelemetry__Proto__Resource__V1__Resource *input_resource, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { - struct cprof_resource *output_resource; - struct cfl_kvlist *attributes; - int result; + struct cfl_kvlist *attributes; + int result; + + *output_resource = NULL; if (input_resource == NULL) { - output_resource = cprof_resource_create(NULL); - if (output_resource != NULL) { - output_resource->dropped_attributes_count = 0; + *output_resource = cprof_resource_create(NULL); + if (*output_resource != NULL) { + (*output_resource)->dropped_attributes_count = 0; + + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } - return output_resource; + + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } attributes = cfl_kvlist_create(); if (attributes == NULL) { - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } result = convert_kvarray_to_kvlist(attributes, input_resource->attributes, - input_resource->n_attributes); + input_resource->n_attributes, + dictionary != NULL ? dictionary->string_table : NULL, + dictionary != NULL ? dictionary->n_string_table : 0); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { cfl_kvlist_destroy(attributes); - - attributes = NULL; + return result; } - output_resource = cprof_resource_create(attributes); + *output_resource = cprof_resource_create(attributes); - if (output_resource == NULL) { - if (attributes != NULL) { - cfl_kvlist_destroy(attributes); - } + if (*output_resource == NULL) { + cfl_kvlist_destroy(attributes); - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - output_resource->dropped_attributes_count = input_resource->dropped_attributes_count; + (*output_resource)->dropped_attributes_count = input_resource->dropped_attributes_count; - return output_resource; + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } -static struct cprof_instrumentation_scope *decode_instrumentation_scope( - Opentelemetry__Proto__Common__V1__InstrumentationScope *input_instrumentation_scope) +static int decode_instrumentation_scope( + struct cprof_instrumentation_scope **instrumentation_scope, + Opentelemetry__Proto__Common__V1__InstrumentationScope *input_instrumentation_scope, + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary *dictionary) { - struct cprof_instrumentation_scope *instrumentation_scope; - int result; + int result; + + *instrumentation_scope = NULL; - instrumentation_scope = cprof_instrumentation_scope_create( + *instrumentation_scope = cprof_instrumentation_scope_create( input_instrumentation_scope->name, input_instrumentation_scope->version, NULL, input_instrumentation_scope->dropped_attributes_count); - if (instrumentation_scope == NULL) { - return NULL; + if (*instrumentation_scope == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = convert_kvarray_to_kvlist(instrumentation_scope->attributes, + result = convert_kvarray_to_kvlist((*instrumentation_scope)->attributes, input_instrumentation_scope->attributes, - input_instrumentation_scope->n_attributes); + input_instrumentation_scope->n_attributes, + dictionary != NULL ? dictionary->string_table : NULL, + dictionary != NULL ? dictionary->n_string_table : 0); if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { - cprof_instrumentation_scope_destroy(instrumentation_scope); + cprof_instrumentation_scope_destroy(*instrumentation_scope); + *instrumentation_scope = NULL; - return NULL; + return result; } - return instrumentation_scope; + return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } @@ -120,17 +129,25 @@ static int decode_profile_sample_entry(struct cprof_sample *sample, 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) { + if (input_sample->stack_index >= 0) { + if (dictionary == NULL || + dictionary->stack_table == NULL || + (size_t) input_sample->stack_index >= dictionary->n_stack_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + stack = dictionary->stack_table[input_sample->stack_index]; + if (stack == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + 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)) { + dictionary->location_table == NULL || + (size_t) location_index >= dictionary->n_location_table) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -151,6 +168,13 @@ static int decode_profile_sample_entry(struct cprof_sample *sample, } for (index = 0; index < input_sample->n_attribute_indices; index++) { + if (dictionary == NULL || + dictionary->attribute_table == NULL || + input_sample->attribute_indices[index] < 0 || + (size_t) input_sample->attribute_indices[index] >= dictionary->n_attribute_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + result = cprof_sample_add_attribute(sample, (uint64_t)input_sample->attribute_indices[index]); if (result != 0) { @@ -166,7 +190,18 @@ static int decode_profile_sample_entry(struct cprof_sample *sample, } } - sample->link = (uint64_t)(input_sample->link_index >= 0 ? input_sample->link_index : 0); + if (input_sample->link_index >= 0) { + if (dictionary == NULL || + dictionary->link_table == NULL || + (size_t) input_sample->link_index >= dictionary->n_link_table) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + sample->link = (uint64_t) input_sample->link_index; + } + else { + sample->link = 0; + } return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } @@ -206,9 +241,9 @@ static int decode_line_entry(struct cprof_line *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)) { + dictionary == NULL || + dictionary->function_table == NULL || + (size_t) input_line->function_index >= dictionary->n_function_table) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -228,9 +263,9 @@ static int decode_location_entry(struct cprof_location *location, 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)) { + dictionary == NULL || + dictionary->mapping_table == NULL || + (size_t) input_location->mapping_index >= dictionary->n_mapping_table) { return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } @@ -370,6 +405,10 @@ static int decode_profile_entry(struct cprof_profile *profile, /* Mappings */ if (dictionary->mapping_table != NULL) { for (index = 0; index < dictionary->n_mapping_table; index++) { + if (dictionary->mapping_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + mapping = cprof_mapping_create(profile); if (mapping == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -384,6 +423,10 @@ static int decode_profile_entry(struct cprof_profile *profile, /* Locations */ if (dictionary->location_table != NULL) { for (index = 0; index < dictionary->n_location_table; index++) { + if (dictionary->location_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + location = cprof_location_create(profile); if (location == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -400,6 +443,10 @@ static int decode_profile_entry(struct cprof_profile *profile, /* Functions */ if (dictionary->function_table != NULL) { for (index = 0; index < dictionary->n_function_table; index++) { + if (dictionary->function_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + function = cprof_function_create(profile); if (function == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -422,6 +469,10 @@ static int decode_profile_entry(struct cprof_profile *profile, return result; } for (index = 0; index < dictionary->n_attribute_table; index++) { + if (dictionary->attribute_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + attribute_unit = cprof_attribute_unit_create(profile); if (attribute_unit == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -437,6 +488,10 @@ static int decode_profile_entry(struct cprof_profile *profile, /* Link table */ if (dictionary->link_table != NULL) { for (index = 0; index < dictionary->n_link_table; index++) { + if (dictionary->link_table[index] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + link = cprof_link_create(profile); if (link == NULL) { return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; @@ -465,7 +520,6 @@ static int decode_profile_entry(struct cprof_profile *profile, return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; } - indexed_attribute_key = ""; indexed_attribute_key_index = indexed_attribute_entry->key_strindex; if (dictionary->string_table != NULL && @@ -474,11 +528,17 @@ static int decode_profile_entry(struct cprof_profile *profile, dictionary->string_table[indexed_attribute_key_index] != NULL) { indexed_attribute_key = dictionary->string_table[indexed_attribute_key_index]; } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } - indexed_attribute_value = clone_variant(indexed_attribute_entry->value); + result = clone_variant(&indexed_attribute_value, + indexed_attribute_entry->value, + dictionary->string_table, + dictionary->n_string_table); - if (indexed_attribute_value == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; } if (cfl_kvlist_insert(profile->attributes, @@ -606,14 +666,20 @@ static int decode_scope_profiles_entry(struct cprof_resource_profiles *resource_ } if (input_scope_profiles->scope != NULL) { - profiles->scope = decode_instrumentation_scope(input_scope_profiles->scope); + result = decode_instrumentation_scope(&profiles->scope, + input_scope_profiles->scope, + dictionary); } else { profiles->scope = cprof_instrumentation_scope_create(NULL, NULL, NULL, 0); + result = profiles->scope != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - if (profiles->scope == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cprof_scope_profiles_destroy(profiles); + return result; } if (input_scope_profiles->profiles != NULL && input_scope_profiles->n_profiles > 0) { @@ -645,12 +711,11 @@ static int decode_resource_profiles_entry(struct cprof *context, return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - profile->resource = decode_resource(resource_profile->resource); + result = decode_resource(&profile->resource, resource_profile->resource, dictionary); - if (profile->resource == NULL) { + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { cprof_resource_profiles_destroy(profile); - - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + return result; } result = CPROF_DECODE_OPENTELEMETRY_SUCCESS; @@ -740,6 +805,10 @@ int cprof_decode_opentelemetry_create(struct cprof **result_context, if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS) { *result_context = context; + + if (offset != NULL) { + *offset = in_size; + } } else if (context != NULL) { cprof_destroy(context); diff --git a/lib/cprofiles/src/cprof_encode_text.c b/lib/cprofiles/src/cprof_encode_text.c index 08203a5dbb8..93871fc0b36 100644 --- a/lib/cprofiles/src/cprof_encode_text.c +++ b/lib/cprofiles/src/cprof_encode_text.c @@ -1473,24 +1473,28 @@ static int encode_cprof_sample( return result; } - result = encode_int64_t(context, - CFL_TRUE, - "Locations start index : ", - "\n", - instance->locations_start_index); + if (instance->location_index_count == 0 && + (instance->locations_start_index != 0 || + instance->locations_length != 0)) { + result = encode_int64_t(context, + CFL_TRUE, + "Locations start index : ", + "\n", + instance->locations_start_index); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; - } + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } - result = encode_uint64_t(context, - CFL_TRUE, - "Locations length : ", - "\n", - instance->locations_length); + result = encode_uint64_t(context, + CFL_TRUE, + "Locations length : ", + "\n", + instance->locations_length); - if (result != CPROF_ENCODE_TEXT_SUCCESS) { - return result; + if (result != CPROF_ENCODE_TEXT_SUCCESS) { + return result; + } } result = encode_int64_t_array(context, diff --git a/lib/cprofiles/src/cprof_opentelemetry_variant_helpers.c b/lib/cprofiles/src/cprof_opentelemetry_variant_helpers.c index 27f399054da..5d37f482a02 100644 --- a/lib/cprofiles/src/cprof_opentelemetry_variant_helpers.c +++ b/lib/cprofiles/src/cprof_opentelemetry_variant_helpers.c @@ -1,19 +1,32 @@ #include #include -static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source); +static int clone_variant(struct cfl_variant **result_instance, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len); static int clone_array(struct cfl_array *target, - Opentelemetry__Proto__Common__V1__ArrayValue *source); + Opentelemetry__Proto__Common__V1__ArrayValue *source, + char **string_table, + size_t string_table_len); static int clone_array_entry(struct cfl_array *target, - Opentelemetry__Proto__Common__V1__AnyValue *source); + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len); static int clone_kvlist(struct cfl_kvlist *target, - Opentelemetry__Proto__Common__V1__KeyValueList *source); + Opentelemetry__Proto__Common__V1__KeyValueList *source, + char **string_table, + size_t string_table_len); static int clone_kvlist_entry(struct cfl_kvlist *target, - Opentelemetry__Proto__Common__V1__KeyValue *source); + Opentelemetry__Proto__Common__V1__KeyValue *source, + char **string_table, + size_t string_table_len); static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, Opentelemetry__Proto__Common__V1__KeyValue **source, - size_t source_length); + size_t source_length, + char **string_table, + size_t string_table_len); static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit **source, @@ -22,91 +35,131 @@ static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, size_t string_table_len); -static struct cfl_variant *clone_variant(Opentelemetry__Proto__Common__V1__AnyValue *source) +static int clone_variant(struct cfl_variant **result_instance, + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len) { struct cfl_kvlist *new_child_kvlist; struct cfl_array *new_child_array; - struct cfl_variant *result_instance = NULL; + const char *resolved_string; int result; + *result_instance = NULL; + if (source == NULL) { - return cfl_variant_create_from_string(""); + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE) { - result_instance = cfl_variant_create_from_string(source->string_value != NULL ? 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_STRING_VALUE_STRINDEX) { + if (string_table == NULL || + source->string_value_strindex < 0 || + (size_t) source->string_value_strindex >= string_table_len || + string_table[source->string_value_strindex] == NULL) { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } + + resolved_string = string_table[source->string_value_strindex]; + *result_instance = cfl_variant_create_from_string((char *) resolved_string); } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE) { - result_instance = cfl_variant_create_from_bool(source->bool_value); + *result_instance = cfl_variant_create_from_bool(source->bool_value); } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_INT_VALUE) { - result_instance = cfl_variant_create_from_int64(source->int_value); + *result_instance = cfl_variant_create_from_int64(source->int_value); } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE) { - result_instance = cfl_variant_create_from_double(source->double_value); + *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(""); + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } new_child_kvlist = cfl_kvlist_create(); if (new_child_kvlist == NULL) { - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result_instance = cfl_variant_create_from_kvlist(new_child_kvlist); + *result_instance = cfl_variant_create_from_kvlist(new_child_kvlist); - if (result_instance == NULL) { + if (*result_instance == NULL) { cfl_kvlist_destroy(new_child_kvlist); - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = clone_kvlist(new_child_kvlist, source->kvlist_value); - if (result) { - cfl_variant_destroy(result_instance); + result = clone_kvlist(new_child_kvlist, + source->kvlist_value, + string_table, + string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_variant_destroy(*result_instance); + *result_instance = NULL; - return NULL; + return result; } } 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(""); + *result_instance = cfl_variant_create_from_string(""); + + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } new_child_array = cfl_array_create(source->array_value->n_values); if (new_child_array == NULL) { - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result_instance = cfl_variant_create_from_array(new_child_array); - if (result_instance == NULL) { + *result_instance = cfl_variant_create_from_array(new_child_array); + if (*result_instance == NULL) { cfl_array_destroy(new_child_array); - return NULL; + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } - result = clone_array(new_child_array, source->array_value); - if (result) { - cfl_variant_destroy(result_instance); + result = clone_array(new_child_array, + source->array_value, + string_table, + string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + cfl_variant_destroy(*result_instance); + *result_instance = NULL; - return NULL; + return result; } } else if (source->value_case == OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE) { - result_instance = cfl_variant_create_from_bytes((char *) source->bytes_value.data, source->bytes_value.len, - CFL_FALSE); + *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(""); + *result_instance = cfl_variant_create_from_string(""); } - return result_instance; + return *result_instance != NULL ? + CPROF_DECODE_OPENTELEMETRY_SUCCESS : + CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; } static int clone_array(struct cfl_array *target, - Opentelemetry__Proto__Common__V1__ArrayValue *source) + Opentelemetry__Proto__Common__V1__ArrayValue *source, + char **string_table, + size_t string_table_len) { int result; size_t index; @@ -117,21 +170,26 @@ static int clone_array(struct cfl_array *target, result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && index < source->n_values ; index++) { - result = clone_array_entry(target, source->values[index]); + result = clone_array_entry(target, + source->values[index], + string_table, + string_table_len); } return result; } static int clone_array_entry(struct cfl_array *target, - Opentelemetry__Proto__Common__V1__AnyValue *source) + Opentelemetry__Proto__Common__V1__AnyValue *source, + char **string_table, + size_t string_table_len) { struct cfl_variant *new_child_instance; int result; - new_child_instance = clone_variant(source); - if (new_child_instance == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + result = clone_variant(&new_child_instance, source, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; } result = cfl_array_append(target, new_child_instance); @@ -144,7 +202,9 @@ static int clone_array_entry(struct cfl_array *target, } static int clone_kvlist(struct cfl_kvlist *target, - Opentelemetry__Proto__Common__V1__KeyValueList *source) + Opentelemetry__Proto__Common__V1__KeyValueList *source, + char **string_table, + size_t string_table_len) { int result; size_t index; @@ -155,7 +215,10 @@ static int clone_kvlist(struct cfl_kvlist *target, result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && index < source->n_values ; index++) { - result = clone_kvlist_entry(target, source->values[index]); + result = clone_kvlist_entry(target, + source->values[index], + string_table, + string_table_len); } return result; @@ -163,7 +226,9 @@ static int clone_kvlist(struct cfl_kvlist *target, static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, Opentelemetry__Proto__Common__V1__KeyValue **source, - size_t source_length) + size_t source_length, + char **string_table, + size_t string_table_len) { int result; size_t index; @@ -174,29 +239,50 @@ static int convert_kvarray_to_kvlist(struct cfl_kvlist *target, result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && index < source_length ; index++) { - result = clone_kvlist_entry(target, source[index]); + result = clone_kvlist_entry(target, + source[index], + string_table, + string_table_len); } return result; } static int clone_kvlist_entry(struct cfl_kvlist *target, - Opentelemetry__Proto__Common__V1__KeyValue *source) + Opentelemetry__Proto__Common__V1__KeyValue *source, + char **string_table, + size_t string_table_len) { struct cfl_variant *new_child_instance; int result; char *key; + const char *resolved_key; if (source == NULL) { return CPROF_DECODE_OPENTELEMETRY_SUCCESS; } - key = source->key != NULL ? source->key : ""; + if (source->key != NULL && source->key[0] != '\0') { + resolved_key = source->key; + } + else if (string_table != NULL && + source->key_strindex >= 0 && + (size_t) source->key_strindex < string_table_len && + string_table[source->key_strindex] != NULL) { + resolved_key = string_table[source->key_strindex]; + } + else if (source->key != NULL) { + resolved_key = source->key; + } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } - new_child_instance = clone_variant(source->value); + key = (char *) resolved_key; - if (new_child_instance == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + result = clone_variant(&new_child_instance, source->value, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; } result = cfl_kvlist_insert(target, key, new_child_instance); @@ -217,6 +303,7 @@ static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, size_t string_table_len) { size_t index; + int result; const char *key; struct cfl_variant *val; Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *entry; @@ -225,12 +312,17 @@ static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, entry = source[index]; key = ""; - if (entry != NULL && - string_table != NULL && entry->key_strindex >= 0 && - (size_t)entry->key_strindex < string_table_len && - string_table[entry->key_strindex] != NULL) { + if (entry == NULL) { + key = ""; + } + else 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]; } + else { + return CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR; + } /* * Preserve positional alignment with OTLP attribute table indexes. @@ -240,13 +332,15 @@ static int convert_keyvalueandunit_array_to_kvlist(struct cfl_kvlist *target, 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(""); + if (val == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } } else { - val = clone_variant(entry->value); - } - - if (val == NULL) { - return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + result = clone_variant(&val, entry->value, string_table, string_table_len); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS) { + return result; + } } if (cfl_kvlist_insert(target, (char *)key, val) != 0) { diff --git a/lib/cprofiles/tests/opentelemetry_transcoder.c b/lib/cprofiles/tests/opentelemetry_transcoder.c index e6861e79cf0..f458059efc6 100644 --- a/lib/cprofiles/tests/opentelemetry_transcoder.c +++ b/lib/cprofiles/tests/opentelemetry_transcoder.c @@ -468,6 +468,82 @@ static struct cprof *create_cprof_with_dictionary_tables(void) return cprof; } +static cfl_sds_t pack_export_service_request( + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request) +{ + cfl_sds_t packed; + size_t packed_size; + + packed_size = + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__get_packed_size( + request); + + packed = cfl_sds_create_size(packed_size); + if (packed == NULL) { + return NULL; + } + + cfl_sds_set_len( + packed, + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__pack( + request, (uint8_t *) packed)); + + return packed; +} + +static int decode_export_service_request( + struct cprof **decoded_context, + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request) +{ + cfl_sds_t packed; + size_t offset; + int result; + + packed = pack_export_service_request(request); + if (packed == NULL) { + return CPROF_DECODE_OPENTELEMETRY_ALLOCATION_ERROR; + } + + offset = 0; + result = cprof_decode_opentelemetry_create(decoded_context, + (unsigned char *) packed, + cfl_sds_len(packed), + &offset); + + cfl_sds_destroy(packed); + + return result; +} + +static Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest * +create_unpacked_dictionary_request(void) +{ + cfl_sds_t otlp_result; + struct cprof *context; + int result; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + + context = create_cprof_with_dictionary_tables(); + if (context == NULL) { + return NULL; + } + + result = cprof_encode_opentelemetry_create(&otlp_result, context); + cprof_destroy(context); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS || otlp_result == NULL) { + return NULL; + } + + request = opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__unpack( + NULL, + cfl_sds_len(otlp_result), + (const unsigned char *) otlp_result); + + cprof_encode_opentelemetry_destroy(otlp_result); + + return request; +} + /* Encode cprof with full dictionary (mappings, functions, locations, links) and check success. */ static void test_encoder_dictionary_tables() { @@ -619,6 +695,9 @@ static void verify_decoded_cprof_dictionary_tables(struct cprof *decoded) /* 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) { + size_t location_table_size; + size_t location_index; + sample_iter = profile->samples.next; sample = cfl_list_entry(sample_iter, struct cprof_sample, _head); TEST_CHECK(sample->value_count == 1); @@ -627,7 +706,23 @@ static void verify_decoded_cprof_dictionary_tables(struct cprof *decoded) } 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"); + location_table_size = cfl_list_size(&profile->locations); + location_index = sample->location_index[0]; + + TEST_CHECK(location_index < location_table_size && + "sample must reference a valid decoded location"); + if (location_index < location_table_size) { + loc_iter = profile->locations.next; + for (i = 0; i < location_index && loc_iter != &profile->locations; i++) { + loc_iter = loc_iter->next; + } + + if (loc_iter != &profile->locations) { + loc = cfl_list_entry(loc_iter, struct cprof_location, _head); + TEST_CHECK(loc->address == 0x1000ULL && + "sample must reference decoded location at 0x1000"); + } + } } /* 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"); @@ -746,11 +841,718 @@ static void test_decoder_dictionary_tables() cprof_encode_opentelemetry_destroy(otlp_result); } +static void test_decoder_dictionary_string_references() +{ + int result; + struct cprof *decoded_context; + struct cprof_resource_profiles *resource_profiles_context; + struct cprof_scope_profiles *scope_profiles_context; + struct cprof_profile *profile_context; + struct cfl_variant *resource_attribute_value; + struct cfl_variant *scope_attribute_value; + struct cfl_variant *attribute_value; + struct cfl_list *iterator; + + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest request = + OPENTELEMETRY__PROTO__COLLECTOR__PROFILES__V1DEVELOPMENT__EXPORT_PROFILES_SERVICE_REQUEST__INIT; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary dictionary = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILES_DICTIONARY__INIT; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles resource_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__RESOURCE_PROFILES__INIT; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles scope_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__SCOPE_PROFILES__INIT; + Opentelemetry__Proto__Resource__V1__Resource resource = + OPENTELEMETRY__PROTO__RESOURCE__V1__RESOURCE__INIT; + Opentelemetry__Proto__Common__V1__InstrumentationScope scope = + OPENTELEMETRY__PROTO__COMMON__V1__INSTRUMENTATION_SCOPE__INIT; + Opentelemetry__Proto__Profiles__V1development__Profile profile = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILE__INIT; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit attribute_entry = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__KEY_VALUE_AND_UNIT__INIT; + Opentelemetry__Proto__Common__V1__AnyValue attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__KeyValue resource_attribute = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue resource_attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__KeyValue scope_attribute = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue scope_attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + char *string_table_entries[] = { + "", + "attr.key", + "attr.value" + }; + Opentelemetry__Proto__Profiles__V1development__KeyValueAndUnit *attribute_table_entries[] = { + &attribute_entry + }; + int32_t profile_attribute_indices[] = { + 0 + }; + Opentelemetry__Proto__Profiles__V1development__Profile *profiles[] = { + &profile + }; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *scope_profiles_entries[] = { + &scope_profiles + }; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *resource_profiles_entries[] = { + &resource_profiles + }; + Opentelemetry__Proto__Common__V1__KeyValue *resource_attributes[] = { + &resource_attribute + }; + Opentelemetry__Proto__Common__V1__KeyValue *scope_attributes[] = { + &scope_attribute + }; + + attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + attribute_value_ref.string_value_strindex = 2; + + resource_attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + resource_attribute_value_ref.string_value_strindex = 2; + + scope_attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + scope_attribute_value_ref.string_value_strindex = 2; + + attribute_entry.key_strindex = 1; + attribute_entry.value = &attribute_value_ref; + + resource_attribute.key_strindex = 1; + resource_attribute.value = &resource_attribute_value_ref; + + scope_attribute.key_strindex = 1; + scope_attribute.value = &scope_attribute_value_ref; + + dictionary.string_table = string_table_entries; + dictionary.n_string_table = sizeof(string_table_entries) / sizeof(string_table_entries[0]); + dictionary.attribute_table = attribute_table_entries; + dictionary.n_attribute_table = sizeof(attribute_table_entries) / sizeof(attribute_table_entries[0]); + + resource.attributes = resource_attributes; + resource.n_attributes = sizeof(resource_attributes) / sizeof(resource_attributes[0]); + + scope.attributes = scope_attributes; + scope.n_attributes = sizeof(scope_attributes) / sizeof(scope_attributes[0]); + + profile.time_unix_nano = 1000; + profile.duration_nano = 100; + profile.attribute_indices = profile_attribute_indices; + profile.n_attribute_indices = sizeof(profile_attribute_indices) / sizeof(profile_attribute_indices[0]); + + scope_profiles.scope = &scope; + scope_profiles.profiles = profiles; + scope_profiles.n_profiles = sizeof(profiles) / sizeof(profiles[0]); + + resource_profiles.resource = &resource; + resource_profiles.scope_profiles = scope_profiles_entries; + resource_profiles.n_scope_profiles = sizeof(scope_profiles_entries) / sizeof(scope_profiles_entries[0]); + + request.dictionary = &dictionary; + request.resource_profiles = resource_profiles_entries; + request.n_resource_profiles = sizeof(resource_profiles_entries) / sizeof(resource_profiles_entries[0]); + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, &request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); + TEST_CHECK(decoded_context != NULL); + + if (result == CPROF_DECODE_OPENTELEMETRY_SUCCESS && decoded_context != NULL) { + TEST_CHECK(cfl_list_size(&decoded_context->profiles) == 1); + if (cfl_list_size(&decoded_context->profiles) != 1) { + cprof_decode_opentelemetry_destroy(decoded_context); + return; + } + + iterator = decoded_context->profiles.next; + resource_profiles_context = cfl_list_entry(iterator, struct cprof_resource_profiles, _head); + + resource_attribute_value = cfl_kvlist_fetch(resource_profiles_context->resource->attributes, "attr.key"); + TEST_CHECK(resource_attribute_value != NULL); + if (resource_attribute_value != NULL) { + TEST_CHECK(resource_attribute_value->type == CFL_VARIANT_STRING); + if (resource_attribute_value->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(resource_attribute_value->data.as_string, "attr.value") == 0); + } + } + + TEST_CHECK(cfl_list_size(&resource_profiles_context->scope_profiles) == 1); + if (cfl_list_size(&resource_profiles_context->scope_profiles) != 1) { + cprof_decode_opentelemetry_destroy(decoded_context); + return; + } + iterator = resource_profiles_context->scope_profiles.next; + scope_profiles_context = cfl_list_entry(iterator, struct cprof_scope_profiles, _head); + + scope_attribute_value = cfl_kvlist_fetch(scope_profiles_context->scope->attributes, "attr.key"); + TEST_CHECK(scope_attribute_value != NULL); + if (scope_attribute_value != NULL) { + TEST_CHECK(scope_attribute_value->type == CFL_VARIANT_STRING); + if (scope_attribute_value->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(scope_attribute_value->data.as_string, "attr.value") == 0); + } + } + + TEST_CHECK(cfl_list_size(&scope_profiles_context->profiles) == 1); + if (cfl_list_size(&scope_profiles_context->profiles) != 1) { + cprof_decode_opentelemetry_destroy(decoded_context); + return; + } + iterator = scope_profiles_context->profiles.next; + profile_context = cfl_list_entry(iterator, struct cprof_profile, _head); + + attribute_value = cfl_kvlist_fetch(profile_context->attributes, "attr.key"); + TEST_CHECK(attribute_value != NULL); + if (attribute_value != NULL) { + TEST_CHECK(attribute_value->type == CFL_VARIANT_STRING); + if (attribute_value->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(attribute_value->data.as_string, "attr.value") == 0); + } + } + + cprof_decode_opentelemetry_destroy(decoded_context); + } +} + +static void test_decoder_dictionary_nested_string_references() +{ + int result; + struct cprof *decoded_context; + struct cprof_resource_profiles *resource_profiles_context; + struct cprof_scope_profiles *scope_profiles_context; + struct cfl_variant *resource_attribute_value; + struct cfl_variant *scope_attribute_value; + struct cfl_variant *array_entry; + struct cfl_variant *nested_value; + struct cfl_list *iterator; + + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest request = + OPENTELEMETRY__PROTO__COLLECTOR__PROFILES__V1DEVELOPMENT__EXPORT_PROFILES_SERVICE_REQUEST__INIT; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary dictionary = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILES_DICTIONARY__INIT; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles resource_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__RESOURCE_PROFILES__INIT; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles scope_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__SCOPE_PROFILES__INIT; + Opentelemetry__Proto__Resource__V1__Resource resource = + OPENTELEMETRY__PROTO__RESOURCE__V1__RESOURCE__INIT; + Opentelemetry__Proto__Common__V1__InstrumentationScope scope = + OPENTELEMETRY__PROTO__COMMON__V1__INSTRUMENTATION_SCOPE__INIT; + Opentelemetry__Proto__Profiles__V1development__Profile profile = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILE__INIT; + Opentelemetry__Proto__Common__V1__KeyValue resource_attribute = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue resource_attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__KeyValueList resource_kvlist = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE_LIST__INIT; + Opentelemetry__Proto__Common__V1__KeyValue resource_nested_entry = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue resource_nested_value = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__KeyValue scope_attribute = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue scope_attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__ArrayValue scope_array = + OPENTELEMETRY__PROTO__COMMON__V1__ARRAY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue scope_array_string_entry = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue scope_array_kvlist_entry = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__KeyValueList scope_kvlist = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE_LIST__INIT; + Opentelemetry__Proto__Common__V1__KeyValue scope_nested_entry = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue scope_nested_value = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + char *string_table_entries[] = { + "", + "outer.key", + "array.value", + "inner.key", + "inner.value" + }; + Opentelemetry__Proto__Profiles__V1development__Profile *profiles[] = { + &profile + }; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *scope_profiles_entries[] = { + &scope_profiles + }; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *resource_profiles_entries[] = { + &resource_profiles + }; + Opentelemetry__Proto__Common__V1__KeyValue *resource_attributes[] = { + &resource_attribute + }; + Opentelemetry__Proto__Common__V1__KeyValue *scope_attributes[] = { + &scope_attribute + }; + Opentelemetry__Proto__Common__V1__KeyValue *resource_kvlist_entries[] = { + &resource_nested_entry + }; + Opentelemetry__Proto__Common__V1__KeyValue *scope_kvlist_entries[] = { + &scope_nested_entry + }; + Opentelemetry__Proto__Common__V1__AnyValue *scope_array_entries[] = { + &scope_array_string_entry, + &scope_array_kvlist_entry + }; + + resource_nested_value.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + resource_nested_value.string_value_strindex = 4; + resource_nested_entry.key_strindex = 3; + resource_nested_entry.value = &resource_nested_value; + resource_kvlist.values = resource_kvlist_entries; + resource_kvlist.n_values = sizeof(resource_kvlist_entries) / sizeof(resource_kvlist_entries[0]); + resource_attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE; + resource_attribute_value_ref.kvlist_value = &resource_kvlist; + resource_attribute.key_strindex = 1; + resource_attribute.value = &resource_attribute_value_ref; + + scope_array_string_entry.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + scope_array_string_entry.string_value_strindex = 2; + scope_nested_value.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + scope_nested_value.string_value_strindex = 4; + scope_nested_entry.key_strindex = 3; + scope_nested_entry.value = &scope_nested_value; + scope_kvlist.values = scope_kvlist_entries; + scope_kvlist.n_values = sizeof(scope_kvlist_entries) / sizeof(scope_kvlist_entries[0]); + scope_array_kvlist_entry.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE; + scope_array_kvlist_entry.kvlist_value = &scope_kvlist; + scope_array.values = scope_array_entries; + scope_array.n_values = sizeof(scope_array_entries) / sizeof(scope_array_entries[0]); + scope_attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE; + scope_attribute_value_ref.array_value = &scope_array; + scope_attribute.key_strindex = 1; + scope_attribute.value = &scope_attribute_value_ref; + + dictionary.string_table = string_table_entries; + dictionary.n_string_table = sizeof(string_table_entries) / sizeof(string_table_entries[0]); + + resource.attributes = resource_attributes; + resource.n_attributes = sizeof(resource_attributes) / sizeof(resource_attributes[0]); + + scope.attributes = scope_attributes; + scope.n_attributes = sizeof(scope_attributes) / sizeof(scope_attributes[0]); + + profile.time_unix_nano = 1000; + profile.duration_nano = 100; + + scope_profiles.scope = &scope; + scope_profiles.profiles = profiles; + scope_profiles.n_profiles = sizeof(profiles) / sizeof(profiles[0]); + + resource_profiles.resource = &resource; + resource_profiles.scope_profiles = scope_profiles_entries; + resource_profiles.n_scope_profiles = sizeof(scope_profiles_entries) / sizeof(scope_profiles_entries[0]); + + request.dictionary = &dictionary; + request.resource_profiles = resource_profiles_entries; + request.n_resource_profiles = sizeof(resource_profiles_entries) / sizeof(resource_profiles_entries[0]); + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, &request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); + TEST_CHECK(decoded_context != NULL); + + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS || decoded_context == NULL) { + return; + } + + iterator = decoded_context->profiles.next; + resource_profiles_context = cfl_list_entry(iterator, struct cprof_resource_profiles, _head); + + resource_attribute_value = cfl_kvlist_fetch(resource_profiles_context->resource->attributes, "outer.key"); + TEST_CHECK(resource_attribute_value != NULL); + if (resource_attribute_value != NULL) { + TEST_CHECK(resource_attribute_value->type == CFL_VARIANT_KVLIST); + if (resource_attribute_value->type == CFL_VARIANT_KVLIST) { + nested_value = cfl_kvlist_fetch(resource_attribute_value->data.as_kvlist, "inner.key"); + TEST_CHECK(nested_value != NULL); + if (nested_value != NULL) { + TEST_CHECK(nested_value->type == CFL_VARIANT_STRING); + if (nested_value->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(nested_value->data.as_string, "inner.value") == 0); + } + } + } + } + + iterator = resource_profiles_context->scope_profiles.next; + scope_profiles_context = cfl_list_entry(iterator, struct cprof_scope_profiles, _head); + + scope_attribute_value = cfl_kvlist_fetch(scope_profiles_context->scope->attributes, "outer.key"); + TEST_CHECK(scope_attribute_value != NULL); + if (scope_attribute_value != NULL) { + TEST_CHECK(scope_attribute_value->type == CFL_VARIANT_ARRAY); + if (scope_attribute_value->type == CFL_VARIANT_ARRAY) { + TEST_CHECK(cfl_array_size(scope_attribute_value->data.as_array) == 2); + + array_entry = cfl_array_fetch_by_index(scope_attribute_value->data.as_array, 0); + TEST_CHECK(array_entry != NULL); + if (array_entry != NULL) { + TEST_CHECK(array_entry->type == CFL_VARIANT_STRING); + if (array_entry->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(array_entry->data.as_string, "array.value") == 0); + } + } + + array_entry = cfl_array_fetch_by_index(scope_attribute_value->data.as_array, 1); + TEST_CHECK(array_entry != NULL); + if (array_entry != NULL) { + TEST_CHECK(array_entry->type == CFL_VARIANT_KVLIST); + if (array_entry->type == CFL_VARIANT_KVLIST) { + nested_value = cfl_kvlist_fetch(array_entry->data.as_kvlist, "inner.key"); + TEST_CHECK(nested_value != NULL); + if (nested_value != NULL) { + TEST_CHECK(nested_value->type == CFL_VARIANT_STRING); + if (nested_value->type == CFL_VARIANT_STRING) { + TEST_CHECK(strcmp(nested_value->data.as_string, "inner.value") == 0); + } + } + } + } + } + } + + cprof_decode_opentelemetry_destroy(decoded_context); +} + +static void test_decoder_rejects_missing_string_table_for_resource_attributes() +{ + int result; + struct cprof *decoded_context; + + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest request = + OPENTELEMETRY__PROTO__COLLECTOR__PROFILES__V1DEVELOPMENT__EXPORT_PROFILES_SERVICE_REQUEST__INIT; + Opentelemetry__Proto__Profiles__V1development__ProfilesDictionary dictionary = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILES_DICTIONARY__INIT; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles resource_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__RESOURCE_PROFILES__INIT; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles scope_profiles = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__SCOPE_PROFILES__INIT; + Opentelemetry__Proto__Resource__V1__Resource resource = + OPENTELEMETRY__PROTO__RESOURCE__V1__RESOURCE__INIT; + Opentelemetry__Proto__Profiles__V1development__Profile profile = + OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__PROFILE__INIT; + Opentelemetry__Proto__Common__V1__KeyValue resource_attribute = + OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT; + Opentelemetry__Proto__Common__V1__AnyValue resource_attribute_value_ref = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__INIT; + Opentelemetry__Proto__Profiles__V1development__Profile *profiles[] = { + &profile + }; + Opentelemetry__Proto__Profiles__V1development__ScopeProfiles *scope_profiles_entries[] = { + &scope_profiles + }; + Opentelemetry__Proto__Profiles__V1development__ResourceProfiles *resource_profiles_entries[] = { + &resource_profiles + }; + Opentelemetry__Proto__Common__V1__KeyValue *resource_attributes[] = { + &resource_attribute + }; + + resource_attribute_value_ref.value_case = + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX; + resource_attribute_value_ref.string_value_strindex = 1; + resource_attribute.key_strindex = 1; + resource_attribute.value = &resource_attribute_value_ref; + + resource.attributes = resource_attributes; + resource.n_attributes = sizeof(resource_attributes) / sizeof(resource_attributes[0]); + + profile.time_unix_nano = 1000; + profile.duration_nano = 100; + + scope_profiles.profiles = profiles; + scope_profiles.n_profiles = sizeof(profiles) / sizeof(profiles[0]); + + resource_profiles.resource = &resource; + resource_profiles.scope_profiles = scope_profiles_entries; + resource_profiles.n_scope_profiles = sizeof(scope_profiles_entries) / sizeof(scope_profiles_entries[0]); + + request.dictionary = &dictionary; + request.resource_profiles = resource_profiles_entries; + request.n_resource_profiles = sizeof(resource_profiles_entries) / sizeof(resource_profiles_entries[0]); + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, &request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); +} + +static void test_decoder_rejects_invalid_stack_table_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + Opentelemetry__Proto__Profiles__V1development__Stack **original_stack_table; + size_t original_stack_table_count; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + original_stack_table = request->dictionary->stack_table; + original_stack_table_count = request->dictionary->n_stack_table; + request->dictionary->stack_table = NULL; + request->dictionary->n_stack_table = 0; + request->resource_profiles[0]->scope_profiles[0]->profiles[0]->samples[0]->stack_index = 0; + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + request->dictionary->stack_table = original_stack_table; + request->dictionary->n_stack_table = original_stack_table_count; + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + +static void test_decoder_rejects_invalid_location_mapping_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + size_t index; + Opentelemetry__Proto__Profiles__V1development__Location *location_entry; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + location_entry = NULL; + for (index = 0; index < request->dictionary->n_location_table; index++) { + if (request->dictionary->location_table[index] != NULL) { + location_entry = request->dictionary->location_table[index]; + break; + } + } + + TEST_CHECK(location_entry != NULL); + if (location_entry == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + location_entry->mapping_index = request->dictionary->n_mapping_table; + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + +static void test_decoder_rejects_invalid_line_function_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + size_t index; + size_t line_index; + Opentelemetry__Proto__Profiles__V1development__Location *location_entry; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + location_entry = NULL; + for (index = 0; index < request->dictionary->n_location_table; index++) { + if (request->dictionary->location_table[index] != NULL && + request->dictionary->location_table[index]->n_lines > 0) { + location_entry = request->dictionary->location_table[index]; + break; + } + } + + TEST_CHECK(location_entry != NULL); + if (location_entry == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + for (line_index = 0; line_index < location_entry->n_lines; line_index++) { + if (location_entry->lines[line_index] != NULL) { + location_entry->lines[line_index]->function_index = request->dictionary->n_function_table; + break; + } + } + + TEST_CHECK(line_index < location_entry->n_lines); + if (line_index >= location_entry->n_lines) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + +static void test_decoder_rejects_invalid_profile_attribute_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + Opentelemetry__Proto__Profiles__V1development__Profile *profile; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + profile = request->resource_profiles[0]->scope_profiles[0]->profiles[0]; + TEST_CHECK(profile != NULL); + if (profile == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + profile->attribute_indices = realloc(profile->attribute_indices, sizeof(int32_t)); + TEST_CHECK(profile->attribute_indices != NULL); + if (profile->attribute_indices == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + profile->n_attribute_indices = 1; + profile->attribute_indices[0] = request->dictionary->n_attribute_table; + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + +static void test_decoder_rejects_invalid_sample_attribute_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + Opentelemetry__Proto__Profiles__V1development__Profile *profile; + Opentelemetry__Proto__Profiles__V1development__Sample *sample; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + profile = request->resource_profiles[0]->scope_profiles[0]->profiles[0]; + sample = profile->samples[0]; + TEST_CHECK(sample != NULL); + if (sample == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + sample->attribute_indices = realloc(sample->attribute_indices, sizeof(int32_t)); + TEST_CHECK(sample->attribute_indices != NULL); + if (sample->attribute_indices == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + sample->n_attribute_indices = 1; + sample->attribute_indices[0] = request->dictionary->n_attribute_table; + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + +static void test_decoder_rejects_invalid_sample_link_reference() +{ + int result; + struct cprof *decoded_context; + Opentelemetry__Proto__Collector__Profiles__V1development__ExportProfilesServiceRequest *request; + Opentelemetry__Proto__Profiles__V1development__Profile *profile; + Opentelemetry__Proto__Profiles__V1development__Sample *sample; + + request = create_unpacked_dictionary_request(); + TEST_CHECK(request != NULL); + if (request == NULL) { + return; + } + + profile = request->resource_profiles[0]->scope_profiles[0]->profiles[0]; + sample = profile->samples[0]; + TEST_CHECK(sample != NULL); + if (sample == NULL) { + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); + return; + } + + sample->link_index = request->dictionary->n_link_table; + + decoded_context = NULL; + result = decode_export_service_request(&decoded_context, request); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_INVALID_ARGUMENT_ERROR); + TEST_CHECK(decoded_context == NULL); + + opentelemetry__proto__collector__profiles__v1development__export_profiles_service_request__free_unpacked( + request, NULL); +} + 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}, + {"decoder_dictionary_string_references", test_decoder_dictionary_string_references}, + {"decoder_dictionary_nested_string_references", test_decoder_dictionary_nested_string_references}, + {"decoder_rejects_missing_string_table_for_resource_attributes", + test_decoder_rejects_missing_string_table_for_resource_attributes}, + {"decoder_rejects_invalid_stack_table_reference", test_decoder_rejects_invalid_stack_table_reference}, + {"decoder_rejects_invalid_location_mapping_reference", + test_decoder_rejects_invalid_location_mapping_reference}, + {"decoder_rejects_invalid_line_function_reference", + test_decoder_rejects_invalid_line_function_reference}, + {"decoder_rejects_invalid_profile_attribute_reference", + test_decoder_rejects_invalid_profile_attribute_reference}, + {"decoder_rejects_invalid_sample_attribute_reference", + test_decoder_rejects_invalid_sample_attribute_reference}, + {"decoder_rejects_invalid_sample_link_reference", + test_decoder_rejects_invalid_sample_link_reference}, { 0 } }; diff --git a/lib/cprofiles/tests/text_encoder.c b/lib/cprofiles/tests/text_encoder.c index 433bbc304c7..736393ad67f 100644 --- a/lib/cprofiles/tests/text_encoder.c +++ b/lib/cprofiles/tests/text_encoder.c @@ -20,11 +20,14 @@ #include #include "cprof_tests.h" #include +#include +#include #include #include #include #include +#include unsigned char serialized_data[] = { 0x82, 0xA4, 0x6D, 0x65, 0x74, 0x61, 0x80, 0xA8, 0x70, 0x72, 0x6F, 0x66, @@ -659,7 +662,192 @@ static void test_encoder() } } +static struct cprof *create_otlp_text_fixture(void) +{ + struct cprof *cprof; + struct cprof_resource_profiles *resource_profiles; + struct cprof_scope_profiles *scope_profiles; + struct cprof_profile *profile; + struct cprof_sample *sample; + struct cprof_location *location; + struct cprof_line *line; + struct cprof_function *function; + struct cprof_mapping *mapping; + struct cprof_resource *resource; + struct cfl_kvlist *attrs; + size_t id_bin; + size_t id_func; + + 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 = 1000; + profile->duration_nanos = 100; + cprof_sample_type_str_create(profile, "cpu", "nanoseconds", + CPROF_AGGREGATION_TEMPORALITY_CUMULATIVE); + + id_bin = cprof_profile_string_add(profile, "/bin/app", -1); + id_func = cprof_profile_string_add(profile, "leaf_func", -1); + if (id_bin == 0 || id_func == 0) { + cprof_profile_destroy(profile); + cprof_scope_profiles_destroy(scope_profiles); + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + mapping = cprof_mapping_create(profile); + function = cprof_function_create(profile); + location = cprof_location_create(profile); + sample = cprof_sample_create(profile); + if (mapping == NULL || function == NULL || location == NULL || sample == 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->filename = (int64_t) id_bin; + + function->name = (int64_t) id_func; + function->system_name = (int64_t) id_func; + function->start_line = 10; + + location->mapping_index = 0; + location->address = 0x1010ULL; + line = cprof_line_create(location); + 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; + + if (cprof_sample_add_location_index(sample, 0) != 0 || + cprof_sample_add_value(sample, 7) != 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); + + if (cprof_resource_profiles_add(cprof, resource_profiles) != 0) { + cprof_resource_profiles_destroy(resource_profiles); + cprof_destroy(cprof); + return NULL; + } + + return cprof; +} + +static void test_encoder_otlp_roundtrip_resolved_locations() +{ + cfl_sds_t otlp_result; + cfl_sds_t text_result; + struct cprof *source_context; + struct cprof *decoded_context; + int result; + size_t offset; + + source_context = create_otlp_text_fixture(); + TEST_CHECK(source_context != NULL); + if (source_context == NULL) { + return; + } + + result = cprof_encode_opentelemetry_create(&otlp_result, source_context); + cprof_destroy(source_context); + TEST_CHECK(result == CPROF_ENCODE_OPENTELEMETRY_SUCCESS); + if (result != CPROF_ENCODE_OPENTELEMETRY_SUCCESS) { + return; + } + + offset = 0; + decoded_context = NULL; + result = cprof_decode_opentelemetry_create(&decoded_context, + (unsigned char *) otlp_result, + cfl_sds_len(otlp_result), + &offset); + cprof_encode_opentelemetry_destroy(otlp_result); + TEST_CHECK(result == CPROF_DECODE_OPENTELEMETRY_SUCCESS); + TEST_CHECK(decoded_context != NULL); + if (result != CPROF_DECODE_OPENTELEMETRY_SUCCESS || decoded_context == NULL) { + return; + } + + result = cprof_encode_text_create(&text_result, + decoded_context, + CPROF_ENCODE_TEXT_RENDER_RESOLVED); + TEST_CHECK(result == CPROF_ENCODE_TEXT_SUCCESS); + if (result == CPROF_ENCODE_TEXT_SUCCESS) { + TEST_CHECK(strstr(text_result, "Location index : [ \"leaf_func\"") != NULL); + TEST_CHECK(strstr(text_result, "Locations start index : ") == NULL); + TEST_CHECK(strstr(text_result, "Locations length : ") == NULL); + + cprof_encode_text_destroy(text_result); + } + + cprof_decode_opentelemetry_destroy(decoded_context); +} + TEST_LIST = { {"encoder", test_encoder}, + {"encoder_otlp_roundtrip_resolved_locations", test_encoder_otlp_roundtrip_resolved_locations}, { 0 } }; diff --git a/lib/ctraces/.github/workflows/build.yaml b/lib/ctraces/.github/workflows/build.yaml index 5b40bbe7b08..81b7e7930a7 100644 --- a/lib/ctraces/.github/workflows/build.yaml +++ b/lib/ctraces/.github/workflows/build.yaml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-latest, windows-2019] + os: [windows-latest] permissions: contents: read steps: @@ -22,7 +22,7 @@ jobs: with: submodules: true - - name: Build on ${{ matrix.os }} with vs-2019 + - name: Build on ${{ matrix.os }} run: | .\scripts\win_build.bat @@ -79,9 +79,9 @@ jobs: working-directory: ctraces build-debian: - name: Debian Buster build to confirm no issues once used downstream + name: Debian Bullseye build to confirm no issues once used downstream runs-on: ubuntu-latest - container: debian:buster + container: debian:bullseye steps: - name: Set up base image dependencies run: | diff --git a/lib/ctraces/src/ctr_decode_opentelemetry.c b/lib/ctraces/src/ctr_decode_opentelemetry.c index 8c5078078dc..d62f78a5fc5 100644 --- a/lib/ctraces/src/ctr_decode_opentelemetry.c +++ b/lib/ctraces/src/ctr_decode_opentelemetry.c @@ -308,6 +308,11 @@ static int convert_any_value(struct opentelemetry_decode_value *ctr_val, result = convert_string_value(ctr_val, value_type, key, val->string_value); break; + case OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX: + /* Profiling-only string dictionary reference: ignore in traces. */ + result = 0; + break; + case OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE: result = convert_bool_value(ctr_val, value_type, key, val->bool_value); break; diff --git a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.c b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.c index fcb79ca08c0..a23ea3f2ef5 100644 --- a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.c +++ b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.c @@ -277,7 +277,7 @@ void opentelemetry__proto__common__v1__entity_ref__free_unpacked assert(message->base.descriptor == &opentelemetry__proto__common__v1__entity_ref__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } -static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__any_value__field_descriptors[7] = +static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__any_value__field_descriptors[8] = { { "string_value", @@ -363,6 +363,18 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__any_valu PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "string_value_strindex", + 8, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(Opentelemetry__Proto__Common__V1__AnyValue, value_case), + offsetof(Opentelemetry__Proto__Common__V1__AnyValue, string_value_strindex), + NULL, + NULL, + PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned opentelemetry__proto__common__v1__any_value__field_indices_by_name[] = { 4, /* field[4] = array_value */ @@ -372,11 +384,12 @@ static const unsigned opentelemetry__proto__common__v1__any_value__field_indices 2, /* field[2] = int_value */ 5, /* field[5] = kvlist_value */ 0, /* field[0] = string_value */ + 7, /* field[7] = string_value_strindex */ }; static const ProtobufCIntRange opentelemetry__proto__common__v1__any_value__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 7 } + { 0, 8 } }; const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__any_value__descriptor = { @@ -386,7 +399,7 @@ const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__any_value__de "Opentelemetry__Proto__Common__V1__AnyValue", "opentelemetry.proto.common.v1", sizeof(Opentelemetry__Proto__Common__V1__AnyValue), - 7, + 8, opentelemetry__proto__common__v1__any_value__field_descriptors, opentelemetry__proto__common__v1__any_value__field_indices_by_name, 1, opentelemetry__proto__common__v1__any_value__number_ranges, @@ -469,7 +482,7 @@ const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__key_value_lis (ProtobufCMessageInit) opentelemetry__proto__common__v1__key_value_list__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__key_value__field_descriptors[2] = +static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__key_value__field_descriptors[3] = { { "key", @@ -495,15 +508,28 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__common__v1__key_valu 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "key_strindex", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(Opentelemetry__Proto__Common__V1__KeyValue, key_strindex), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned opentelemetry__proto__common__v1__key_value__field_indices_by_name[] = { 0, /* field[0] = key */ + 2, /* field[2] = key_strindex */ 1, /* field[1] = value */ }; static const ProtobufCIntRange opentelemetry__proto__common__v1__key_value__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 2 } + { 0, 3 } }; const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__key_value__descriptor = { @@ -513,7 +539,7 @@ const ProtobufCMessageDescriptor opentelemetry__proto__common__v1__key_value__de "Opentelemetry__Proto__Common__V1__KeyValue", "opentelemetry.proto.common.v1", sizeof(Opentelemetry__Proto__Common__V1__KeyValue), - 2, + 3, opentelemetry__proto__common__v1__key_value__field_descriptors, opentelemetry__proto__common__v1__key_value__field_indices_by_name, 1, opentelemetry__proto__common__v1__key_value__number_ranges, diff --git a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.h b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.h index ac8d04b7fdd..9d97bbf56f4 100644 --- a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.h +++ b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/common/v1/common.pb-c.h @@ -36,7 +36,8 @@ typedef enum { OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_DOUBLE_VALUE = 4, OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_ARRAY_VALUE = 5, OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_KVLIST_VALUE = 6, - OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE = 7 + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BYTES_VALUE = 7, + OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX = 8 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE__CASE) } Opentelemetry__Proto__Common__V1__AnyValue__ValueCase; @@ -56,6 +57,17 @@ struct Opentelemetry__Proto__Common__V1__AnyValue char *string_value; Opentelemetry__Proto__Common__V1__ArrayValue *array_value; Opentelemetry__Proto__Common__V1__KeyValueList *kvlist_value; + /* + * Reference to the string value in ProfilesDictionary.string_table. + * Note: This is currently used exclusively in the Profiling signal. + * Implementers of OTLP receivers for signals other than Profiling should + * treat the presence of this value as a non-fatal issue. + * Log an error or warning indicating an unexpected field intended for the + * Profiling signal and process the data as if this value were absent or + * empty, ignoring its semantic content for the non-Profiling signal. + * Status: [Development] + */ + int32_t string_value_strindex; protobuf_c_boolean bool_value; }; }; @@ -116,16 +128,29 @@ struct Opentelemetry__Proto__Common__V1__KeyValue ProtobufCMessage base; /* * The key name of the pair. + * key_ref MUST NOT be set if key is used. */ char *key; /* * The value of the pair. */ Opentelemetry__Proto__Common__V1__AnyValue *value; + /* + * Reference to the string key in ProfilesDictionary.string_table. + * key MUST NOT be set if key_strindex is used. + * Note: This is currently used exclusively in the Profiling signal. + * Implementers of OTLP receivers for signals other than Profiling should + * treat the presence of this key as a non-fatal issue. + * Log an error or warning indicating an unexpected field intended for the + * Profiling signal and process the data as if this value were absent or + * empty, ignoring its semantic content for the non-Profiling signal. + * Status: [Development] + */ + int32_t key_strindex; }; #define OPENTELEMETRY__PROTO__COMMON__V1__KEY_VALUE__INIT \ { PROTOBUF_C_MESSAGE_INIT (&opentelemetry__proto__common__v1__key_value__descriptor) \ -, (char *)protobuf_c_empty_string, NULL } +, (char *)protobuf_c_empty_string, NULL, 0 } /* diff --git a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/logs/v1/logs.pb-c.h b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/logs/v1/logs.pb-c.h index aacf998e911..e31963433d0 100644 --- a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/logs/v1/logs.pb-c.h +++ b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/logs/v1/logs.pb-c.h @@ -29,9 +29,6 @@ typedef struct Opentelemetry__Proto__Logs__V1__LogRecord Opentelemetry__Proto__L * Possible values for LogRecord.SeverityNumber. */ typedef enum _Opentelemetry__Proto__Logs__V1__SeverityNumber { - /* - * UNSPECIFIED is the default SeverityNumber, it MUST NOT be used. - */ OPENTELEMETRY__PROTO__LOGS__V1__SEVERITY_NUMBER__SEVERITY_NUMBER_UNSPECIFIED = 0, OPENTELEMETRY__PROTO__LOGS__V1__SEVERITY_NUMBER__SEVERITY_NUMBER_TRACE = 1, OPENTELEMETRY__PROTO__LOGS__V1__SEVERITY_NUMBER__SEVERITY_NUMBER_TRACE2 = 2, diff --git a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.c b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.c index 683c9465c11..7f454df2449 100644 --- a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.c +++ b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.c @@ -1216,21 +1216,9 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__profiles__v1developm 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, - { - "values", - 2, - PROTOBUF_C_LABEL_REPEATED, - PROTOBUF_C_TYPE_INT64, - offsetof(Opentelemetry__Proto__Profiles__V1development__Sample, n_values), - offsetof(Opentelemetry__Proto__Profiles__V1development__Sample, values), - NULL, - NULL, - PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */ - 0,NULL,NULL /* reserved1,reserved2, etc */ - }, { "attribute_indices", - 3, + 2, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_INT32, offsetof(Opentelemetry__Proto__Profiles__V1development__Sample, n_attribute_indices), @@ -1242,7 +1230,7 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__profiles__v1developm }, { "link_index", - 4, + 3, PROTOBUF_C_LABEL_NONE, PROTOBUF_C_TYPE_INT32, 0, /* quantifier_offset */ @@ -1252,6 +1240,18 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__profiles__v1developm 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "values", + 4, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_INT64, + offsetof(Opentelemetry__Proto__Profiles__V1development__Sample, n_values), + offsetof(Opentelemetry__Proto__Profiles__V1development__Sample, values), + NULL, + NULL, + PROTOBUF_C_FIELD_FLAG_PACKED, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, { "timestamps_unix_nano", 5, @@ -1266,11 +1266,11 @@ static const ProtobufCFieldDescriptor opentelemetry__proto__profiles__v1developm }, }; static const unsigned opentelemetry__proto__profiles__v1development__sample__field_indices_by_name[] = { - 2, /* field[2] = attribute_indices */ - 3, /* field[3] = link_index */ + 1, /* field[1] = attribute_indices */ + 2, /* field[2] = link_index */ 0, /* field[0] = stack_index */ 4, /* field[4] = timestamps_unix_nano */ - 1, /* field[1] = values */ + 3, /* field[3] = values */ }; static const ProtobufCIntRange opentelemetry__proto__profiles__v1development__sample__number_ranges[1 + 1] = { diff --git a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.h b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.h index 6e63c86c19e..bf826e32cfe 100644 --- a/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.h +++ b/lib/fluent-otel-proto/proto_c/opentelemetry/proto/profiles/v1development/profiles.pb-c.h @@ -251,11 +251,16 @@ struct Opentelemetry__Proto__Profiles__V1development__Profile size_t n_samples; Opentelemetry__Proto__Profiles__V1development__Sample **samples; /* - * Time of collection (UTC) represented as nanoseconds past the epoch. + * Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00 + * UTC on 1 January 1970. */ uint64_t time_unix_nano; /* - * Duration of the profile, if a duration makes sense. + * Duration of the profile. For instant profiles like live heap snapshot, the + * duration can be zero but it may be preferable to set time_unix_nano to the + * process start time and duration_nano to the relative time when the profile + * was gathered. This ensures Sample.timestamps_unix_nano values such as + * allocation timestamp fall into the profile time range. */ uint64_t duration_nano; /* @@ -366,7 +371,10 @@ struct Opentelemetry__Proto__Profiles__V1development__ValueType * A Sample MUST have have at least one values or timestamps_unix_nano entry. If * both fields are populated, they MUST contain the same number of elements, and * the elements at the same index MUST refer to the same event. - * Examples of different ways of representing a sample with the total value of 10: + * For the purposes of efficiently representing aggregated data observations, a Sample is regarded + * as having a shared identity and an associated collection of per-observation data points. + * Samples having the same identity SHOULD be combined by inserting timestamps and values to the data arrays. + * Examples of different ways ('shapes') of representing a sample with the total value of 10: * Report of a stacktrace at 10 timestamps (consumers must assume the value is 1 for each point): * values: [] * timestamps_unix_nano: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -376,6 +384,8 @@ struct Opentelemetry__Proto__Profiles__V1development__ValueType * Report of a stacktrace at 4 timestamps where each point records a specific value: * values: [2, 2, 3, 3] * timestamps_unix_nano: [1, 2, 3, 4] + * All Samples for a Profile SHOULD have the same shape, i.e. all data observation series should consistently + * adopt the same data recording style. */ struct Opentelemetry__Proto__Profiles__V1development__Sample { @@ -384,11 +394,6 @@ struct Opentelemetry__Proto__Profiles__V1development__Sample * Reference to stack in ProfilesDictionary.stack_table. */ int32_t stack_index; - /* - * The type and unit of each value is defined by Profile.sample_type. - */ - size_t n_values; - int64_t *values; /* * References to attributes in ProfilesDictionary.attribute_table. [optional] */ @@ -400,15 +405,22 @@ struct Opentelemetry__Proto__Profiles__V1development__Sample */ int32_t link_index; /* - * Timestamps associated with Sample represented in nanoseconds. These - * timestamps should fall within the Profile's time range. + * The type and unit of each value is defined by Profile.sample_type. + */ + size_t n_values; + int64_t *values; + /* + * Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds + * since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the + * [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano) + * time range. */ size_t n_timestamps_unix_nano; uint64_t *timestamps_unix_nano; }; #define OPENTELEMETRY__PROTO__PROFILES__V1DEVELOPMENT__SAMPLE__INIT \ { PROTOBUF_C_MESSAGE_INIT (&opentelemetry__proto__profiles__v1development__sample__descriptor) \ -, 0, 0,NULL, 0,NULL, 0, 0,NULL } +, 0, 0,NULL, 0, 0,NULL, 0,NULL } /* diff --git a/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.c b/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.c index 005bc196f8e..776ee4fb594 100644 --- a/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.c +++ b/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors. + * Copyright (c) 2008-2023, Dave Benson and the protobuf-c authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,14 +28,14 @@ */ /*! \file - * Support library for `protoc-gen-c` generated code. + * Support library for `protoc-c` generated code. * * This file implements the public API used by the code generated - * by `protoc-gen-c`. + * by `protoc-c`. * * \authors Dave Benson and the protobuf-c authors * - * \copyright 2008-2025. Licensed under the terms of the [BSD-2-Clause] license. + * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license. */ /** @@ -3278,8 +3278,6 @@ protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc, n_unknown * sizeof(ProtobufCMessageUnknownField)); if (rv->unknown_fields == NULL) goto error_cleanup; - } else { - rv->unknown_fields = NULL; } /* do real parsing */ diff --git a/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.h b/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.h old mode 100644 new mode 100755 index cb5c82ec43d..a0625117e8d --- a/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.h +++ b/lib/fluent-otel-proto/proto_c/protobuf-c/protobuf-c.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors. + * Copyright (c) 2008-2023, Dave Benson and the protobuf-c authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,17 +34,16 @@ * * This file defines the public API for the `libprotobuf-c` support library. * This API includes interfaces that can be used directly by client code as well - * as the interfaces used by the code generated by the `protoc-gen-c` compiler - * plugin. + * as the interfaces used by the code generated by the `protoc-c` compiler. * * The `libprotobuf-c` support library performs the actual serialization and * deserialization of Protocol Buffers messages. It interacts with structures, - * definitions, and metadata generated by the `protoc-gen-c` compiler plugin - * from .proto files. + * definitions, and metadata generated by the `protoc-c` compiler from .proto + * files. * * \authors Dave Benson and the `protobuf-c` authors. * - * \copyright 2008-2025. Licensed under the terms of the [BSD-2-Clause] license. + * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license. * * [protobuf-c]: https://github.com/protobuf-c/protobuf-c * [Protocol Buffers]: https://developers.google.com/protocol-buffers/ @@ -75,7 +74,7 @@ * * - Identifiers for functions and globals are all lowercase, with camel case * words separated by single underscores. For example, one of the function - * prototypes generated by `protoc-gen-c` for the above example: + * prototypes generated by `protoc-c` for the above example: * ~~~{.c} Foo__Bar__BazBah * @@ -795,16 +794,16 @@ protobuf_c_version_number(void); * The version of the protobuf-c headers, represented as a string using the same * format as protobuf_c_version(). */ -#define PROTOBUF_C_VERSION "1.5.2" +#define PROTOBUF_C_VERSION "1.5.0" /** * The version of the protobuf-c headers, represented as an integer using the * same format as protobuf_c_version_number(). */ -#define PROTOBUF_C_VERSION_NUMBER 1005002 +#define PROTOBUF_C_VERSION_NUMBER 1005000 /** - * The minimum protoc-gen-c version which works with the current version of the + * The minimum protoc-c version which works with the current version of the * protobuf-c headers. */ #define PROTOBUF_C_MIN_COMPILER_VERSION 1000000 diff --git a/plugins/in_opentelemetry/opentelemetry_logs.c b/plugins/in_opentelemetry/opentelemetry_logs.c index df8f3cfbc3b..d8327422491 100644 --- a/plugins/in_opentelemetry/opentelemetry_logs.c +++ b/plugins/in_opentelemetry/opentelemetry_logs.c @@ -153,6 +153,11 @@ static int otlp_pack_any_value(msgpack_packer *mp_pck, result = otel_pack_string(mp_pck, body->string_value); break; + case OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_STRING_VALUE_STRINDEX: + /* Profiling-only string dictionary reference: ignore in logs. */ + result = msgpack_pack_nil(mp_pck); + break; + case OPENTELEMETRY__PROTO__COMMON__V1__ANY_VALUE__VALUE_BOOL_VALUE: result = otel_pack_bool(mp_pck, body->bool_value); break; @@ -261,6 +266,13 @@ static int otel_pack_v1_metadata(struct flb_opentelemetry *ctx, } } + if (log_record->dropped_attributes_count > 0) { + flb_mp_map_header_append(&mh); + msgpack_pack_str(mp_pck, 24); + msgpack_pack_str_body(mp_pck, "dropped_attributes_count", 24); + msgpack_pack_uint64(mp_pck, log_record->dropped_attributes_count); + } + if (log_record->trace_id.len > 0) { flb_mp_map_header_append(&mh); msgpack_pack_str(mp_pck, 8); @@ -286,6 +298,14 @@ static int otel_pack_v1_metadata(struct flb_opentelemetry *ctx, msgpack_pack_str_body(mp_pck, "trace_flags", 11); msgpack_pack_uint8(mp_pck, (uint8_t) log_record->flags & 0xff); + if (log_record->event_name != NULL && strlen(log_record->event_name) > 0) { + flb_mp_map_header_append(&mh); + msgpack_pack_str(mp_pck, 10); + msgpack_pack_str_body(mp_pck, "event_name", 10); + msgpack_pack_str(mp_pck, strlen(log_record->event_name)); + msgpack_pack_str_body(mp_pck, log_record->event_name, strlen(log_record->event_name)); + } + flb_mp_map_header_end(&mh); /* otlp key end */ diff --git a/src/opentelemetry/flb_opentelemetry_logs.c b/src/opentelemetry/flb_opentelemetry_logs.c index 4d39324ddc9..fe5c8ef2de4 100644 --- a/src/opentelemetry/flb_opentelemetry_logs.c +++ b/src/opentelemetry/flb_opentelemetry_logs.c @@ -50,8 +50,11 @@ static int process_json_payload_log_records_entry( msgpack_object *observed_time_unix_nano = NULL; msgpack_object *severity_number = NULL; msgpack_object *severity_text = NULL; + msgpack_object *dropped_attributes_count = NULL; msgpack_object *trace_id = NULL; msgpack_object *span_id = NULL; + msgpack_object *trace_flags = NULL; + msgpack_object *event_name = NULL; struct flb_time timestamp; if (error_status) { @@ -153,6 +156,12 @@ static int process_json_payload_log_records_entry( metadata_object = &log_records_entry->ptr[result].val; } + /* droppedAttributesCount */ + result = flb_otel_utils_find_map_entry_by_key(log_records_entry, "droppedAttributesCount", 0, FLB_TRUE); + if (result >= 0) { + dropped_attributes_count = &log_records_entry->ptr[result].val; + } + /* traceId */ result = flb_otel_utils_find_map_entry_by_key(log_records_entry, "traceId", 0, FLB_TRUE); if (result >= 0) { @@ -229,6 +238,12 @@ static int process_json_payload_log_records_entry( } } + /* traceFlags */ + result = flb_otel_utils_find_map_entry_by_key(log_records_entry, "traceFlags", 0, FLB_TRUE); + if (result >= 0) { + trace_flags = &log_records_entry->ptr[result].val; + } + /* body */ result = flb_otel_utils_find_map_entry_by_key(log_records_entry, "body", 0, FLB_TRUE); if (result == -1) { @@ -244,6 +259,12 @@ static int process_json_payload_log_records_entry( body_object = &log_records_entry->ptr[result].val; } + /* eventName */ + result = flb_otel_utils_find_map_entry_by_key(log_records_entry, "eventName", 0, FLB_TRUE); + if (result >= 0) { + event_name = &log_records_entry->ptr[result].val; + } + result = flb_log_event_encoder_begin_record(encoder); if (result == FLB_EVENT_ENCODER_SUCCESS) { @@ -301,11 +322,20 @@ static int process_json_payload_log_records_entry( result = flb_otel_utils_json_payload_append_converted_kvlist(encoder, FLB_LOG_EVENT_METADATA, metadata_object); } + if (dropped_attributes_count != NULL && + (dropped_attributes_count->type == MSGPACK_OBJECT_POSITIVE_INTEGER || + dropped_attributes_count->type == MSGPACK_OBJECT_NEGATIVE_INTEGER)) { + flb_log_event_encoder_append_metadata_values(encoder, + FLB_LOG_EVENT_STRING_VALUE("dropped_attributes_count", 24), + FLB_LOG_EVENT_MSGPACK_OBJECT_VALUE(dropped_attributes_count)); + } + if (trace_id != NULL && trace_id->type == MSGPACK_OBJECT_STR && trace_id->via.str.size == 32) { if (flb_otel_utils_hex_to_id(trace_id->via.str.ptr, trace_id->via.str.size, tmp_id, 16) != 0) { if (error_status) { *error_status = FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID; } + flb_log_event_encoder_rollback_record(encoder); return -FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID; } flb_log_event_encoder_append_metadata_values(encoder, @@ -318,6 +348,7 @@ static int process_json_payload_log_records_entry( if (error_status) { *error_status = FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID; } + flb_log_event_encoder_rollback_record(encoder); return -FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID; } flb_log_event_encoder_append_metadata_values(encoder, @@ -325,6 +356,50 @@ static int process_json_payload_log_records_entry( FLB_LOG_EVENT_BINARY_VALUE(tmp_id, 8)); } + if (trace_flags != NULL) { + if (trace_flags->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + timestamp_uint64 = trace_flags->via.u64; + + flb_log_event_encoder_append_metadata_values(encoder, + FLB_LOG_EVENT_STRING_VALUE("trace_flags", 11), + FLB_LOG_EVENT_UINT64_VALUE((uint8_t) (timestamp_uint64 & 0xff))); + } + else if (trace_flags->type == MSGPACK_OBJECT_STR) { + memset(timestamp_str, 0, sizeof(timestamp_str)); + if (trace_flags->via.str.size < sizeof(timestamp_str)) { + strncpy(timestamp_str, + trace_flags->via.str.ptr, + trace_flags->via.str.size); + } + else { + strncpy(timestamp_str, + trace_flags->via.str.ptr, + sizeof(timestamp_str) - 1); + } + + for (i = 0; i < strlen(timestamp_str); i++) { + if (!isdigit((unsigned char) timestamp_str[i])) { + timestamp_str[0] = '\0'; + break; + } + } + + if (strlen(timestamp_str) > 0) { + timestamp_uint64 = strtoull(timestamp_str, NULL, 10); + + flb_log_event_encoder_append_metadata_values(encoder, + FLB_LOG_EVENT_STRING_VALUE("trace_flags", 11), + FLB_LOG_EVENT_UINT64_VALUE((uint8_t) (timestamp_uint64 & 0xff))); + } + } + } + + if (event_name != NULL && event_name->type == MSGPACK_OBJECT_STR) { + flb_log_event_encoder_append_metadata_values(encoder, + FLB_LOG_EVENT_STRING_VALUE("event_name", 10), + FLB_LOG_EVENT_MSGPACK_OBJECT_VALUE(event_name)); + } + result = flb_log_event_encoder_commit_map(encoder, FLB_LOG_EVENT_METADATA); if (result == FLB_EVENT_ENCODER_SUCCESS && diff --git a/tests/internal/data/opentelemetry/logs.json b/tests/internal/data/opentelemetry/logs.json index dbc9cd0c1dd..1c9dcd89a89 100644 --- a/tests/internal/data/opentelemetry/logs.json +++ b/tests/internal/data/opentelemetry/logs.json @@ -251,6 +251,27 @@ } }, + "valid_log_with_event_name_and_dropped_attributes_count": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "eventName": "com.example.user.login", + "droppedAttributesCount": 3, + "body": {"stringValue": "test log with event metadata"} + }] + }] + }] + }, + "expected": { + "group_metadata": {"schema":"otlp","resource_id":0,"scope_id":0}, + "group_body": {"resource":{}}, + "log_metadata": {"otlp":{"dropped_attributes_count":3,"event_name":"com.example.user.login"}}, + "log_body": {"log": "test log with event metadata"} + } + }, + "valid_log_with_trace_span": { "input": { "resourceLogs": [{ @@ -272,6 +293,26 @@ } }, + "valid_log_with_trace_flags": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "traceFlags": 129, + "body": {"stringValue": "test log with trace flags"} + }] + }] + }] + }, + "expected": { + "group_metadata": {"schema":"otlp","resource_id":0,"scope_id":0}, + "group_body": {"resource":{}}, + "log_metadata": {"otlp":{"trace_flags":129}}, + "log_body": {"log": "test log with trace flags"} + } + }, + "valid_log_with_resource": { "input": { "resourceLogs": [{ diff --git a/tests/internal/log.c b/tests/internal/log.c index c9f94898c59..10b09fb8b49 100644 --- a/tests/internal/log.c +++ b/tests/internal/log.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "flb_tests_internal.h" @@ -25,8 +26,8 @@ static int check_clock(uint64_t timeout, struct flb_time *tm_start) diff = now - start; if (!(TEST_CHECK(diff < timeout))) { - TEST_MSG("clock error, unsuppresed log: now=%llu, start=%llu timeout=%llu(%llu), diff=%llu", - now, start, start+timeout, timeout, diff); + TEST_MSG("clock error, unsuppresed log: now=%" PRIu64 ", start=%" PRIu64 " timeout=%" PRIu64 "(%" PRIu64 "), diff=%" PRIu64, + now, start, start + timeout, timeout, diff); return -1; }