From b81fdbb6e049bfc847490828d26536902ef7b9d2 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 17 Dec 2024 13:49:18 +0100 Subject: [PATCH 01/26] Initial component ffi --- Cargo.lock | 9 ++ Cargo.toml | 5 +- build-config-ffi.sh | 148 +++++++++++++++++++++++++++++++ build-telemetry-ffi.sh | 134 ---------------------------- library-config-ffi/Cargo.toml | 22 +++++ library-config-ffi/build.rs | 10 +++ library-config-ffi/cbindgen.toml | 33 +++++++ library-config-ffi/src/lib.rs | 66 ++++++++++++++ 8 files changed, 291 insertions(+), 136 deletions(-) create mode 100755 build-config-ffi.sh delete mode 100755 build-telemetry-ffi.sh create mode 100644 library-config-ffi/Cargo.toml create mode 100644 library-config-ffi/build.rs create mode 100644 library-config-ffi/cbindgen.toml create mode 100644 library-config-ffi/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8d0eeb25f5..fd6da6815d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,6 +1442,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "datadog-library-config-ffi" +version = "0.0.1" +dependencies = [ + "build_common", + "ddcommon", + "ddcommon-ffi", +] + [[package]] name = "datadog-live-debugger" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index eeccc2d51d..430288f810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "ddsketch", "tinybytes", "dogstatsd-client", + "library-config-ffi", ] # https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2 @@ -66,7 +67,7 @@ debug = 2 # full debug info codegen-units = 1 debug = "line-tables-only" lto = true -opt-level = "s" # optimize for size +opt-level = "s" # optimize for size [profile.release.package.datadog-serverless-trace-mini-agent] strip = true @@ -82,4 +83,4 @@ codegen-units = 1 # proptest pulls in a dependency on libm, which changes the runtime of some math functions # so benchmarks are not measuring the same thing as the release build. This patch removes # the default dependency on libm. A PR will be opened to proptest to make this optional. -proptest = { git = 'https://github.com/bantonsson/proptest.git', branch = "ban/avoid-libm-in-std"} +proptest = { git = 'https://github.com/bantonsson/proptest.git', branch = "ban/avoid-libm-in-std" } diff --git a/build-config-ffi.sh b/build-config-ffi.sh new file mode 100755 index 0000000000..401dd9a8a0 --- /dev/null +++ b/build-config-ffi.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +# Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +# SPDX-License-Identifier: Apache-2.0 + +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +# Location to place all artifacts +if [ -z $CARGO_TARGET_DIR ] ; then + export CARGO_TARGET_DIR=$PWD/target +fi + +set -eu + +destdir="$1" + +if [ $CARGO_TARGET_DIR = $destdir ]; then + echo "Error: CARGO_TARGET_DIR and destdir cannot be the same" + exit 1 +fi + +mkdir -v -p "$destdir/include/datadog" "$destdir/lib/pkgconfig" "$destdir/cmake" + +version=$(awk -F\" '$1 ~ /^version/ { print $2 }' < profiling-ffi/Cargo.toml) +target="$(rustc -vV | awk '/^host:/ { print $2 }')" +shared_library_suffix=".so" +static_library_suffix=".a" +library_prefix="lib" +remove_rpath=0 +fix_macos_rpath=0 + +# Rust provides this note about the link libraries: +# note: Link against the following native artifacts when linking against this +# static library. The order and any duplication can be significant on some +# platforms. +# +# We've decided to strip out -lgcc_s because if it's provided then it will +# always make it into the final runtime dependencies, even if -static-libgcc is +# provided. At least on Alpine, libgcc_s may not even exist in the users' +# images, so -static-libgcc is recommended there. +case "$target" in + "x86_64-alpine-linux-musl"|"aarch64-alpine-linux-musl") + # on alpine musl, Rust adds some weird runpath to cdylibs + remove_rpath=1 + ;; + + "x86_64-apple-darwin"|"aarch64-apple-darwin") + + shared_library_suffix=".dylib" + # fix usage of library in macos via rpath + fix_macos_rpath=1 + ;; + + "x86_64-unknown-linux-gnu"|"aarch64-unknown-linux-gnu") + ;; + + "x86_64-pc-windows-msvc") + shared_library_suffix=".dll" + static_library_suffix=".lib" + library_prefix="" + ;; + + *) + >&2 echo "Unknown platform '${target}'" + exit 1 + ;; +esac + + +cp -v LICENSE LICENSE-3rdparty.yml NOTICE "$destdir/" + + +crate_dir="library-config-ffi" +crate="datadog-library-config-ffi" + + +FEATURES=( + "cbindgen" +) + +FEATURES=$(IFS=, ; echo "${FEATURES[*]}") +echo "Building for features: $FEATURES" + +# build inside the crate to use the config.toml file +( cd "$crate_dir" && cargo build --features $FEATURES --release --target "${target}" ) + +# Remove _ffi suffix when copying +crate_name_underscore=$(echo "$crate" | sed 's/-/_/g') +renamed_stem=$(echo "$crate_name_underscore" | sed 's/_ffi//g') + +shared_library_name="${library_prefix}${crate_name_underscore}${shared_library_suffix}" +shared_library_rename="${library_prefix}${renamed_stem}${shared_library_suffix}" + +static_library_name="${library_prefix}${crate_name_underscore}${static_library_suffix}" +static_library_rename="${library_prefix}${renamed_stem}${static_library_suffix}" + +cp -v "$CARGO_TARGET_DIR/${target}/release/${shared_library_name}" "$destdir/lib/${shared_library_rename}" +cp -v "$CARGO_TARGET_DIR/${target}/release/${static_library_name}" "$destdir/lib/${static_library_rename}" + +shared_library_name="${shared_library_rename}" +static_library_name="${static_library_rename}" + +if [[ "$remove_rpath" -eq 1 ]]; then + patchelf --remove-rpath "$destdir/lib/${shared_library_name}" +fi + +if [[ "$fix_macos_rpath" -eq 1 ]]; then + install_name_tool -id @rpath/${shared_library_name} "$destdir/lib/${shared_library_name}" +fi + +if command -v patchelf > /dev/null && [[ "$target" != "x86_64-pc-windows-msvc" ]]; then + patchelf --set-soname ${shared_library_name} "$destdir/lib/${shared_library_rename}" +fi + +# objcopy might not be available on macOS +if command -v objcopy > /dev/null && [[ "$target" != "x86_64-pc-windows-msvc" ]]; then + # Remove .llvmbc section which is not useful for clients + objcopy --remove-section .llvmbc "$destdir/lib/${static_library_name}" + + # Ship debug information separate from shared library, so that downstream packages can selectively include it + # https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + objcopy --only-keep-debug "$destdir/lib/$shared_library_name" "$destdir/lib/$shared_library_name.debug" + strip -S "$destdir/lib/$shared_library_name" + objcopy --add-gnu-debuglink="$destdir/lib/$shared_library_name.debug" "$destdir/lib/$shared_library_name" +fi + +echo "Building tools" +cargo build --package tools --bins + +echo "Generating $destdir/include/libdatadog headers..." +rm -r $destdir/include/datadog/ +mkdir $destdir/include/datadog/ + +CBINDGEN_HEADERS="common.h library-config.h" + +CBINDGEN_HEADERS_DESTS="" +for header in $CBINDGEN_HEADERS; do + HEADER_DEST="$destdir/include/datadog/$header" + cp "$CARGO_TARGET_DIR/include/datadog/$header" "$HEADER_DEST" + CBINDGEN_HEADERS_DESTS="$CBINDGEN_HEADERS_DESTS $HEADER_DEST" +done + +"$CARGO_TARGET_DIR"/debug/dedup_headers $CBINDGEN_HEADERS_DESTS + +echo "Done." diff --git a/build-telemetry-ffi.sh b/build-telemetry-ffi.sh deleted file mode 100755 index a2e7cd803c..0000000000 --- a/build-telemetry-ffi.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ -# SPDX-License-Identifier: Apache-2.0 - -set -eu - -destdir="$1" - -mkdir -v -p "$destdir/include/datadog" "$destdir/lib/pkgconfig" "$destdir/cmake" - -version=$(awk -F\" '$1 ~ /^version/ { print $2 }' < ddtelemetry-ffi/Cargo.toml) -target="$(rustc -vV | awk '/^host:/ { print $2 }')" -shared_library_suffix=".so" -static_library_suffix=".a" -library_prefix="lib" -remove_rpath=0 -fix_macos_rpath=0 - -# Rust provides this note about the link libraries: -# note: Link against the following native artifacts when linking against this -# static library. The order and any duplication can be significant on some -# platforms. -# -# We've decided to strip out -lgcc_s because if it's provided then it will -# always make it into the final runtime dependencies, even if -static-libgcc is -# provided. At least on Alpine, libgcc_s may not even exist in the users' -# images, so -static-libgcc is recommended there. -case "$target" in - "x86_64-alpine-linux-musl"|"aarch64-alpine-linux-musl") - expected_native_static_libs=" -lssp_nonshared -lgcc_s -lc" - native_static_libs=" -lssp_nonshared -lc" - # on alpine musl, Rust adds some weird runpath to cdylibs - remove_rpath=1 - ;; - - "x86_64-apple-darwin"|"aarch64-apple-darwin") - expected_native_static_libs=" -framework Security -framework CoreFoundation -liconv -lSystem -lresolv -lc -lm -liconv" - native_static_libs="${expected_native_static_libs}" - shared_library_suffix=".dylib" - # fix usage of library in macos via rpath - fix_macos_rpath=1 - ;; - - "x86_64-unknown-linux-gnu"|"aarch64-unknown-linux-gnu") - expected_native_static_libs=" -ldl -lrt -lpthread -lgcc_s -lc -lm -lrt -lpthread -lutil -ldl -lutil" - native_static_libs=" -ldl -lrt -lpthread -lc -lm -lrt -lpthread -lutil -ldl -lutil" - ;; - - "x86_64-pc-windows-msvc") - expected_native_static_libs="" # I don't know what to expect - native_static_libs="" # I don't know what to expect - shared_library_suffix=".dll" - static_library_suffix=".lib" - library_prefix="" - ;; - - *) - >&2 echo "Unknown platform '${target}'" - exit 1 - ;; -esac - -cp -v LICENSE LICENSE-3rdparty.yml NOTICE "$destdir/" - -export RUSTFLAGS="${RUSTFLAGS:- -C relocation-model=pic}" - -datadog_telemetry_ffi="ddtelemetry-ffi" -echo "Building the ${datadog_telemetry_ffi} crate (may take some time)..." -DESTDIR="$destdir" cargo build --package="${datadog_telemetry_ffi}" --release --target "${target}" - -# Remove _ffi suffix when copying -shared_library_name="${library_prefix}ddtelemetry_ffi${shared_library_suffix}" -shared_library_rename="${library_prefix}ddtelemetry${shared_library_suffix}" - -static_library_name="${library_prefix}ddtelemetry_ffi${static_library_suffix}" -static_library_rename="${library_prefix}ddtelemetry${static_library_suffix}" - -cp -v "target/${target}/release/${shared_library_name}" "$destdir/lib/${shared_library_rename}" -cp -v "target/${target}/release/${static_library_name}" "$destdir/lib/${static_library_rename}" - -shared_library_name="${shared_library_rename}" -static_library_name="${static_library_rename}" - -if [[ "$remove_rpath" -eq 1 ]]; then - patchelf --remove-rpath "$destdir/lib/${shared_library_name}" -fi - -if [[ "$fix_macos_rpath" -eq 1 ]]; then - install_name_tool -id @rpath/${shared_library_name} "$destdir/lib/${shared_library_name}" -fi - -# objcopy might not be available on macOS -if command -v objcopy > /dev/null && [[ "$target" != "x86_64-pc-windows-msvc" ]]; then - # Remove .llvmbc section which is not useful for clients - objcopy --remove-section .llvmbc "$destdir/lib/${static_library_name}" - - # Ship debug information separate from shared library, so that downstream packages can selectively include it - # https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html - objcopy --only-keep-debug "$destdir/lib/$shared_library_name" "$destdir/lib/$shared_library_name.debug" - strip -S "$destdir/lib/$shared_library_name" - objcopy --add-gnu-debuglink="$destdir/lib/$shared_library_name.debug" "$destdir/lib/$shared_library_name" -fi - -echo "Checking that native-static-libs are as expected for this platform..." -cd ddtelemetry-ffi -actual_native_static_libs="$(cargo rustc --release --target "${target}" -- --print=native-static-libs 2>&1 | awk -F ':' '/note: native-static-libs:/ { print $3 }')" -echo "Actual native-static-libs:${actual_native_static_libs}" -echo "Expected native-static-libs:${expected_native_static_libs}" - -# Compare unique elements between expected and actual native static libs. -# If actual libs is different from expected libs but still a subset of expected libs -# (ie. we will overlink compared to what is actually needed), this is not considered as an error. -# Raise an error only if some libs are in actual libs but not in expected libs. - -# trim leading and trailing spaces, then split the string on " -" by inserting new lines and sort lines while removing duplicates -unique_expected_libs=$(echo "$expected_native_static_libs "| awk '{ gsub(/^[ \t]+|[ \t]+$/, "");gsub(/ +-/,"\n-")};1' | sort -u) -unique_libs=$(echo "$actual_native_static_libs "| awk '{ gsub(/^[ \t]+|[ \t]+$/, "");gsub(/ +-/,"\n-")};1' | sort -u) - -unexpected_native_libs=$(comm -13 <(echo "$unique_expected_libs") <(echo "$unique_libs")) -if [ -n "$unexpected_native_libs" ]; then - echo "Error - More native static libraries are required for linking than expected:" 1>&2 - echo "$unexpected_native_libs" 1>&2 - exit 1 -fi -cd - - -echo "Building tools" -cargo build --package tools --bins - -echo "Generating $destdir/include/libdatadog headers..." -./target/debug/dedup_headers "${destdir}/include/datadog/common.h" "${destdir}/include/datadog/telemetry.h" - -echo "Done." diff --git a/library-config-ffi/Cargo.toml b/library-config-ffi/Cargo.toml new file mode 100644 index 0000000000..158dbd2bda --- /dev/null +++ b/library-config-ffi/Cargo.toml @@ -0,0 +1,22 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present Datadog, Inc. + +[package] +name = "datadog-library-config-ffi" +version = "0.0.1" +edition = "2021" + +[lib] +crate-type = ["staticlib", "cdylib"] +bench = false + +[dependencies] +ddcommon = { path = "../ddcommon" } +ddcommon-ffi = { path = "../ddcommon-ffi", default-features = false } + +[features] +default = ["cbindgen"] +cbindgen = ["build_common/cbindgen", "ddcommon-ffi/cbindgen"] + +[build-dependencies] +build_common = { path = "../build-common" } diff --git a/library-config-ffi/build.rs b/library-config-ffi/build.rs new file mode 100644 index 0000000000..e4ef771eeb --- /dev/null +++ b/library-config-ffi/build.rs @@ -0,0 +1,10 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 +extern crate build_common; + +use build_common::generate_and_configure_header; + +fn main() { + let header_name = "library-config.h"; + generate_and_configure_header(header_name); +} diff --git a/library-config-ffi/cbindgen.toml b/library-config-ffi/cbindgen.toml new file mode 100644 index 0000000000..822d5f53fe --- /dev/null +++ b/library-config-ffi/cbindgen.toml @@ -0,0 +1,33 @@ +# Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +# SPDX-License-Identifier: Apache-2.0 + +language = "C" +tab_width = 2 +header = """// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 +""" +include_guard = "DDOG_LIBRARY_CONFIG_H" +style = "both" + +no_includes = true +sys_includes = ["stdbool.h", "stddef.h", "stdint.h", "stdio.h"] +includes = ["common.h"] + +[export.mangle] +rename_types = "PascalCase" + +[export.rename] +"ParseTagsResult" = "ddog_Vec_Tag_ParseResult" +"PushTagResult" = "ddog_Vec_Tag_PushResult" +"FILE" = "FILE" + +[enum] +prefix_with_name = true +rename_variants = "ScreamingSnakeCase" + +[fn] +must_use = "DDOG_CHECK_RETURN" + +[parse] +parse_deps = true +include = ["ddcommon", "ddcommon-ffi"] diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs new file mode 100644 index 0000000000..6e4401f462 --- /dev/null +++ b/library-config-ffi/src/lib.rs @@ -0,0 +1,66 @@ +use std::path::PathBuf; + +use ddcommon_ffi as ffi; + +#[repr(C)] +pub struct ProcessInfo<'a> { + pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, + pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, + language: ffi::CharSlice<'a>, +} + +#[repr(C)] +pub enum Value { + NumVal(i64), + BoolVal(bool), + StrVal(ffi::StringWrapper), +} + +#[repr(C)] +pub enum ConfigName { + DdTraceDebug = 0, +} + +#[repr(C)] +pub struct Config { + pub name: ConfigName, + pub value: Value, +} + +#[derive(Debug)] +pub struct Configurator { + debug_logs: bool, + #[allow(dead_code)] + static_config_file_path: PathBuf, +} + +#[no_mangle] +pub extern "C" fn ddog_library_config_new(debug_logs: bool) -> Box { + Box::new(Configurator { + debug_logs, + static_config_file_path: PathBuf::from( + "/etc/datadog-agent/managed/datadog-apm-libraries/st", + ), + }) +} + +#[no_mangle] +pub extern "C" fn ddog_library_config_drop(_: Box) {} + +#[no_mangle] +pub extern "C" fn ddog_library_config_get<'a>( + configurator: &'a Configurator, + process_info: ProcessInfo<'a>, +) -> ffi::Vec { + if configurator.debug_logs { + println!("Called library_config_common_component:"); + println!("\tconfigurator: {:?}", configurator); + println!("\tprocess args: {:?}", process_info.args); + // TODO: this is for testing purpose, we don't want to log env variables + println!("\tprocess envs: {:?}", process_info.args); + } + ffi::Vec::from(vec![Config { + name: ConfigName::DdTraceDebug, + value: Value::BoolVal(true), + }]) +} From a59b1863852e63abf946767242b14dc474cde237 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Mon, 23 Dec 2024 17:29:05 +0100 Subject: [PATCH 02/26] Add examples --- examples/library-config-ffi/config.c | 28 +++++++++++++++++++ examples/{ffi => profiling-ffi}/.gitignore | 0 .../{ffi => profiling-ffi}/CMakeLists.txt | 0 .../{ffi => profiling-ffi}/array_queue.cpp | 0 examples/{ffi => profiling-ffi}/crashinfo.cpp | 0 .../{ffi => profiling-ffi}/crashtracking.c | 0 examples/{ffi => profiling-ffi}/exporter.cpp | 0 examples/{ffi => profiling-ffi}/profiles.c | 0 .../{ffi => profiling-ffi}/symbolizer.cpp | 0 examples/{ffi => profiling-ffi}/telemetry.c | 0 .../telemetry_metrics.c | 0 .../{ffi => profiling-ffi}/trace_exporter.c | 0 12 files changed, 28 insertions(+) create mode 100644 examples/library-config-ffi/config.c rename examples/{ffi => profiling-ffi}/.gitignore (100%) rename examples/{ffi => profiling-ffi}/CMakeLists.txt (100%) rename examples/{ffi => profiling-ffi}/array_queue.cpp (100%) rename examples/{ffi => profiling-ffi}/crashinfo.cpp (100%) rename examples/{ffi => profiling-ffi}/crashtracking.c (100%) rename examples/{ffi => profiling-ffi}/exporter.cpp (100%) rename examples/{ffi => profiling-ffi}/profiles.c (100%) rename examples/{ffi => profiling-ffi}/symbolizer.cpp (100%) rename examples/{ffi => profiling-ffi}/telemetry.c (100%) rename examples/{ffi => profiling-ffi}/telemetry_metrics.c (100%) rename examples/{ffi => profiling-ffi}/trace_exporter.c (100%) diff --git a/examples/library-config-ffi/config.c b/examples/library-config-ffi/config.c new file mode 100644 index 0000000000..9043e7c5ee --- /dev/null +++ b/examples/library-config-ffi/config.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#define DDOG_VAL_STR_PTR(val) (val.tag == DDOG_VALUE_STR_VAL ? val.str_val.message.ptr : NULL) + +#define DDOG_VAL_STR_LEN(val) (val.tag == DDOG_VALUE_STR_VAL ? val.str_val.message.len : 0) + +#define DDOG_SLICE_CHARSLICE(arr) \ + ((ddog_Slice_CharSlice){.ptr = arr, .len = sizeof(arr) / sizeof(arr[0])}) + +int main(int argc, const char *const *argv) { + ddog_Configurator *configurator = ddog_library_config_new(true); + ddog_CharSlice args[] = { + DDOG_CHARSLICE_C("/bin/true"), + }; + ddog_CharSlice envp[] = { + DDOG_CHARSLICE_C("FOO=BAR"), + }; + ddog_Vec_Config configs = ddog_library_config_get( + configurator, (ddog_ProcessInfo){.args = DDOG_SLICE_CHARSLICE(args), + .envp = DDOG_SLICE_CHARSLICE(envp), + .language = DDOG_CHARSLICE_C("java")}); + for (int i = 0; i < configs.len; i++) { + printf("%d %*.s\n", configs.ptr[i].name, (int)DDOG_VAL_STR_LEN(configs.ptr[i].value), + DDOG_VAL_STR_PTR(configs.ptr[i].value)); + } +} \ No newline at end of file diff --git a/examples/ffi/.gitignore b/examples/profiling-ffi/.gitignore similarity index 100% rename from examples/ffi/.gitignore rename to examples/profiling-ffi/.gitignore diff --git a/examples/ffi/CMakeLists.txt b/examples/profiling-ffi/CMakeLists.txt similarity index 100% rename from examples/ffi/CMakeLists.txt rename to examples/profiling-ffi/CMakeLists.txt diff --git a/examples/ffi/array_queue.cpp b/examples/profiling-ffi/array_queue.cpp similarity index 100% rename from examples/ffi/array_queue.cpp rename to examples/profiling-ffi/array_queue.cpp diff --git a/examples/ffi/crashinfo.cpp b/examples/profiling-ffi/crashinfo.cpp similarity index 100% rename from examples/ffi/crashinfo.cpp rename to examples/profiling-ffi/crashinfo.cpp diff --git a/examples/ffi/crashtracking.c b/examples/profiling-ffi/crashtracking.c similarity index 100% rename from examples/ffi/crashtracking.c rename to examples/profiling-ffi/crashtracking.c diff --git a/examples/ffi/exporter.cpp b/examples/profiling-ffi/exporter.cpp similarity index 100% rename from examples/ffi/exporter.cpp rename to examples/profiling-ffi/exporter.cpp diff --git a/examples/ffi/profiles.c b/examples/profiling-ffi/profiles.c similarity index 100% rename from examples/ffi/profiles.c rename to examples/profiling-ffi/profiles.c diff --git a/examples/ffi/symbolizer.cpp b/examples/profiling-ffi/symbolizer.cpp similarity index 100% rename from examples/ffi/symbolizer.cpp rename to examples/profiling-ffi/symbolizer.cpp diff --git a/examples/ffi/telemetry.c b/examples/profiling-ffi/telemetry.c similarity index 100% rename from examples/ffi/telemetry.c rename to examples/profiling-ffi/telemetry.c diff --git a/examples/ffi/telemetry_metrics.c b/examples/profiling-ffi/telemetry_metrics.c similarity index 100% rename from examples/ffi/telemetry_metrics.c rename to examples/profiling-ffi/telemetry_metrics.c diff --git a/examples/ffi/trace_exporter.c b/examples/profiling-ffi/trace_exporter.c similarity index 100% rename from examples/ffi/trace_exporter.c rename to examples/profiling-ffi/trace_exporter.c From 37430399a8ae67c5a2306810a554001ae730e660 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Mon, 23 Dec 2024 18:58:53 +0100 Subject: [PATCH 03/26] Add library-config feature to profiling artifact build --- build-profiling-ffi.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-profiling-ffi.sh b/build-profiling-ffi.sh index 46094b4d13..12a6497fb3 100755 --- a/build-profiling-ffi.sh +++ b/build-profiling-ffi.sh @@ -230,7 +230,9 @@ mkdir $destdir/include/datadog/ CBINDGEN_HEADERS="common.h profiling.h telemetry.h crashtracker.h data-pipeline.h" # When optional features are added, don't forget to also include thei headers here case $ARG_FEATURES in + datadog-library-config-ffi) CBINDGEN_HEADERS="$CBINDGEN_HEADERS library-config.h" esac + CBINDGEN_HEADERS_DESTS="" for header in $CBINDGEN_HEADERS; do HEADER_DEST="$destdir/include/datadog/$header" From ba251306b06c7ab12240cf6565c32f0f02f119fb Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Mon, 23 Dec 2024 18:59:13 +0100 Subject: [PATCH 04/26] fix function prefix --- library-config-ffi/cbindgen.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library-config-ffi/cbindgen.toml b/library-config-ffi/cbindgen.toml index 822d5f53fe..a47ba9975e 100644 --- a/library-config-ffi/cbindgen.toml +++ b/library-config-ffi/cbindgen.toml @@ -10,9 +10,13 @@ include_guard = "DDOG_LIBRARY_CONFIG_H" style = "both" no_includes = true -sys_includes = ["stdbool.h", "stddef.h", "stdint.h", "stdio.h"] includes = ["common.h"] +[export] +prefix = "ddog_" +renaming_overrides_prefixing = true + + [export.mangle] rename_types = "PascalCase" From cbde4badb99f147c3e236fa68ac07c03d5b0cd90 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 24 Dec 2024 16:38:29 +0100 Subject: [PATCH 05/26] build profiling ffi also commit cargo toml --- Cargo.lock | 23 +++++++++++++++++++++++ library-config-ffi/Cargo.toml | 5 ++++- profiling-ffi/Cargo.toml | 2 ++ profiling-ffi/src/lib.rs | 4 ++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fd6da6815d..7a0a1a29c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,9 +1446,12 @@ dependencies = [ name = "datadog-library-config-ffi" version = "0.0.1" dependencies = [ + "anyhow", "build_common", "ddcommon", "ddcommon-ffi", + "serde", + "serde_yaml", ] [[package]] @@ -1533,6 +1536,7 @@ dependencies = [ "build_common", "data-pipeline-ffi", "datadog-crashtracker-ffi", + "datadog-library-config-ffi", "datadog-profiling", "ddcommon", "ddcommon-ffi", @@ -5017,6 +5021,19 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.6.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -6089,6 +6106,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/library-config-ffi/Cargo.toml b/library-config-ffi/Cargo.toml index 158dbd2bda..eb0c03e878 100644 --- a/library-config-ffi/Cargo.toml +++ b/library-config-ffi/Cargo.toml @@ -7,12 +7,15 @@ version = "0.0.1" edition = "2021" [lib] -crate-type = ["staticlib", "cdylib"] +crate-type = ["staticlib", "cdylib", "lib"] bench = false [dependencies] ddcommon = { path = "../ddcommon" } ddcommon-ffi = { path = "../ddcommon-ffi", default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9.34" +anyhow = "1.0" [features] default = ["cbindgen"] diff --git a/profiling-ffi/Cargo.toml b/profiling-ffi/Cargo.toml index 2e1e62c5f5..570a68e395 100644 --- a/profiling-ffi/Cargo.toml +++ b/profiling-ffi/Cargo.toml @@ -26,6 +26,7 @@ crashtracker-collector = ["crashtracker-ffi", "datadog-crashtracker-ffi/collecto # Enables the use of this library to receiver crash-info from a suitable collector crashtracker-receiver = ["crashtracker-ffi", "datadog-crashtracker-ffi/receiver"] demangler = ["crashtracker-ffi", "datadog-crashtracker-ffi/demangler"] +datadog-library-config-ffi = ["dep:datadog-library-config-ffi"] [build-dependencies] build_common = { path = "../build-common" } @@ -46,3 +47,4 @@ symbolizer-ffi = { path = "../symbolizer-ffi", optional = true, default-features symbolic-demangle = { version = "12.8.0", default-features = false, features = ["rust", "cpp", "msvc"] } symbolic-common = "12.8.0" data-pipeline-ffi = { path = "../data-pipeline-ffi", default-features = false, optional = true } +datadog-library-config-ffi = { path = "../library-config-ffi", default-features = false, optional = true } diff --git a/profiling-ffi/src/lib.rs b/profiling-ffi/src/lib.rs index f254814478..6cb53579ff 100644 --- a/profiling-ffi/src/lib.rs +++ b/profiling-ffi/src/lib.rs @@ -18,3 +18,7 @@ pub use ddtelemetry_ffi::*; #[cfg(feature = "data-pipeline-ffi")] #[allow(unused_imports)] pub use data_pipeline_ffi::*; + +// re-export crashtracker ffi +#[cfg(feature = "datadog-library-config-ffi")] +pub use datadog_library_config_ffi::*; From c840768e4d0c62efb567f3b92c7e8c934342848f Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Thu, 26 Dec 2024 15:03:53 +0100 Subject: [PATCH 06/26] Add static config deserialzation --- Cargo.lock | 1 + ddcommon-ffi/src/cstr.rs | 102 ++++++++++ ddcommon-ffi/src/lib.rs | 2 + ddcommon-ffi/src/vec.rs | 28 ++- examples/library-config-ffi/config.c | 31 +++- examples/profiling-ffi/CMakeLists.txt | 4 + examples/profiling-ffi/library_config.c | 46 +++++ library-config-ffi/Cargo.toml | 3 + library-config-ffi/src/lib.rs | 88 +++++---- library-config-ffi/src/static_config.rs | 236 ++++++++++++++++++++++++ 10 files changed, 478 insertions(+), 63 deletions(-) create mode 100644 ddcommon-ffi/src/cstr.rs create mode 100644 examples/profiling-ffi/library_config.c create mode 100644 library-config-ffi/src/static_config.rs diff --git a/Cargo.lock b/Cargo.lock index 7a0a1a29c4..02617d0c9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1452,6 +1452,7 @@ dependencies = [ "ddcommon-ffi", "serde", "serde_yaml", + "tempfile", ] [[package]] diff --git a/ddcommon-ffi/src/cstr.rs b/ddcommon-ffi/src/cstr.rs new file mode 100644 index 0000000000..b60d84fb04 --- /dev/null +++ b/ddcommon-ffi/src/cstr.rs @@ -0,0 +1,102 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt; +use std::{ + ffi::c_char, + marker::PhantomData, + mem::{self, ManuallyDrop}, + ptr::{self, NonNull}, +}; + +/// Ffi safe type representing a borrowed null-terminated C array +/// Equivalent to a std::ffi::CStr +#[repr(C)] +pub struct CStr<'a> { + /// Null terminated char array + ptr: ptr::NonNull, + /// Length of the array, not counting the null-terminator + length: usize, + _lifetime_marker: std::marker::PhantomData<&'a c_char>, +} + +impl<'a> CStr<'a> { + pub fn from_std(s: &'a std::ffi::CStr) -> Self { + Self { + ptr: unsafe { ptr::NonNull::new_unchecked(s.as_ptr().cast_mut()) }, + length: s.to_bytes().len() - 1, + _lifetime_marker: std::marker::PhantomData, + } + } + + pub fn into_std(&self) -> &'a std::ffi::CStr { + unsafe { + std::ffi::CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts( + self.ptr.as_ptr().cast_const().cast(), + self.length, + )) + } + } +} + +/// Ffi safe type representing an owned null-terminated C array +/// Equivalent to a std::ffi::CString +#[repr(C)] +pub struct CString { + /// Null terminated char array + ptr: ptr::NonNull, + /// Length of the array, not counting the null-terminator + length: usize, +} + +impl fmt::Debug for CString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_cstr().into_std().fmt(f) + } +} + +impl CString { + pub fn new>>(t: T) -> Result { + Ok(Self::from_std(std::ffi::CString::new(t)?)) + } + + pub fn as_cstr<'a>(&'a self) -> CStr<'a> { + CStr { + ptr: self.ptr, + length: self.length, + _lifetime_marker: PhantomData, + } + } + + pub fn from_std(s: std::ffi::CString) -> Self { + let s = ManuallyDrop::new(s); + Self { + ptr: unsafe { ptr::NonNull::new_unchecked(s.as_ptr().cast_mut()) }, + length: s.to_bytes().len(), + } + } + + pub fn into_std(self) -> std::ffi::CString { + let s = ManuallyDrop::new(self); + unsafe { + std::ffi::CString::from_vec_with_nul_unchecked(Vec::from_raw_parts( + s.ptr.as_ptr().cast(), + s.length, + s.length, + )) + } + } +} + +impl Drop for CString { + fn drop(&mut self) { + let ptr = mem::replace(&mut self.ptr, NonNull::dangling()); + drop(unsafe { + std::ffi::CString::from_vec_with_nul_unchecked(Vec::from_raw_parts( + ptr.as_ptr().cast(), + self.length, + self.length, + )) + }); + } +} diff --git a/ddcommon-ffi/src/lib.rs b/ddcommon-ffi/src/lib.rs index 98d59957fc..2864860b14 100644 --- a/ddcommon-ffi/src/lib.rs +++ b/ddcommon-ffi/src/lib.rs @@ -14,6 +14,7 @@ pub mod tags; pub mod timespec; pub mod utils; pub mod vec; +pub mod cstr; pub use error::*; pub use handle::*; @@ -23,3 +24,4 @@ pub use slice::{CharSlice, Slice}; pub use string::*; pub use timespec::*; pub use vec::Vec; +pub use cstr::*; diff --git a/ddcommon-ffi/src/vec.rs b/ddcommon-ffi/src/vec.rs index 147bc5d39d..60a0893227 100644 --- a/ddcommon-ffi/src/vec.rs +++ b/ddcommon-ffi/src/vec.rs @@ -23,6 +23,23 @@ pub struct Vec { _marker: PhantomData, } +impl Vec { + pub fn from_std(vec: alloc::vec::Vec) -> Self { + let mut v = ManuallyDrop::new(vec); + Self { + ptr: v.as_mut_ptr(), + len: v.len(), + capacity: v.capacity(), + _marker: PhantomData, + } + } + + pub fn into_std(self) -> alloc::vec::Vec { + let v = ManuallyDrop::new(self); + unsafe { alloc::vec::Vec::from_raw_parts(v.ptr.cast_mut(), v.len, v.capacity) } + } +} + unsafe impl Send for Vec {} unsafe impl Sync for Vec {} @@ -45,20 +62,13 @@ impl Drop for Vec { impl From> for alloc::vec::Vec { fn from(vec: Vec) -> Self { - let v = ManuallyDrop::new(vec); - unsafe { alloc::vec::Vec::from_raw_parts(v.ptr as *mut T, v.len, v.capacity) } + vec.into_std() } } impl From> for Vec { fn from(vec: alloc::vec::Vec) -> Self { - let mut v = ManuallyDrop::new(vec); - Self { - ptr: v.as_mut_ptr(), - len: v.len(), - capacity: v.capacity(), - _marker: PhantomData, - } + Self::from_std(vec) } } diff --git a/examples/library-config-ffi/config.c b/examples/library-config-ffi/config.c index 9043e7c5ee..86d0dad16e 100644 --- a/examples/library-config-ffi/config.c +++ b/examples/library-config-ffi/config.c @@ -1,28 +1,43 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + #include #include #include +#include -#define DDOG_VAL_STR_PTR(val) (val.tag == DDOG_VALUE_STR_VAL ? val.str_val.message.ptr : NULL) - -#define DDOG_VAL_STR_LEN(val) (val.tag == DDOG_VALUE_STR_VAL ? val.str_val.message.len : 0) +#define DDOG_VAL_STR_PTR(val) \ + (val.tag == DDOG_LIBRARY_CONFIG_VALUE_STR_VAL ? val.str_val.ptr : "\0") #define DDOG_SLICE_CHARSLICE(arr) \ ((ddog_Slice_CharSlice){.ptr = arr, .len = sizeof(arr) / sizeof(arr[0])}) int main(int argc, const char *const *argv) { - ddog_Configurator *configurator = ddog_library_config_new(true); + ddog_Configurator *configurator = ddog_library_configurator_new(true); + ddog_library_configurator_with_path(configurator, + DDOG_CHARSLICE_C("/tmp/foobar/static_config.yaml")); + ddog_CharSlice args[] = { DDOG_CHARSLICE_C("/bin/true"), }; ddog_CharSlice envp[] = { DDOG_CHARSLICE_C("FOO=BAR"), }; - ddog_Vec_Config configs = ddog_library_config_get( + ddog_Result_VecLibraryConfig config_result = ddog_library_configurator_get( configurator, (ddog_ProcessInfo){.args = DDOG_SLICE_CHARSLICE(args), .envp = DDOG_SLICE_CHARSLICE(envp), .language = DDOG_CHARSLICE_C("java")}); + if (config_result.tag == DDOG_RESULT_VEC_LIBRARY_CONFIG_ERR_VEC_LIBRARY_CONFIG) { + ddog_Error err = config_result.err; + fprintf(stderr, "%.*s", (int)err.message.len, err.message.ptr); + ddog_Error_drop(&err); + exit(1); + } + + ddog_Vec_LibraryConfig configs = config_result.ok; for (int i = 0; i < configs.len; i++) { - printf("%d %*.s\n", configs.ptr[i].name, (int)DDOG_VAL_STR_LEN(configs.ptr[i].value), - DDOG_VAL_STR_PTR(configs.ptr[i].value)); + const ddog_LibraryConfig *cfg = &configs.ptr[i]; + ddog_CStr name = ddog_library_config_name_to_env(cfg->name); + printf("%s %s\n", name.ptr, DDOG_VAL_STR_PTR(cfg->value)); } -} \ No newline at end of file +} diff --git a/examples/profiling-ffi/CMakeLists.txt b/examples/profiling-ffi/CMakeLists.txt index b7b7150745..ee28432b7b 100644 --- a/examples/profiling-ffi/CMakeLists.txt +++ b/examples/profiling-ffi/CMakeLists.txt @@ -66,3 +66,7 @@ add_executable(array_queue array_queue.cpp) target_compile_features(array_queue PRIVATE cxx_std_20) target_link_libraries(array_queue PRIVATE Datadog::Profiling) set_vcruntime_link_type(array_queue ${VCRUNTIME_LINK_TYPE}) + +add_executable(library_config library_config.c) +target_link_libraries(library_config PRIVATE Datadog::Profiling) +set_vcruntime_link_type(library_config ${VCRUNTIME_LINK_TYPE}) diff --git a/examples/profiling-ffi/library_config.c b/examples/profiling-ffi/library_config.c new file mode 100644 index 0000000000..442c97bed2 --- /dev/null +++ b/examples/profiling-ffi/library_config.c @@ -0,0 +1,46 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +#include <_stdlib.h> +#include +#include +#include +#include + +#define DDOG_VAL_STR_PTR(val) \ + (val.tag == DDOG_LIBRARY_CONFIG_VALUE_STR_VAL ? val.str_val.ptr : "\0") + +#define DDOG_SLICE_CHARSLICE(arr) \ + ((ddog_Slice_CharSlice){.ptr = arr, .len = sizeof(arr) / sizeof(arr[0])}) + +int main(int argc, const char *const *argv) { + ddog_Configurator *configurator = ddog_library_configurator_new(true); + ddog_library_configurator_with_path(configurator, + DDOG_CHARSLICE_C("/tmp/foobar/static_config.yaml")); + + ddog_CharSlice args[] = { + DDOG_CHARSLICE_C("/bin/true"), + }; + ddog_CharSlice envp[] = { + DDOG_CHARSLICE_C("FOO=BAR"), + }; + ddog_Result_VecLibraryConfig config_result = ddog_library_configurator_get( + configurator, (ddog_ProcessInfo){.args = DDOG_SLICE_CHARSLICE(args), + .envp = DDOG_SLICE_CHARSLICE(envp), + .language = DDOG_CHARSLICE_C("java")}); + if (config_result.tag == DDOG_RESULT_VEC_LIBRARY_CONFIG_ERR_VEC_LIBRARY_CONFIG) { + ddog_Error err = config_result.err; + fprintf(stderr, "%.*s", (int)err.message.len, err.message.ptr); + ddog_Error_drop(&err); + exit(1); + } + + ddog_Vec_LibraryConfig configs = config_result.ok; + for (int i = 0; i < configs.len; i++) { + const ddog_LibraryConfig *cfg = &configs.ptr[i]; + ddog_CStr name = ddog_library_config_name_to_env(cfg->name); + + printf("Setting env variable: %s=%s\n", name.ptr, DDOG_VAL_STR_PTR(cfg->value)); + setenv(name.ptr, DDOG_VAL_STR_PTR(cfg->value), 1); + } +} diff --git a/library-config-ffi/Cargo.toml b/library-config-ffi/Cargo.toml index eb0c03e878..f9bdbc68aa 100644 --- a/library-config-ffi/Cargo.toml +++ b/library-config-ffi/Cargo.toml @@ -23,3 +23,6 @@ cbindgen = ["build_common/cbindgen", "ddcommon-ffi/cbindgen"] [build-dependencies] build_common = { path = "../build-common" } + +[dev-dependencies] +tempfile = {version = "3.3"} diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 6e4401f462..8b67c007b1 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -1,66 +1,62 @@ -use std::path::PathBuf; +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 -use ddcommon_ffi as ffi; +mod static_config; -#[repr(C)] -pub struct ProcessInfo<'a> { - pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, - pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, - language: ffi::CharSlice<'a>, -} +use std::path::PathBuf; -#[repr(C)] -pub enum Value { - NumVal(i64), - BoolVal(bool), - StrVal(ffi::StringWrapper), -} +use ddcommon_ffi::{self as ffi, slice::AsBytes}; +use static_config::{Configurator, LibraryConfig, LibraryConfigName, ProcessInfo}; -#[repr(C)] -pub enum ConfigName { - DdTraceDebug = 0, -} +// TODO: Centos 6 build +// Trust me it works bro 😉😉😉 +// #[cfg(linux)] +// std::arch::global_asm!(".symver memcpy,memcpy@GLIBC_2.2.5"); -#[repr(C)] -pub struct Config { - pub name: ConfigName, - pub value: Value, -} -#[derive(Debug)] -pub struct Configurator { - debug_logs: bool, - #[allow(dead_code)] - static_config_file_path: PathBuf, +#[no_mangle] +pub extern "C" fn ddog_library_configurator_new(debug_logs: bool) -> Box { + Box::new(Configurator::new( + debug_logs, + PathBuf::from("/etc/datadog-agent/managed/datadog-apm-libraries/static_config.yaml"), + )) } #[no_mangle] -pub extern "C" fn ddog_library_config_new(debug_logs: bool) -> Box { - Box::new(Configurator { - debug_logs, - static_config_file_path: PathBuf::from( - "/etc/datadog-agent/managed/datadog-apm-libraries/st", - ), - }) +/// Sets the path at which we will read the static configuration file. +/// This should mainly be used for testing +pub extern "C" fn ddog_library_configurator_with_path( + configurator: &mut Configurator, + p: ffi::CharSlice, +) { + configurator.static_config_file_path = PathBuf::from(p.to_utf8_lossy().into_owned()); } #[no_mangle] -pub extern "C" fn ddog_library_config_drop(_: Box) {} +pub extern "C" fn ddog_library_configurator_drop(_: Box) {} #[no_mangle] -pub extern "C" fn ddog_library_config_get<'a>( +pub extern "C" fn ddog_library_configurator_get<'a>( configurator: &'a Configurator, process_info: ProcessInfo<'a>, -) -> ffi::Vec { +) -> ffi::Result> { if configurator.debug_logs { - println!("Called library_config_common_component:"); - println!("\tconfigurator: {:?}", configurator); - println!("\tprocess args: {:?}", process_info.args); + eprintln!("Called library_config_common_component:"); + eprintln!("\tconfigurator: {:?}", configurator); + eprintln!("\tprocess args: {:?}", process_info.args); // TODO: this is for testing purpose, we don't want to log env variables - println!("\tprocess envs: {:?}", process_info.args); + eprintln!("\tprocess envs: {:?}", process_info.args); } - ffi::Vec::from(vec![Config { - name: ConfigName::DdTraceDebug, - value: Value::BoolVal(true), - }]) + configurator + .get_configuration(process_info) + .map(ffi::Vec::from_std) + .into() } + +#[no_mangle] +pub extern "C" fn ddog_library_config_name_to_env(name: LibraryConfigName) -> ffi::CStr<'static> { + ffi::CStr::from_std(name.to_env_name()) +} + +#[no_mangle] +pub extern "C" fn ddog_library_config_drop(_: ffi::Vec) {} diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs new file mode 100644 index 0000000000..36f99729b5 --- /dev/null +++ b/library-config-ffi/src/static_config.rs @@ -0,0 +1,236 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::anyhow; +use ddcommon::cstr; +use ddcommon_ffi::{self as ffi}; +use std::{collections::HashMap, io::ErrorKind, path::PathBuf}; + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +enum Origin { + ProcessArguments, + EnvironmentVariable, + Language, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +enum Operator { + Equals, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +struct Selector { + origin: Origin, + matches: Vec, + operator: Operator, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +struct Rule { + selectors: Vec, + configuration: HashMap, +} + +#[derive(serde::Deserialize, Default, Debug, PartialEq, Eq)] +struct StaticConfig { + rules: Vec, +} + +fn find_static_config<'a, 'b>( + cfg: &'a StaticConfig, + process_info: &ProcessInfo<'b>, +) -> Option<&'a HashMap> { + for rule in &cfg.rules { + if rule + .selectors + .iter() + .all(|s| selector_match(&s, process_info)) + { + return Some(&rule.configuration); + } + } + None +} + +fn selector_match(selector: &Selector, process_info: &ProcessInfo) -> bool { + // todo: match selectors + true +} + +fn template_configs( + config: &HashMap, + process_info: &ProcessInfo, +) -> anyhow::Result> { + config + .iter() + .map(|(&name, v)| { + Ok(LibraryConfig { + name: name, + value: LibraryConfigValue::StrVal(ffi::CString::new(template_config( + v, + process_info, + ))?), + }) + }) + .collect() +} + +fn template_config(config_val: &str, process_info: &ProcessInfo) -> String { + // todo: template configuration + config_val.to_owned() +} + +#[derive(Debug)] +pub struct Configurator { + pub debug_logs: bool, + #[allow(dead_code)] + pub static_config_file_path: PathBuf, +} + +#[repr(C)] +#[derive(Clone, Copy, serde::Deserialize, Debug, PartialEq, Eq, Hash)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum LibraryConfigName { + DdTraceDebug = 0, + DdProfilingEnabled = 1, +} + +impl LibraryConfigName { + pub fn to_env_name(self) -> &'static std::ffi::CStr { + use LibraryConfigName::*; + match self { + DdTraceDebug => cstr!("DD_TRACE_DEBUG"), + DdProfilingEnabled => cstr!("DD_PROFILING_ENABLED"), + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub enum LibraryConfigValue { + NumVal(i64), + BoolVal(bool), + StrVal(ffi::CString), +} + +#[repr(C)] +#[derive(Debug)] +pub struct LibraryConfig { + pub name: LibraryConfigName, + pub value: LibraryConfigValue, +} + +#[repr(C)] +pub struct ProcessInfo<'a> { + pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, + pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, + language: ffi::CharSlice<'a>, +} + +impl Configurator { + pub fn new(debug_logs: bool, static_config_file_path: PathBuf) -> Self { + Self { + debug_logs, + static_config_file_path, + } + } + + pub fn get_configuration<'a>( + &self, + process_info: ProcessInfo<'a>, + ) -> anyhow::Result> { + let static_config = self.parse_static_config()?; + if self.debug_logs { + eprintln!("Read the following static config: {static_config:?}"); + } + + let Some(configs) = find_static_config(&static_config, &process_info) else { + if self.debug_logs { + eprintln!("No selector matched"); + } + return Ok(Vec::new()); + }; + let library_config = template_configs(configs, &process_info)?; + if self.debug_logs { + eprintln!("Would apply the following configuration: {library_config:?}"); + } + Ok(vec![LibraryConfig { + name: LibraryConfigName::DdTraceDebug, + value: LibraryConfigValue::BoolVal(true), + }]) + } + + fn parse_static_config(&self) -> anyhow::Result { + let mut f = match std::fs::File::open(&self.static_config_file_path) { + Ok(f) => f, + Err(e) if e.kind() == ErrorKind::NotFound => { + if self.debug_logs { + eprintln!( + "Static config file not found at {:?} returning empty rules", + self.static_config_file_path + ); + } + return Ok(StaticConfig::default()); + } + Err(e) => return Err(anyhow!(e)), + }; + Ok(serde_yaml::from_reader(&mut f)?) + } +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use crate::static_config::{LibraryConfigName, Operator, Origin, Rule, Selector, StaticConfig}; + + use super::Configurator; + + macro_rules! map { + ($(($key:expr , $value:expr)),* $(,)?) => { + { + #[allow(unused_mut)] + let mut map = std::collections::HashMap::new(); + $( + map.insert($key, $value); + )* + map + } + }; + } + + #[test] + fn test_parse_static_config() { + let mut tmp = tempfile::NamedTempFile::new().unwrap(); + tmp.as_file_mut() + .write_all( + b" +rules: +- selectors: + - origin: language + matches: [\"java\"] + operator: equals + configuration: + DD_PROFILING_ENABLED: true +", + ) + .unwrap(); + let confgurator = Configurator::new(true, tmp.path().to_path_buf()); + let cfg = confgurator.parse_static_config().unwrap(); + assert_eq!( + cfg, + StaticConfig { + rules: vec![Rule { + selectors: vec![Selector { + origin: Origin::Language, + matches: vec!["java".to_owned()], + operator: Operator::Equals, + }], + configuration: map![(LibraryConfigName::DdProfilingEnabled, "true".to_owned()),], + }] + } + ) + } +} From 785bee2c583ecfba800285f4f29c4bf212103e63 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Fri, 27 Dec 2024 15:10:21 +0100 Subject: [PATCH 07/26] fix(library_config): Generate for CPP (Python) + small fixes --- library-config-ffi/cbindgen.toml | 2 ++ library-config-ffi/src/lib.rs | 9 ++++++--- library-config-ffi/src/static_config.rs | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/library-config-ffi/cbindgen.toml b/library-config-ffi/cbindgen.toml index a47ba9975e..75ce245648 100644 --- a/library-config-ffi/cbindgen.toml +++ b/library-config-ffi/cbindgen.toml @@ -2,12 +2,14 @@ # SPDX-License-Identifier: Apache-2.0 language = "C" +cpp_compat = true tab_width = 2 header = """// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 """ include_guard = "DDOG_LIBRARY_CONFIG_H" style = "both" +pragma_once = true no_includes = true includes = ["common.h"] diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 8b67c007b1..4f410a83c1 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -18,7 +18,7 @@ use static_config::{Configurator, LibraryConfig, LibraryConfigName, ProcessInfo} pub extern "C" fn ddog_library_configurator_new(debug_logs: bool) -> Box { Box::new(Configurator::new( debug_logs, - PathBuf::from("/etc/datadog-agent/managed/datadog-apm-libraries/static_config.yaml"), + PathBuf::from("/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml"), )) } @@ -43,9 +43,12 @@ pub extern "C" fn ddog_library_configurator_get<'a>( if configurator.debug_logs { eprintln!("Called library_config_common_component:"); eprintln!("\tconfigurator: {:?}", configurator); - eprintln!("\tprocess args: {:?}", process_info.args); + let args: Vec = process_info.args.iter().map(|arg| arg.to_utf8_lossy().into_owned()).collect(); + eprintln!("\tprocess args: {:?}", args); // TODO: this is for testing purpose, we don't want to log env variables - eprintln!("\tprocess envs: {:?}", process_info.args); + let envs: Vec = process_info.envp.iter().map(|env| env.to_utf8_lossy().into_owned()).collect(); + eprintln!("\tprocess envs: {:?}", envs); + eprintln!("\tprocess language: {:?}", process_info.language.to_utf8_lossy().into_owned()); } configurator .get_configuration(process_info) diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs index 36f99729b5..a9aca529fe 100644 --- a/library-config-ffi/src/static_config.rs +++ b/library-config-ffi/src/static_config.rs @@ -126,7 +126,7 @@ pub struct LibraryConfig { pub struct ProcessInfo<'a> { pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, - language: ffi::CharSlice<'a>, + pub language: ffi::CharSlice<'a>, } impl Configurator { @@ -154,7 +154,7 @@ impl Configurator { }; let library_config = template_configs(configs, &process_info)?; if self.debug_logs { - eprintln!("Would apply the following configuration: {library_config:?}"); + eprintln!("Will apply the following configuration: {library_config:?}"); } Ok(vec![LibraryConfig { name: LibraryConfigName::DdTraceDebug, From 83affc348818f27f537fc4490ca11ce1dd1304a9 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Mon, 30 Dec 2024 09:54:47 +0100 Subject: [PATCH 08/26] add partial process matching --- ddcommon-ffi/src/cstr.rs | 2 +- ddcommon-ffi/src/lib.rs | 4 +- library-config-ffi/src/lib.rs | 22 +++-- library-config-ffi/src/static_config.rs | 102 +++++++++++++++++++----- 4 files changed, 103 insertions(+), 27 deletions(-) diff --git a/ddcommon-ffi/src/cstr.rs b/ddcommon-ffi/src/cstr.rs index b60d84fb04..adc7bffdc2 100644 --- a/ddcommon-ffi/src/cstr.rs +++ b/ddcommon-ffi/src/cstr.rs @@ -60,7 +60,7 @@ impl CString { Ok(Self::from_std(std::ffi::CString::new(t)?)) } - pub fn as_cstr<'a>(&'a self) -> CStr<'a> { + pub fn as_cstr(&self) -> CStr<'_> { CStr { ptr: self.ptr, length: self.length, diff --git a/ddcommon-ffi/src/lib.rs b/ddcommon-ffi/src/lib.rs index 2864860b14..e9e260301f 100644 --- a/ddcommon-ffi/src/lib.rs +++ b/ddcommon-ffi/src/lib.rs @@ -4,6 +4,7 @@ mod error; pub mod array_queue; +pub mod cstr; pub mod endpoint; pub mod handle; pub mod option; @@ -14,8 +15,8 @@ pub mod tags; pub mod timespec; pub mod utils; pub mod vec; -pub mod cstr; +pub use cstr::*; pub use error::*; pub use handle::*; pub use option::*; @@ -24,4 +25,3 @@ pub use slice::{CharSlice, Slice}; pub use string::*; pub use timespec::*; pub use vec::Vec; -pub use cstr::*; diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 4f410a83c1..851258e3b4 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -13,12 +13,13 @@ use static_config::{Configurator, LibraryConfig, LibraryConfigName, ProcessInfo} // #[cfg(linux)] // std::arch::global_asm!(".symver memcpy,memcpy@GLIBC_2.2.5"); - #[no_mangle] pub extern "C" fn ddog_library_configurator_new(debug_logs: bool) -> Box { Box::new(Configurator::new( debug_logs, - PathBuf::from("/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml"), + PathBuf::from( + "/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml", + ), )) } @@ -43,12 +44,23 @@ pub extern "C" fn ddog_library_configurator_get<'a>( if configurator.debug_logs { eprintln!("Called library_config_common_component:"); eprintln!("\tconfigurator: {:?}", configurator); - let args: Vec = process_info.args.iter().map(|arg| arg.to_utf8_lossy().into_owned()).collect(); + let args: Vec = process_info + .args + .iter() + .map(|arg| arg.to_string()) + .collect(); eprintln!("\tprocess args: {:?}", args); // TODO: this is for testing purpose, we don't want to log env variables - let envs: Vec = process_info.envp.iter().map(|env| env.to_utf8_lossy().into_owned()).collect(); + let envs: Vec = process_info + .envp + .iter() + .map(|env| env.to_string()) + .collect(); eprintln!("\tprocess envs: {:?}", envs); - eprintln!("\tprocess language: {:?}", process_info.language.to_utf8_lossy().into_owned()); + eprintln!( + "\tprocess language: {:?}", + process_info.language.to_string() + ); } configurator .get_configuration(process_info) diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs index a9aca529fe..c92de54b79 100644 --- a/library-config-ffi/src/static_config.rs +++ b/library-config-ffi/src/static_config.rs @@ -38,15 +38,15 @@ struct StaticConfig { rules: Vec, } -fn find_static_config<'a, 'b>( +fn find_static_config<'a>( cfg: &'a StaticConfig, - process_info: &ProcessInfo<'b>, + process_info: &ProcessInfo<'_>, ) -> Option<&'a HashMap> { for rule in &cfg.rules { if rule .selectors .iter() - .all(|s| selector_match(&s, process_info)) + .all(|s| selector_match(s, process_info)) { return Some(&rule.configuration); } @@ -54,9 +54,26 @@ fn find_static_config<'a, 'b>( None } +// Returns true if the selector matches the process info +// Any element in the "matches" section of the selector must match, they are ORed, +// as selectors are ANDed. fn selector_match(selector: &Selector, process_info: &ProcessInfo) -> bool { - // todo: match selectors - true + match selector.operator { + Operator::Equals => match selector.origin { + Origin::Language => selector + .matches + .iter() + .any(|m| m == &process_info.language.to_string()), + Origin::ProcessArguments => process_info + .args + .iter() + .any(|arg| selector.matches.iter().any(|m| m == &arg.to_string())), + Origin::EnvironmentVariable => process_info + .envp + .iter() + .any(|env| selector.matches.iter().any(|m| m == &env.to_string())), + }, + } } fn template_configs( @@ -67,7 +84,7 @@ fn template_configs( .iter() .map(|(&name, v)| { Ok(LibraryConfig { - name: name, + name, value: LibraryConfigValue::StrVal(ffi::CString::new(template_config( v, process_info, @@ -77,7 +94,7 @@ fn template_configs( .collect() } -fn template_config(config_val: &str, process_info: &ProcessInfo) -> String { +fn template_config(config_val: &str, _process_info: &ProcessInfo) -> String { // todo: template configuration config_val.to_owned() } @@ -92,9 +109,13 @@ pub struct Configurator { #[repr(C)] #[derive(Clone, Copy, serde::Deserialize, Debug, PartialEq, Eq, Hash)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[allow(clippy::enum_variant_names)] pub enum LibraryConfigName { DdTraceDebug = 0, - DdProfilingEnabled = 1, + DdService = 1, + DdEnv = 2, + DdVersion = 3, + DdProfilingEnabled = 4, } impl LibraryConfigName { @@ -102,6 +123,9 @@ impl LibraryConfigName { use LibraryConfigName::*; match self { DdTraceDebug => cstr!("DD_TRACE_DEBUG"), + DdService => cstr!("DD_SERVICE"), + DdEnv => cstr!("DD_ENV"), + DdVersion => cstr!("DD_VERSION"), DdProfilingEnabled => cstr!("DD_PROFILING_ENABLED"), } } @@ -109,6 +133,7 @@ impl LibraryConfigName { #[repr(C)] #[derive(Debug)] +#[allow(clippy::enum_variant_names)] pub enum LibraryConfigValue { NumVal(i64), BoolVal(bool), @@ -137,9 +162,9 @@ impl Configurator { } } - pub fn get_configuration<'a>( + pub fn get_configuration( &self, - process_info: ProcessInfo<'a>, + process_info: ProcessInfo<'_>, ) -> anyhow::Result> { let static_config = self.parse_static_config()?; if self.debug_logs { @@ -156,10 +181,7 @@ impl Configurator { if self.debug_logs { eprintln!("Will apply the following configuration: {library_config:?}"); } - Ok(vec![LibraryConfig { - name: LibraryConfigName::DdTraceDebug, - value: LibraryConfigValue::BoolVal(true), - }]) + Ok(library_config) } fn parse_static_config(&self) -> anyhow::Result { @@ -184,9 +206,9 @@ impl Configurator { mod tests { use std::io::Write; - use crate::static_config::{LibraryConfigName, Operator, Origin, Rule, Selector, StaticConfig}; - use super::Configurator; + use crate::static_config::{LibraryConfigName, Operator, Origin, Rule, Selector, StaticConfig}; + use ddcommon_ffi::{self as ffi}; macro_rules! map { ($(($key:expr , $value:expr)),* $(,)?) => { @@ -214,11 +236,12 @@ rules: operator: equals configuration: DD_PROFILING_ENABLED: true + DD_SERVICE: my-service ", ) .unwrap(); - let confgurator = Configurator::new(true, tmp.path().to_path_buf()); - let cfg = confgurator.parse_static_config().unwrap(); + let configurator = Configurator::new(true, tmp.path().to_path_buf()); + let cfg = configurator.parse_static_config().unwrap(); assert_eq!( cfg, StaticConfig { @@ -228,9 +251,50 @@ rules: matches: vec!["java".to_owned()], operator: Operator::Equals, }], - configuration: map![(LibraryConfigName::DdProfilingEnabled, "true".to_owned()),], + configuration: map![ + (LibraryConfigName::DdProfilingEnabled, "true".to_owned()), + (LibraryConfigName::DdService, "my-service".to_owned()) + ], }] } ) } + + #[test] + fn test_selector_match() { + let args = vec![ffi::CharSlice::from("-jar HelloWorld.jar")]; + let envp = vec![ffi::CharSlice::from("ENV=VAR")]; + let process_info = super::ProcessInfo { + args: args.as_slice().into(), + envp: envp.as_slice().into(), + language: ffi::CharSlice::from("java"), + }; + let selector = Selector { + origin: Origin::Language, + matches: vec!["java".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::ProcessArguments, + matches: vec!["-jar HelloWorld.jar".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::EnvironmentVariable, + matches: vec!["ENV=VAR".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::Language, + matches: vec!["python".to_owned()], + operator: Operator::Equals, + }; + assert!(!super::selector_match(&selector, &process_info)); + } } From 62b32df17df7b3e467b80c32014d9f3ba328b68a Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Mon, 30 Dec 2024 15:13:40 +0100 Subject: [PATCH 09/26] licenses & test fixes --- LICENSE-3rdparty.yml | 240 +++++++++++++++++- examples/{profiling-ffi => ffi}/.gitignore | 0 .../{profiling-ffi => ffi}/CMakeLists.txt | 0 .../{profiling-ffi => ffi}/array_queue.cpp | 0 examples/{profiling-ffi => ffi}/crashinfo.cpp | 0 .../{profiling-ffi => ffi}/crashtracking.c | 0 examples/{profiling-ffi => ffi}/exporter.cpp | 0 .../{profiling-ffi => ffi}/library_config.c | 1 - examples/{profiling-ffi => ffi}/profiles.c | 0 .../{profiling-ffi => ffi}/symbolizer.cpp | 0 examples/{profiling-ffi => ffi}/telemetry.c | 0 .../telemetry_metrics.c | 0 .../{profiling-ffi => ffi}/trace_exporter.c | 0 examples/library-config-ffi/config.c | 43 ---- tools/docker/Dockerfile.build | 1 + 15 files changed, 240 insertions(+), 45 deletions(-) rename examples/{profiling-ffi => ffi}/.gitignore (100%) rename examples/{profiling-ffi => ffi}/CMakeLists.txt (100%) rename examples/{profiling-ffi => ffi}/array_queue.cpp (100%) rename examples/{profiling-ffi => ffi}/crashinfo.cpp (100%) rename examples/{profiling-ffi => ffi}/crashtracking.c (100%) rename examples/{profiling-ffi => ffi}/exporter.cpp (100%) rename examples/{profiling-ffi => ffi}/library_config.c (98%) rename examples/{profiling-ffi => ffi}/profiles.c (100%) rename examples/{profiling-ffi => ffi}/symbolizer.cpp (100%) rename examples/{profiling-ffi => ffi}/telemetry.c (100%) rename examples/{profiling-ffi => ffi}/telemetry_metrics.c (100%) rename examples/{profiling-ffi => ffi}/trace_exporter.c (100%) delete mode 100644 examples/library-config-ffi/config.c diff --git a/LICENSE-3rdparty.yml b/LICENSE-3rdparty.yml index f3d694237d..e19a5fb36e 100644 --- a/LICENSE-3rdparty.yml +++ b/LICENSE-3rdparty.yml @@ -1,4 +1,4 @@ -root_name: datadog-alloc, builder, build_common, tools, datadog-crashtracker, ddcommon, ddtelemetry, datadog-ddsketch, datadog-crashtracker-ffi, ddcommon-ffi, datadog-profiling, datadog-profiling-ffi, data-pipeline-ffi, data-pipeline, datadog-trace-normalization, datadog-trace-protobuf, datadog-trace-obfuscation, datadog-trace-utils, tinybytes, dogstatsd-client, ddtelemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, dogstatsd, datadog-ipc, datadog-ipc-macros, tarpc, tarpc-plugins, spawn_worker, cc_utils, datadog-live-debugger, datadog-live-debugger-ffi, datadog-remote-config, datadog-dynamic-configuration, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, sidecar_mockgen, test_spawn_from_lib, datadog-serverless-trace-mini-agent, datadog-trace-mini-agent +root_name: datadog-alloc, builder, build_common, tools, datadog-crashtracker, ddcommon, ddtelemetry, datadog-ddsketch, datadog-crashtracker-ffi, ddcommon-ffi, datadog-profiling, datadog-profiling-ffi, data-pipeline-ffi, data-pipeline, datadog-trace-normalization, datadog-trace-protobuf, datadog-trace-obfuscation, datadog-trace-utils, tinybytes, dogstatsd-client, datadog-library-config-ffi, ddtelemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, dogstatsd, datadog-ipc, datadog-ipc-macros, tarpc, tarpc-plugins, spawn_worker, cc_utils, datadog-live-debugger, datadog-live-debugger-ffi, datadog-remote-config, datadog-dynamic-configuration, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, sidecar_mockgen, test_spawn_from_lib, datadog-serverless-trace-mini-agent, datadog-trace-mini-agent third_party_libraries: - package_name: addr2line package_version: 0.24.2 @@ -26387,6 +26387,214 @@ third_party_libraries: DEALINGS IN THE SOFTWARE. - license: Apache-2.0 text: " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n" +- package_name: serde_yaml + package_version: 0.9.34+deprecated + repository: https://github.com/dtolnay/serde-yaml + license: MIT OR Apache-2.0 + licenses: + - license: MIT + text: | + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + - license: Apache-2.0 + text: |2 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS - package_name: sha1 package_version: 0.10.6 repository: https://github.com/RustCrypto/hashes @@ -32024,6 +32232,36 @@ third_party_libraries: shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. +- package_name: unsafe-libyaml + package_version: 0.2.11 + repository: https://github.com/dtolnay/unsafe-libyaml + license: MIT + licenses: + - license: MIT + text: | + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. - package_name: untrusted package_version: 0.7.1 repository: https://github.com/briansmith/untrusted diff --git a/examples/profiling-ffi/.gitignore b/examples/ffi/.gitignore similarity index 100% rename from examples/profiling-ffi/.gitignore rename to examples/ffi/.gitignore diff --git a/examples/profiling-ffi/CMakeLists.txt b/examples/ffi/CMakeLists.txt similarity index 100% rename from examples/profiling-ffi/CMakeLists.txt rename to examples/ffi/CMakeLists.txt diff --git a/examples/profiling-ffi/array_queue.cpp b/examples/ffi/array_queue.cpp similarity index 100% rename from examples/profiling-ffi/array_queue.cpp rename to examples/ffi/array_queue.cpp diff --git a/examples/profiling-ffi/crashinfo.cpp b/examples/ffi/crashinfo.cpp similarity index 100% rename from examples/profiling-ffi/crashinfo.cpp rename to examples/ffi/crashinfo.cpp diff --git a/examples/profiling-ffi/crashtracking.c b/examples/ffi/crashtracking.c similarity index 100% rename from examples/profiling-ffi/crashtracking.c rename to examples/ffi/crashtracking.c diff --git a/examples/profiling-ffi/exporter.cpp b/examples/ffi/exporter.cpp similarity index 100% rename from examples/profiling-ffi/exporter.cpp rename to examples/ffi/exporter.cpp diff --git a/examples/profiling-ffi/library_config.c b/examples/ffi/library_config.c similarity index 98% rename from examples/profiling-ffi/library_config.c rename to examples/ffi/library_config.c index 442c97bed2..fbe6dc7efb 100644 --- a/examples/profiling-ffi/library_config.c +++ b/examples/ffi/library_config.c @@ -1,7 +1,6 @@ // Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 -#include <_stdlib.h> #include #include #include diff --git a/examples/profiling-ffi/profiles.c b/examples/ffi/profiles.c similarity index 100% rename from examples/profiling-ffi/profiles.c rename to examples/ffi/profiles.c diff --git a/examples/profiling-ffi/symbolizer.cpp b/examples/ffi/symbolizer.cpp similarity index 100% rename from examples/profiling-ffi/symbolizer.cpp rename to examples/ffi/symbolizer.cpp diff --git a/examples/profiling-ffi/telemetry.c b/examples/ffi/telemetry.c similarity index 100% rename from examples/profiling-ffi/telemetry.c rename to examples/ffi/telemetry.c diff --git a/examples/profiling-ffi/telemetry_metrics.c b/examples/ffi/telemetry_metrics.c similarity index 100% rename from examples/profiling-ffi/telemetry_metrics.c rename to examples/ffi/telemetry_metrics.c diff --git a/examples/profiling-ffi/trace_exporter.c b/examples/ffi/trace_exporter.c similarity index 100% rename from examples/profiling-ffi/trace_exporter.c rename to examples/ffi/trace_exporter.c diff --git a/examples/library-config-ffi/config.c b/examples/library-config-ffi/config.c deleted file mode 100644 index 86d0dad16e..0000000000 --- a/examples/library-config-ffi/config.c +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include - -#define DDOG_VAL_STR_PTR(val) \ - (val.tag == DDOG_LIBRARY_CONFIG_VALUE_STR_VAL ? val.str_val.ptr : "\0") - -#define DDOG_SLICE_CHARSLICE(arr) \ - ((ddog_Slice_CharSlice){.ptr = arr, .len = sizeof(arr) / sizeof(arr[0])}) - -int main(int argc, const char *const *argv) { - ddog_Configurator *configurator = ddog_library_configurator_new(true); - ddog_library_configurator_with_path(configurator, - DDOG_CHARSLICE_C("/tmp/foobar/static_config.yaml")); - - ddog_CharSlice args[] = { - DDOG_CHARSLICE_C("/bin/true"), - }; - ddog_CharSlice envp[] = { - DDOG_CHARSLICE_C("FOO=BAR"), - }; - ddog_Result_VecLibraryConfig config_result = ddog_library_configurator_get( - configurator, (ddog_ProcessInfo){.args = DDOG_SLICE_CHARSLICE(args), - .envp = DDOG_SLICE_CHARSLICE(envp), - .language = DDOG_CHARSLICE_C("java")}); - if (config_result.tag == DDOG_RESULT_VEC_LIBRARY_CONFIG_ERR_VEC_LIBRARY_CONFIG) { - ddog_Error err = config_result.err; - fprintf(stderr, "%.*s", (int)err.message.len, err.message.ptr); - ddog_Error_drop(&err); - exit(1); - } - - ddog_Vec_LibraryConfig configs = config_result.ok; - for (int i = 0; i < configs.len; i++) { - const ddog_LibraryConfig *cfg = &configs.ptr[i]; - ddog_CStr name = ddog_library_config_name_to_env(cfg->name); - printf("%s %s\n", name.ptr, DDOG_VAL_STR_PTR(cfg->value)); - } -} diff --git a/tools/docker/Dockerfile.build b/tools/docker/Dockerfile.build index b6f744fc7a..ad2aa4e0f8 100644 --- a/tools/docker/Dockerfile.build +++ b/tools/docker/Dockerfile.build @@ -80,6 +80,7 @@ COPY "ddsketch/Cargo.toml" "ddsketch/" COPY "dogstatsd/Cargo.toml" "dogstatsd/" COPY "dogstatsd-client/Cargo.toml" "dogstatsd-client/" COPY "dynamic-configuration/Cargo.toml" "dynamic-configuration/" +COPY "library-config-ffi/Cargo.toml" "library-config-ffi/" COPY "live-debugger/Cargo.toml" "live-debugger/" COPY "live-debugger-ffi/Cargo.toml" "live-debugger-ffi/" COPY "profiling/Cargo.toml" "profiling/" From 8ffe75549add8ebd34b12b61c30096eac0f917fe Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Tue, 31 Dec 2024 09:00:40 +0100 Subject: [PATCH 10/26] more ci fixes --- .github/workflows/test.yml | 2 +- builder/Cargo.toml | 1 + builder/src/bin/release.rs | 2 ++ builder/src/profiling.rs | 2 ++ examples/ffi/library_config.c | 11 +++++++++++ tools/docker/Dockerfile.build | 2 +- 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 218dfde697..2c7a538311 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,7 @@ jobs: env: RUSTFLAGS: "${{ matrix.flags }}" run: | - cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker --release -- --out $LIBDD_OUTPUT_FOLDER + cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker,datadog-library-config-ffi --release -- --out $LIBDD_OUTPUT_FOLDER - name: 'Publish libdatadog' uses: actions/upload-artifact@v4 diff --git a/builder/Cargo.toml b/builder/Cargo.toml index 218389f287..5c6e44d5af 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -13,6 +13,7 @@ profiling = [] telemetry = [] data-pipeline = [] symbolizer = [] +datadog-library-config-ffi = [] [lib] bench = false diff --git a/builder/src/bin/release.rs b/builder/src/bin/release.rs index f61d91c471..91fabc5ad1 100644 --- a/builder/src/bin/release.rs +++ b/builder/src/bin/release.rs @@ -72,6 +72,8 @@ pub fn main() { f.push("crashtracker-ffi".to_string()); #[cfg(feature = "symbolizer")] f.push("symbolizer".to_string()); + #[cfg(feature = "datadog-library-config-ffi")] + f.push("datadog-library-config-ffi".to_string()); f }; diff --git a/builder/src/profiling.rs b/builder/src/profiling.rs index 2aebe7bf54..80463fcb39 100644 --- a/builder/src/profiling.rs +++ b/builder/src/profiling.rs @@ -37,6 +37,8 @@ impl Profiling { headers.push("data-pipeline.h"); #[cfg(feature = "symbolizer")] headers.push("blazesym.h"); + #[cfg(feature = "datadog-library-config-ffi")] + headers.push("library-config.h"); let mut origin_path: PathBuf = [&self.source_include, "dummy.h"].iter().collect(); let mut target_path: PathBuf = [&self.target_include, "dummy.h"].iter().collect(); diff --git a/examples/ffi/library_config.c b/examples/ffi/library_config.c index fbe6dc7efb..0c5d5ac610 100644 --- a/examples/ffi/library_config.c +++ b/examples/ffi/library_config.c @@ -6,6 +6,17 @@ #include #include +#ifdef _WIN32 +// Define setenv for Windows +#include +int setenv(const char *name, const char *value, int overwrite) { + if (overwrite || getenv(name) == NULL) { + return SetEnvironmentVariable(name, value) ? 0 : -1; + } + return 0; +} +#endif + #define DDOG_VAL_STR_PTR(val) \ (val.tag == DDOG_LIBRARY_CONFIG_VALUE_STR_VAL ? val.str_val.ptr : "\0") diff --git a/tools/docker/Dockerfile.build b/tools/docker/Dockerfile.build index ad2aa4e0f8..d55b9c10bb 100644 --- a/tools/docker/Dockerfile.build +++ b/tools/docker/Dockerfile.build @@ -160,7 +160,7 @@ RUN cargo build --lib --workspace --exclude builder RUN cargo build --release --lib --workspace --exclude builder COPY ./ ./ -RUN cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker --release -- --out /build/output +RUN cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker,datadog-library-config-ffi --release -- --out /build/output FROM scratch as ffi_build_output From 766522b36d9127eef3a3e15f4b6d6db3ac5d117b Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Tue, 31 Dec 2024 10:47:12 +0100 Subject: [PATCH 11/26] feat(libconfig): Add ddog_library_configurator_get_from_bytes method --- library-config-ffi/src/lib.rs | 38 +++++++++----------- library-config-ffi/src/static_config.rs | 47 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 851258e3b4..10f588cf3d 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -41,33 +41,29 @@ pub extern "C" fn ddog_library_configurator_get<'a>( configurator: &'a Configurator, process_info: ProcessInfo<'a>, ) -> ffi::Result> { - if configurator.debug_logs { - eprintln!("Called library_config_common_component:"); - eprintln!("\tconfigurator: {:?}", configurator); - let args: Vec = process_info - .args - .iter() - .map(|arg| arg.to_string()) - .collect(); - eprintln!("\tprocess args: {:?}", args); - // TODO: this is for testing purpose, we don't want to log env variables - let envs: Vec = process_info - .envp - .iter() - .map(|env| env.to_string()) - .collect(); - eprintln!("\tprocess envs: {:?}", envs); - eprintln!( - "\tprocess language: {:?}", - process_info.language.to_string() - ); - } + configurator.log_process_info(&process_info); configurator .get_configuration(process_info) .map(ffi::Vec::from_std) .into() } +#[no_mangle] +// In some languages like NodeJS, IO from a shared library is expensive. +// Thus we provide a way to pass the configuration as a byte array instead, +// so that the library can do the IO. +pub extern "C" fn ddog_library_configurator_get_from_bytes<'a>( + configurator: &'a Configurator, + process_info: ProcessInfo<'a>, + config_bytes: ffi::CharSlice<'a>, +) -> ffi::Result> { + configurator.log_process_info(&process_info); + configurator + .get_configuration_from_bytes(process_info, config_bytes) + .map(ffi::Vec::from_std) + .into() +} + #[no_mangle] pub extern "C" fn ddog_library_config_name_to_env(name: LibraryConfigName) -> ffi::CStr<'static> { ffi::CStr::from_std(name.to_env_name()) diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs index c92de54b79..a0b55eb635 100644 --- a/library-config-ffi/src/static_config.rs +++ b/library-config-ffi/src/static_config.rs @@ -184,6 +184,53 @@ impl Configurator { Ok(library_config) } + pub fn get_configuration_from_bytes( + &self, + process_info: ProcessInfo<'_>, + config_bytes: ffi::CharSlice<'_>, + ) -> anyhow::Result> { + let static_config = serde_yaml::from_str(config_bytes.to_string().as_str())?; + if self.debug_logs { + eprintln!("Read the following static config: {static_config:?}"); + } + + let Some(configs) = find_static_config(&static_config, &process_info) else { + if self.debug_logs { + eprintln!("No selector matched"); + } + return Ok(Vec::new()); + }; + let library_config = template_configs(configs, &process_info)?; + if self.debug_logs { + eprintln!("Will apply the following configuration: {library_config:?}"); + } + Ok(library_config) + } + + pub fn log_process_info(&self, process_info: &ProcessInfo) { + if self.debug_logs { + eprintln!("Called library_config_common_component:"); + eprintln!("\tconfigurator: {:?}", self); + let args: Vec = process_info + .args + .iter() + .map(|arg| arg.to_string()) + .collect(); + eprintln!("\tprocess args: {:?}", args); + // TODO: this is for testing purpose, we don't want to log env variables + let envs: Vec = process_info + .envp + .iter() + .map(|env| env.to_string()) + .collect(); + eprintln!("\tprocess envs: {:?}", envs); + eprintln!( + "\tprocess language: {:?}", + process_info.language.to_string() + ); + } + } + fn parse_static_config(&self) -> anyhow::Result { let mut f = match std::fs::File::open(&self.static_config_file_path) { Ok(f) => f, From b6d30788e30787a1573acc8460566f9c645e7e29 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Tue, 31 Dec 2024 12:19:16 +0100 Subject: [PATCH 12/26] public static_config for nodejs --- library-config-ffi/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 10f588cf3d..13d6be35c7 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -1,7 +1,7 @@ // Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 -mod static_config; +pub mod static_config; use std::path::PathBuf; From 8a71af642fb6725a107f6544c298ba04aa6a2bd8 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Tue, 31 Dec 2024 16:30:28 +0100 Subject: [PATCH 13/26] fix off by one --- ddcommon-ffi/src/cstr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddcommon-ffi/src/cstr.rs b/ddcommon-ffi/src/cstr.rs index adc7bffdc2..aecdccc9ba 100644 --- a/ddcommon-ffi/src/cstr.rs +++ b/ddcommon-ffi/src/cstr.rs @@ -63,7 +63,7 @@ impl CString { pub fn as_cstr(&self) -> CStr<'_> { CStr { ptr: self.ptr, - length: self.length, + length: self.length + 1, // +1 for the null terminator _lifetime_marker: PhantomData, } } From 6266f3bb0df25d521f12e939f5444269006ac105 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 14 Jan 2025 13:34:01 +0100 Subject: [PATCH 14/26] * Split configuration in two crates for easier integration with other repositories * Change returned configurations to always be string * Modify API to provide file path on config get --- Cargo.lock | 13 +- Cargo.toml | 1 + examples/ffi/library_config.c | 14 +- library-config-ffi/Cargo.toml | 5 +- library-config-ffi/cbindgen.toml | 2 +- library-config-ffi/src/lib.rs | 115 ++++++-- library-config-ffi/src/static_config.rs | 207 +------------ library-config/Cargo.toml | 19 ++ library-config/src/lib.rs | 376 ++++++++++++++++++++++++ 9 files changed, 504 insertions(+), 248 deletions(-) create mode 100644 library-config/Cargo.toml create mode 100644 library-config/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 02617d0c9c..432168cf89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,16 +1442,25 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "datadog-library-config" +version = "0.0.1" +dependencies = [ + "anyhow", + "serde", + "serde_yaml", + "tempfile", +] + [[package]] name = "datadog-library-config-ffi" version = "0.0.1" dependencies = [ "anyhow", "build_common", + "datadog-library-config", "ddcommon", "ddcommon-ffi", - "serde", - "serde_yaml", "tempfile", ] diff --git a/Cargo.toml b/Cargo.toml index 430288f810..7def6b2b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "tinybytes", "dogstatsd-client", "library-config-ffi", + "library-config", ] # https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2 diff --git a/examples/ffi/library_config.c b/examples/ffi/library_config.c index 0c5d5ac610..f4355eaf61 100644 --- a/examples/ffi/library_config.c +++ b/examples/ffi/library_config.c @@ -10,10 +10,10 @@ // Define setenv for Windows #include int setenv(const char *name, const char *value, int overwrite) { - if (overwrite || getenv(name) == NULL) { - return SetEnvironmentVariable(name, value) ? 0 : -1; - } - return 0; + if (overwrite || getenv(name) == NULL) { + return SetEnvironmentVariable(name, value) ? 0 : -1; + } + return 0; } #endif @@ -25,8 +25,6 @@ int setenv(const char *name, const char *value, int overwrite) { int main(int argc, const char *const *argv) { ddog_Configurator *configurator = ddog_library_configurator_new(true); - ddog_library_configurator_with_path(configurator, - DDOG_CHARSLICE_C("/tmp/foobar/static_config.yaml")); ddog_CharSlice args[] = { DDOG_CHARSLICE_C("/bin/true"), @@ -50,7 +48,7 @@ int main(int argc, const char *const *argv) { const ddog_LibraryConfig *cfg = &configs.ptr[i]; ddog_CStr name = ddog_library_config_name_to_env(cfg->name); - printf("Setting env variable: %s=%s\n", name.ptr, DDOG_VAL_STR_PTR(cfg->value)); - setenv(name.ptr, DDOG_VAL_STR_PTR(cfg->value), 1); + printf("Setting env variable: %s=%s\n", name.ptr, cfg->value.ptr); + setenv(name.ptr, cfg->value.ptr, 1); } } diff --git a/library-config-ffi/Cargo.toml b/library-config-ffi/Cargo.toml index f9bdbc68aa..f25b6647cc 100644 --- a/library-config-ffi/Cargo.toml +++ b/library-config-ffi/Cargo.toml @@ -13,8 +13,7 @@ bench = false [dependencies] ddcommon = { path = "../ddcommon" } ddcommon-ffi = { path = "../ddcommon-ffi", default-features = false } -serde = { version = "1.0", features = ["derive"] } -serde_yaml = "0.9.34" +datadog-library-config = { path = "../library-config" } anyhow = "1.0" [features] @@ -25,4 +24,4 @@ cbindgen = ["build_common/cbindgen", "ddcommon-ffi/cbindgen"] build_common = { path = "../build-common" } [dev-dependencies] -tempfile = {version = "3.3"} +tempfile = { version = "3.3" } diff --git a/library-config-ffi/cbindgen.toml b/library-config-ffi/cbindgen.toml index 75ce245648..ccf4d3a49c 100644 --- a/library-config-ffi/cbindgen.toml +++ b/library-config-ffi/cbindgen.toml @@ -36,4 +36,4 @@ must_use = "DDOG_CHECK_RETURN" [parse] parse_deps = true -include = ["ddcommon", "ddcommon-ffi"] +include = ["ddcommon", "ddcommon-ffi", "datadog-library-config"] diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index 13d6be35c7..d47f998b73 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -1,50 +1,102 @@ // Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 -pub mod static_config; +use std::{ffi::c_char, ops::Deref}; -use std::path::PathBuf; - -use ddcommon_ffi::{self as ffi, slice::AsBytes}; -use static_config::{Configurator, LibraryConfig, LibraryConfigName, ProcessInfo}; +use datadog_library_config::{Configurator, LibraryConfigName}; +use ddcommon_ffi::{self as ffi, slice::AsBytes, Slice}; // TODO: Centos 6 build // Trust me it works bro 😉😉😉 // #[cfg(linux)] // std::arch::global_asm!(".symver memcpy,memcpy@GLIBC_2.2.5"); -#[no_mangle] -pub extern "C" fn ddog_library_configurator_new(debug_logs: bool) -> Box { - Box::new(Configurator::new( - debug_logs, - PathBuf::from( - "/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml", - ), - )) +#[repr(C)] +pub struct ProcessInfo<'a> { + pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, + pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, + pub language: ffi::CharSlice<'a>, +} + +fn cast_slice_of_slice_ref<'a, 'b>( + s: &'b ffi::Slice<'a, ffi::CharSlice<'a>>, +) -> &'b ffi::Slice<'a, ffi::slice::ByteSlice<'a>> { + // Safety: + // ffi::CharSlice and ffi::slice::ByteSlice have the same layout, and since they are wrappers around + // *const char i8 and *const char u8 respectively, they are trivially interchangeable + unsafe { std::mem::transmute(s) } +} + +impl<'a> ProcessInfo<'a> { + fn ffi_to_rs(&'a self) -> datadog_library_config::ProcessInfo<'a, ffi::slice::ByteSlice<'a>> { + let language = + unsafe { std::mem::transmute_copy::, Slice<'a, u8>>(&self.language) }; + datadog_library_config::ProcessInfo::> { + args: cast_slice_of_slice_ref(&self.args), + envp: cast_slice_of_slice_ref(&self.envp), + language, + } + } +} + +#[repr(C)] +pub struct LibraryConfig { + pub name: LibraryConfigName, + pub value: ffi::CString, +} + +impl LibraryConfig { + fn rs_vec_to_ffi( + configs: Vec, + ) -> anyhow::Result> { + let cfg: Vec = configs + .into_iter() + .map(|c| { + Ok(LibraryConfig { + name: c.name, + value: ffi::CString::from_std(std::ffi::CString::new(c.value)?), + }) + }) + .collect::, std::ffi::NulError>>()?; + Ok(ffi::Vec::from_std(cfg)) + } } #[no_mangle] -/// Sets the path at which we will read the static configuration file. -/// This should mainly be used for testing -pub extern "C" fn ddog_library_configurator_with_path( - configurator: &mut Configurator, - p: ffi::CharSlice, -) { - configurator.static_config_file_path = PathBuf::from(p.to_utf8_lossy().into_owned()); +pub extern "C" fn ddog_library_configurator_new(debug_logs: bool) -> Box { + Box::new(Configurator::new(debug_logs)) } #[no_mangle] pub extern "C" fn ddog_library_configurator_drop(_: Box) {} +#[no_mangle] +pub extern "C" fn ddog_library_configurator_get_path<'a>( + configurator: &'a Configurator, + process_info: ProcessInfo<'a>, + path: ffi::CharSlice<'a>, +) -> ffi::Result> { + let path = path.to_utf8_lossy(); + let process_info = process_info.ffi_to_rs(); + configurator + .get_config_from_file(path.deref().as_ref(), process_info) + .and_then(LibraryConfig::rs_vec_to_ffi) + .into() +} + #[no_mangle] pub extern "C" fn ddog_library_configurator_get<'a>( configurator: &'a Configurator, process_info: ProcessInfo<'a>, ) -> ffi::Result> { - configurator.log_process_info(&process_info); + let process_info = process_info.ffi_to_rs(); configurator - .get_configuration(process_info) - .map(ffi::Vec::from_std) + .get_config_from_file( + "/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml" + .as_ref(), + process_info, + ) + .and_then(LibraryConfig::rs_vec_to_ffi) .into() } @@ -55,18 +107,25 @@ pub extern "C" fn ddog_library_configurator_get<'a>( pub extern "C" fn ddog_library_configurator_get_from_bytes<'a>( configurator: &'a Configurator, process_info: ProcessInfo<'a>, - config_bytes: ffi::CharSlice<'a>, + config_bytes: ffi::slice::ByteSlice<'a>, ) -> ffi::Result> { - configurator.log_process_info(&process_info); + let process_info = process_info.ffi_to_rs(); configurator - .get_configuration_from_bytes(process_info, config_bytes) - .map(ffi::Vec::from_std) + .get_config_from_bytes(&config_bytes, process_info) + .and_then(LibraryConfig::rs_vec_to_ffi) .into() } #[no_mangle] pub extern "C" fn ddog_library_config_name_to_env(name: LibraryConfigName) -> ffi::CStr<'static> { - ffi::CStr::from_std(name.to_env_name()) + use LibraryConfigName::*; + ffi::CStr::from_std(match name { + DdTraceDebug => ddcommon::cstr!("DD_TRACE_DEBUG"), + DdService => ddcommon::cstr!("DD_SERVICE"), + DdEnv => ddcommon::cstr!("DD_ENV"), + DdVersion => ddcommon::cstr!("DD_VERSION"), + DdProfilingEnabled => ddcommon::cstr!("DD_PROFILING_ENABLED"), + }) } #[no_mangle] diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs index a0b55eb635..bc380c56e7 100644 --- a/library-config-ffi/src/static_config.rs +++ b/library-config-ffi/src/static_config.rs @@ -4,100 +4,8 @@ use anyhow::anyhow; use ddcommon::cstr; use ddcommon_ffi::{self as ffi}; -use std::{collections::HashMap, io::ErrorKind, path::PathBuf}; +use std::{io::ErrorKind, path::PathBuf}; -#[derive(serde::Deserialize, Debug, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -enum Origin { - ProcessArguments, - EnvironmentVariable, - Language, -} - -#[derive(serde::Deserialize, Debug, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -enum Operator { - Equals, -} - -#[derive(serde::Deserialize, Debug, PartialEq, Eq)] -struct Selector { - origin: Origin, - matches: Vec, - operator: Operator, -} - -#[derive(serde::Deserialize, Debug, PartialEq, Eq)] -struct Rule { - selectors: Vec, - configuration: HashMap, -} - -#[derive(serde::Deserialize, Default, Debug, PartialEq, Eq)] -struct StaticConfig { - rules: Vec, -} - -fn find_static_config<'a>( - cfg: &'a StaticConfig, - process_info: &ProcessInfo<'_>, -) -> Option<&'a HashMap> { - for rule in &cfg.rules { - if rule - .selectors - .iter() - .all(|s| selector_match(s, process_info)) - { - return Some(&rule.configuration); - } - } - None -} - -// Returns true if the selector matches the process info -// Any element in the "matches" section of the selector must match, they are ORed, -// as selectors are ANDed. -fn selector_match(selector: &Selector, process_info: &ProcessInfo) -> bool { - match selector.operator { - Operator::Equals => match selector.origin { - Origin::Language => selector - .matches - .iter() - .any(|m| m == &process_info.language.to_string()), - Origin::ProcessArguments => process_info - .args - .iter() - .any(|arg| selector.matches.iter().any(|m| m == &arg.to_string())), - Origin::EnvironmentVariable => process_info - .envp - .iter() - .any(|env| selector.matches.iter().any(|m| m == &env.to_string())), - }, - } -} - -fn template_configs( - config: &HashMap, - process_info: &ProcessInfo, -) -> anyhow::Result> { - config - .iter() - .map(|(&name, v)| { - Ok(LibraryConfig { - name, - value: LibraryConfigValue::StrVal(ffi::CString::new(template_config( - v, - process_info, - ))?), - }) - }) - .collect() -} - -fn template_config(config_val: &str, _process_info: &ProcessInfo) -> String { - // todo: template configuration - config_val.to_owned() -} #[derive(Debug)] pub struct Configurator { @@ -131,22 +39,6 @@ impl LibraryConfigName { } } -#[repr(C)] -#[derive(Debug)] -#[allow(clippy::enum_variant_names)] -pub enum LibraryConfigValue { - NumVal(i64), - BoolVal(bool), - StrVal(ffi::CString), -} - -#[repr(C)] -#[derive(Debug)] -pub struct LibraryConfig { - pub name: LibraryConfigName, - pub value: LibraryConfigValue, -} - #[repr(C)] pub struct ProcessInfo<'a> { pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, @@ -248,100 +140,3 @@ impl Configurator { Ok(serde_yaml::from_reader(&mut f)?) } } - -#[cfg(test)] -mod tests { - use std::io::Write; - - use super::Configurator; - use crate::static_config::{LibraryConfigName, Operator, Origin, Rule, Selector, StaticConfig}; - use ddcommon_ffi::{self as ffi}; - - macro_rules! map { - ($(($key:expr , $value:expr)),* $(,)?) => { - { - #[allow(unused_mut)] - let mut map = std::collections::HashMap::new(); - $( - map.insert($key, $value); - )* - map - } - }; - } - - #[test] - fn test_parse_static_config() { - let mut tmp = tempfile::NamedTempFile::new().unwrap(); - tmp.as_file_mut() - .write_all( - b" -rules: -- selectors: - - origin: language - matches: [\"java\"] - operator: equals - configuration: - DD_PROFILING_ENABLED: true - DD_SERVICE: my-service -", - ) - .unwrap(); - let configurator = Configurator::new(true, tmp.path().to_path_buf()); - let cfg = configurator.parse_static_config().unwrap(); - assert_eq!( - cfg, - StaticConfig { - rules: vec![Rule { - selectors: vec![Selector { - origin: Origin::Language, - matches: vec!["java".to_owned()], - operator: Operator::Equals, - }], - configuration: map![ - (LibraryConfigName::DdProfilingEnabled, "true".to_owned()), - (LibraryConfigName::DdService, "my-service".to_owned()) - ], - }] - } - ) - } - - #[test] - fn test_selector_match() { - let args = vec![ffi::CharSlice::from("-jar HelloWorld.jar")]; - let envp = vec![ffi::CharSlice::from("ENV=VAR")]; - let process_info = super::ProcessInfo { - args: args.as_slice().into(), - envp: envp.as_slice().into(), - language: ffi::CharSlice::from("java"), - }; - let selector = Selector { - origin: Origin::Language, - matches: vec!["java".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::ProcessArguments, - matches: vec!["-jar HelloWorld.jar".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::EnvironmentVariable, - matches: vec!["ENV=VAR".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::Language, - matches: vec!["python".to_owned()], - operator: Operator::Equals, - }; - assert!(!super::selector_match(&selector, &process_info)); - } -} diff --git a/library-config/Cargo.toml b/library-config/Cargo.toml new file mode 100644 index 0000000000..7e65fb3509 --- /dev/null +++ b/library-config/Cargo.toml @@ -0,0 +1,19 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present Datadog, Inc. + +[package] +name = "datadog-library-config" +version = "0.0.1" +edition = "2021" + +[lib] +crate-type = ["lib"] +bench = false + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9.34" +anyhow = "1.0" + +[dev-dependencies] +tempfile = { version = "3.3" } diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs new file mode 100644 index 0000000000..570568430d --- /dev/null +++ b/library-config/src/lib.rs @@ -0,0 +1,376 @@ +use std::collections::HashMap; +use std::ops::Deref; +use std::path::Path; +use std::{fs, io}; + +use anyhow::Context; + +#[repr(C)] +pub struct ProcessInfo<'a, T: Deref> { + pub args: &'a [T], + pub envp: &'a [T], + pub language: T, +} + +#[repr(C)] +#[derive(Clone, Copy, serde::Deserialize, Debug, PartialEq, Eq, Hash)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[allow(clippy::enum_variant_names)] +pub enum LibraryConfigName { + DdTraceDebug = 0, + DdService = 1, + DdEnv = 2, + DdVersion = 3, + DdProfilingEnabled = 4, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +enum Origin { + ProcessArguments, + EnvironmentVariable, + Language, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +enum Operator { + Equals, + PrefixMatches, + SuffixMatches, + // todox + // WildcardMatches, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +struct Selector { + origin: Origin, + matches: Vec, + operator: Operator, +} + +#[derive(serde::Deserialize, Debug, PartialEq, Eq)] +struct Rule { + selectors: Vec, + configuration: HashMap, +} + +#[derive(serde::Deserialize, Default, Debug, PartialEq, Eq)] +struct StableConfig { + rules: Vec, +} + +fn find_stable_config<'a, 'b>( + cfg: &'b StableConfig, + process_info: &'a ProcessInfo<'a, impl Deref>, +) -> Option<&'b HashMap> { + for rule in &cfg.rules { + if rule + .selectors + .iter() + .all(|s| selector_match(s, process_info)) + { + return Some(&rule.configuration); + } + } + None +} + +// Returns true if the selector matches the process info +// Any element in the "matches" section of the selector must match, they are ORed, +// as selectors are ANDed. +fn selector_match<'a>( + selector: &Selector, + process_info: &'a ProcessInfo<'a, impl Deref>, +) -> bool { + match selector.origin { + Origin::Language => string_selector(selector, process_info.language.deref()), + Origin::ProcessArguments => string_list_selector(selector, process_info.args), + Origin::EnvironmentVariable => string_list_selector(selector, process_info.envp), + } +} + +fn string_list_selector<'a, B: Deref>(selector: &Selector, l: &'a [B]) -> bool { + l.into_iter().any(|v| string_selector(selector, v.deref())) +} + +fn string_selector(selector: &Selector, matches: &[u8]) -> bool { + selector + .matches + .iter() + .any(|m| string_operator_match(&selector.operator, m.as_bytes(), matches)) +} + +fn string_operator_match(op: &Operator, matches: &[u8], value: &[u8]) -> bool { + match op { + Operator::Equals => matches == value, + Operator::PrefixMatches => value.starts_with(matches), + Operator::SuffixMatches => value.ends_with(matches), + // Operator::WildcardMatches => todo!("Wildcard matches is not implemented"), + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct LibraryConfig { + pub name: LibraryConfigName, + pub value: String, +} + +fn template_configs<'a>( + config: &HashMap, + process_info: &'a ProcessInfo<'a, impl Deref>, +) -> anyhow::Result> { + config + .iter() + .map(|(&name, v)| { + Ok(LibraryConfig { + name, + value: template_config(v, process_info)?, + }) + }) + .collect() +} + +/// Templates a config string. +/// +/// variables are enclosed in double curly brackets "{{" and "}}" +/// +/// For instance: +/// +/// with the following varriable definition, var = "abc" var2 = "def", this transforms \ +/// "foo_{{ var }}_bar_{{ var2 }}" -> "foo_abc_bar_def" +fn template_config<'a>( + config_val: &str, + process_info: &'a ProcessInfo<'a, impl Deref>, +) -> anyhow::Result { + let mut rest = config_val; + let mut templated = String::with_capacity(config_val.len()); + loop { + let Some((head, after_bracket)) = rest.split_once("{{") else { + templated.push_str(rest); + return Ok(templated); + }; + templated.push_str(head); + let Some((template_var, tail)) = after_bracket.split_once("}}") else { + anyhow::bail!("unterminated template in config") + }; + let template_var = template_var.trim(); + let val = match template_var { + "language" => String::from_utf8_lossy(process_info.language.deref()), + _ => std::borrow::Cow::Borrowed("UNDEFINED"), + }; + templated.push_str(&val); + rest = tail; + } +} + +#[derive(Debug)] +pub struct Configurator { + debug_logs: bool, +} + +impl Configurator { + pub fn new(debug_logs: bool) -> Self { + Self { debug_logs } + } + + fn log_process_info(&self, process_info: &ProcessInfo<'_, impl Deref>) { + if self.debug_logs { + eprintln!("Called library_config_common_component:"); + eprintln!("\tconfigurator: {:?}", self); + eprintln!("\tprocess args:"); + process_info + .args + .iter() + .map(|arg| String::from_utf8_lossy(&*arg)) + .for_each(|e| eprintln!(" {:?}", e.as_ref())); + + // TODO: this is for testing purpose, we don't want to log env variables + // eprintln!("\tprocess envs:"); + // process_info + // .envp + // .iter() + // .map(|arg| String::from_utf8_lossy(&*arg)) + // .for_each(|e: std::borrow::Cow<'_, str>| eprintln!(" {:?}", e.as_ref())); + eprintln!( + "\tprocess language: {:?}", + String::from_utf8_lossy(&*process_info.language).as_ref() + ); + } + } + + pub fn get_config_from_file( + &self, + path: &Path, + process_info: ProcessInfo<'_, impl Deref>, + ) -> anyhow::Result> { + let stable_config = match fs::File::open(path) { + Ok(file) => self.parse_stable_config(&mut io::BufReader::new(file))?, + Err(e) if e.kind() == io::ErrorKind::NotFound => StableConfig::default(), + Err(e) => return Err(e).context("failed to open config file"), + }; + self.get_config(&stable_config, process_info) + } + + pub fn get_config_from_bytes( + &self, + s: &[u8], + process_info: ProcessInfo<'_, impl Deref>, + ) -> anyhow::Result> { + let stable_config: StableConfig = self.parse_stable_config(&mut io::Cursor::new(s))?; + self.get_config(&stable_config, process_info) + } + + fn parse_stable_config(&self, f: &mut F) -> anyhow::Result { + let stable_config = serde_yaml::from_reader(f)?; + if self.debug_logs { + eprintln!("Read the following static config: {stable_config:?}"); + } + Ok(stable_config) + } + + fn get_config( + &self, + stable_config: &StableConfig, + process_info: ProcessInfo<'_, impl Deref>, + ) -> anyhow::Result> { + self.log_process_info(&process_info); + let Some(configs) = find_stable_config(stable_config, &process_info) else { + if self.debug_logs { + eprintln!("No selector matched"); + } + return Ok(Vec::new()); + }; + let library_config = template_configs(configs, &process_info)?; + if self.debug_logs { + eprintln!("Will apply the following configuration: {library_config:?}"); + } + Ok(library_config) + } +} + +#[cfg(test)] +mod tests { + use std::io::{Seek, Write}; + + use super::{template_config, Configurator, ProcessInfo}; + use crate::{LibraryConfigName, Operator, Origin, Rule, Selector, StableConfig}; + + macro_rules! map { + ($(($key:expr , $value:expr)),* $(,)?) => { + { + #[allow(unused_mut)] + let mut map = std::collections::HashMap::new(); + $( + map.insert($key, $value); + )* + map + } + }; + } + + #[test] + fn test_template_config() { + let config_template = "my_{{ language }}_service"; + let out = template_config( + config_template, + &ProcessInfo::<&[u8]> { + args: &[], + envp: &[], + language: b"java", + }, + ) + .expect("templating failed"); + assert_eq!(&out, "my_java_service"); + } + + #[test] + fn test_match_missing_config() { + let configurator = Configurator::new(true); + let cfg = configurator + .get_config_from_file( + "/file/is/missing".as_ref(), + ProcessInfo::<&[u8]> { + args: &[b"-jar HelloWorld.jar"], + envp: &[b"ENV=VAR"], + language: b"java", + }, + ) + .unwrap(); + assert_eq!(cfg, vec![]); + } + + #[test] + fn test_parse_static_config() { + let mut tmp = tempfile::NamedTempFile::new().unwrap(); + tmp.reopen() + .unwrap() + .write_all( + b" +rules: +- selectors: + - origin: language + matches: [\"java\"] + operator: equals + configuration: + DD_PROFILING_ENABLED: true + DD_SERVICE: my-service +", + ) + .unwrap(); + let configurator = Configurator::new(true); + let cfg = configurator.parse_stable_config(tmp.as_file_mut()).unwrap(); + assert_eq!( + cfg, + StableConfig { + rules: vec![Rule { + selectors: vec![Selector { + origin: Origin::Language, + matches: vec!["java".to_owned()], + operator: Operator::Equals, + }], + configuration: map![ + (LibraryConfigName::DdProfilingEnabled, "true".to_owned()), + (LibraryConfigName::DdService, "my-service".to_owned()) + ], + }] + } + ) + } + + #[test] + fn test_selector_match() { + let process_info = ProcessInfo::<&[u8]> { + args: &[b"-jar HelloWorld.jar"], + envp: &[b"ENV=VAR"], + language: b"java", + }; + let selector = Selector { + origin: Origin::Language, + matches: vec!["java".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::ProcessArguments, + matches: vec!["-jar HelloWorld.jar".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::EnvironmentVariable, + matches: vec!["ENV=VAR".to_owned()], + operator: Operator::Equals, + }; + assert!(super::selector_match(&selector, &process_info)); + + let selector = Selector { + origin: Origin::Language, + matches: vec!["python".to_owned()], + operator: Operator::Equals, + }; + assert!(!super::selector_match(&selector, &process_info)); + } +} From 8ba1544e1721e5ce84129c457fc9b25aeb0d3930 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 14 Jan 2025 20:31:58 +0100 Subject: [PATCH 15/26] Add dictionnary keys for tags and env variables --- library-config/src/lib.rs | 481 +++++++++++++++++++++++++++----------- 1 file changed, 349 insertions(+), 132 deletions(-) diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index 570568430d..26cd315dd1 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; +use std::cell::OnceCell; use std::collections::HashMap; use std::ops::Deref; use std::path::Path; @@ -5,6 +7,208 @@ use std::{fs, io}; use anyhow::Context; +/// This struct holds maps used to match and template configurations. +/// +/// They are computed lazily so that if the templating feature is not necessary, we don't +/// have to create the maps. +/// +/// Maps +/// * tags: This one is fairly simple, the format is tag_key: tag_value +/// * envs: Splits env variables based on the KEY=VALUE +/// * args: Either splits args base on key=value, or if the argument is a long arg +/// then parses --key value +struct MatchMaps<'a> { + tags: &'a HashMap, + env_map: OnceCell>, + args_map: OnceCell>, +} + +impl<'a> MatchMaps<'a> { + fn env( + &self, + process_info: &'a ProcessInfo<'a, impl Deref>, + ) -> &HashMap<&'a str, &'a str> { + self.env_map.get_or_init(|| { + let mut map = HashMap::new(); + for e in process_info.envp { + let Ok(s) = std::str::from_utf8(e.deref()) else { + continue; + }; + let (k, v) = match s.split_once('=') { + Some((k, v)) => (k, v), + None => (s, ""), + }; + map.insert(k, v); + } + map + }) + } + + fn args( + &self, + process_info: &'a ProcessInfo<'a, impl Deref>, + ) -> &HashMap<&str, &str> { + self.args_map.get_or_init(|| { + let mut map = HashMap::new(); + let mut args = process_info.args.iter().peekable(); + loop { + let Some(arg) = args.next() else { + break; + }; + let Ok(arg) = std::str::from_utf8(arg.deref()) else { + continue; + }; + if let Some((k, v)) = arg.split_once('=') { + map.insert(k, v); + } else if args + .peek() + .map(|next_arg| next_arg.starts_with(b"-")) + .unwrap_or(false) + { + let Ok(next) = std::str::from_utf8(args.next().unwrap()) else { + continue; + }; + map.insert(arg, next); + } + } + map + }) + } +} + +struct Matcher<'a, T: Deref> { + process_info: &'a ProcessInfo<'a, T>, + match_maps: MatchMaps<'a>, +} + +impl<'a, T: Deref> Matcher<'a, T> { + fn new(process_info: &'a ProcessInfo<'a, T>, tags: &'a HashMap) -> Self { + Self { + process_info, + match_maps: MatchMaps { + tags, + env_map: OnceCell::new(), + args_map: OnceCell::new(), + }, + } + } + + fn find_stable_config<'b>( + &'a self, + cfg: &'b StableConfig, + ) -> Option<&'b HashMap> { + for rule in &cfg.rules { + if rule.selectors.iter().all(|s| self.selector_match(s)) { + return Some(&rule.configuration); + } + } + None + } + + // Returns true if the selector matches the process info + // Any element in the "matches" section of the selector must match, they are ORed, + // as selectors are ANDed. + fn selector_match(&'a self, selector: &Selector) -> bool { + match selector.origin { + Origin::Language => string_selector(selector, self.process_info.language.deref()), + Origin::ProcessArguments => match &selector.key { + Some(key) => { + let arg_map = self.match_maps.args(&self.process_info); + map_operator_match(selector, arg_map, key) + } + None => string_list_selector(selector, self.process_info.args), + }, + Origin::EnvironmentVariable => match &selector.key { + Some(key) => { + let env_map = self.match_maps.env(&self.process_info); + map_operator_match(selector, env_map, key) + } + None => string_list_selector(selector, self.process_info.envp), + }, + Origin::Tags => match &selector.key { + Some(key) => map_operator_match(selector, self.match_maps.tags, key), + None => false, + }, + } + } + + fn template_configs( + &'a self, + config: &HashMap, + ) -> anyhow::Result> { + config + .iter() + .map(|(&name, v)| { + Ok(LibraryConfig { + name, + value: self.template_config(v)?, + }) + }) + .collect() + } + + /// Templates a config string. + /// + /// variables are enclosed in double curly brackets "{{" and "}}" + /// + /// For instance: + /// + /// with the following varriable definition, var = "abc" var2 = "def", this transforms \ + /// "foo_{{ var }}_bar_{{ var2 }}" -> "foo_abc_bar_def" + fn template_config(&'a self, config_val: &str) -> anyhow::Result { + let mut rest = config_val; + let mut templated = String::with_capacity(config_val.len()); + loop { + let Some((head, after_bracket)) = rest.split_once("{{") else { + templated.push_str(rest); + return Ok(templated); + }; + templated.push_str(head); + let Some((template_var, tail)) = after_bracket.split_once("}}") else { + anyhow::bail!("unterminated template in config") + }; + let (template_var, index) = parse_template_var(template_var.trim()); + let val = match template_var { + "language" => String::from_utf8_lossy(self.process_info.language.deref()), + "environment" => template_map_key(index, self.match_maps.env(self.process_info)), + "process_arguments" => { + template_map_key(index, self.match_maps.args(self.process_info)) + } + "tags" => template_map_key(index, self.match_maps.tags), + _ => std::borrow::Cow::Borrowed("UNDEFINED"), + }; + templated.push_str(&val); + rest = tail; + } + } +} + +fn map_operator_match<'a, 'b>(selector: &Selector, map: &'a impl Get, key: &'b str) -> bool { + let Some(val) = map.get(key) else { + return false; + }; + string_selector(selector, val.as_bytes()) +} + +fn parse_template_var(template_var: &str) -> (&str, Option<&str>) { + match template_var.trim().split_once("[") { + Some((template_var, idx)) => { + let Some((index, _)) = idx.split_once("]") else { + return (template_var, None); + }; + (template_var, Some(index.trim())) + } + None => (template_var, None), + } +} + +fn template_map_key<'a>(key: Option<&str>, map: &'a impl Get) -> Cow<'a, str> { + let Some(key) = key else { + return Cow::Borrowed("UNDEFINED"); + }; + Cow::Borrowed(map.get(key).unwrap_or("UNDEFINED")) +} + #[repr(C)] pub struct ProcessInfo<'a, T: Deref> { pub args: &'a [T], @@ -30,22 +234,28 @@ enum Origin { ProcessArguments, EnvironmentVariable, Language, + Tags, } #[derive(serde::Deserialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] +#[serde(tag = "operator")] enum Operator { - Equals, - PrefixMatches, - SuffixMatches, - // todox + Exists, + Equals { matches: Vec }, + PrefixMatches { matches: Vec }, + SuffixMatches { matches: Vec }, + // todo // WildcardMatches, } #[derive(serde::Deserialize, Debug, PartialEq, Eq)] struct Selector { origin: Origin, - matches: Vec, + #[serde(default)] + key: Option, + // matches: Vec, + #[serde(flatten)] operator: Operator, } @@ -57,36 +267,24 @@ struct Rule { #[derive(serde::Deserialize, Default, Debug, PartialEq, Eq)] struct StableConfig { + #[serde(default)] + tags: HashMap, rules: Vec, } -fn find_stable_config<'a, 'b>( - cfg: &'b StableConfig, - process_info: &'a ProcessInfo<'a, impl Deref>, -) -> Option<&'b HashMap> { - for rule in &cfg.rules { - if rule - .selectors - .iter() - .all(|s| selector_match(s, process_info)) - { - return Some(&rule.configuration); - } +trait Get { + fn get<'b>(&self, k: &'b str) -> Option<&str>; +} + +impl<'a> Get for HashMap<&'a str, &'a str> { + fn get<'b>(&self, k: &'b str) -> Option<&'a str> { + self.get(k).map(|v| *v) } - None } -// Returns true if the selector matches the process info -// Any element in the "matches" section of the selector must match, they are ORed, -// as selectors are ANDed. -fn selector_match<'a>( - selector: &Selector, - process_info: &'a ProcessInfo<'a, impl Deref>, -) -> bool { - match selector.origin { - Origin::Language => string_selector(selector, process_info.language.deref()), - Origin::ProcessArguments => string_list_selector(selector, process_info.args), - Origin::EnvironmentVariable => string_list_selector(selector, process_info.envp), +impl Get for HashMap { + fn get<'b>(&self, k: &'b str) -> Option<&str> { + self.get(k).map(|v| v.as_str()) } } @@ -94,18 +292,24 @@ fn string_list_selector<'a, B: Deref>(selector: &Selector, l: &'a l.into_iter().any(|v| string_selector(selector, v.deref())) } -fn string_selector(selector: &Selector, matches: &[u8]) -> bool { - selector - .matches +fn string_selector(selector: &Selector, value: &[u8]) -> bool { + let matches = match &selector.operator { + Operator::Exists => return true, + Operator::Equals { matches } => matches, + Operator::PrefixMatches { matches } => matches, + Operator::SuffixMatches { matches } => matches, + }; + matches .iter() - .any(|m| string_operator_match(&selector.operator, m.as_bytes(), matches)) + .any(|m| string_operator_match(&selector.operator, m.as_bytes(), value)) } fn string_operator_match(op: &Operator, matches: &[u8], value: &[u8]) -> bool { match op { - Operator::Equals => matches == value, - Operator::PrefixMatches => value.starts_with(matches), - Operator::SuffixMatches => value.ends_with(matches), + Operator::Equals { .. } => matches == value, + Operator::PrefixMatches { .. } => value.starts_with(matches), + Operator::SuffixMatches { .. } => value.ends_with(matches), + Operator::Exists => true, // Operator::WildcardMatches => todo!("Wildcard matches is not implemented"), } } @@ -116,54 +320,6 @@ pub struct LibraryConfig { pub value: String, } -fn template_configs<'a>( - config: &HashMap, - process_info: &'a ProcessInfo<'a, impl Deref>, -) -> anyhow::Result> { - config - .iter() - .map(|(&name, v)| { - Ok(LibraryConfig { - name, - value: template_config(v, process_info)?, - }) - }) - .collect() -} - -/// Templates a config string. -/// -/// variables are enclosed in double curly brackets "{{" and "}}" -/// -/// For instance: -/// -/// with the following varriable definition, var = "abc" var2 = "def", this transforms \ -/// "foo_{{ var }}_bar_{{ var2 }}" -> "foo_abc_bar_def" -fn template_config<'a>( - config_val: &str, - process_info: &'a ProcessInfo<'a, impl Deref>, -) -> anyhow::Result { - let mut rest = config_val; - let mut templated = String::with_capacity(config_val.len()); - loop { - let Some((head, after_bracket)) = rest.split_once("{{") else { - templated.push_str(rest); - return Ok(templated); - }; - templated.push_str(head); - let Some((template_var, tail)) = after_bracket.split_once("}}") else { - anyhow::bail!("unterminated template in config") - }; - let template_var = template_var.trim(); - let val = match template_var { - "language" => String::from_utf8_lossy(process_info.language.deref()), - _ => std::borrow::Cow::Borrowed("UNDEFINED"), - }; - templated.push_str(&val); - rest = tail; - } -} - #[derive(Debug)] pub struct Configurator { debug_logs: bool, @@ -183,7 +339,7 @@ impl Configurator { .args .iter() .map(|arg| String::from_utf8_lossy(&*arg)) - .for_each(|e| eprintln!(" {:?}", e.as_ref())); + .for_each(|e| eprintln!("\t\t{:?}", e.as_ref())); // TODO: this is for testing purpose, we don't want to log env variables // eprintln!("\tprocess envs:"); @@ -235,15 +391,16 @@ impl Configurator { process_info: ProcessInfo<'_, impl Deref>, ) -> anyhow::Result> { self.log_process_info(&process_info); - let Some(configs) = find_stable_config(stable_config, &process_info) else { + let matcher = Matcher::new(&process_info, &stable_config.tags); + let Some(configs) = matcher.find_stable_config(stable_config) else { if self.debug_logs { eprintln!("No selector matched"); } return Ok(Vec::new()); }; - let library_config = template_configs(configs, &process_info)?; + let library_config = matcher.template_configs(configs)?; if self.debug_logs { - eprintln!("Will apply the following configuration: {library_config:?}"); + eprintln!("Will apply the following configuration:\n\t{library_config:?}"); } Ok(library_config) } @@ -251,10 +408,12 @@ impl Configurator { #[cfg(test)] mod tests { - use std::io::{Seek, Write}; + use std::{collections::HashMap, io::Write}; - use super::{template_config, Configurator, ProcessInfo}; - use crate::{LibraryConfigName, Operator, Origin, Rule, Selector, StableConfig}; + use super::{Configurator, ProcessInfo}; + use crate::{ + LibraryConfig, LibraryConfigName, Matcher, Operator, Origin, Rule, Selector, StableConfig, + }; macro_rules! map { ($(($key:expr , $value:expr)),* $(,)?) => { @@ -270,20 +429,54 @@ mod tests { } #[test] - fn test_template_config() { - let config_template = "my_{{ language }}_service"; - let out = template_config( - config_template, - &ProcessInfo::<&[u8]> { - args: &[], - envp: &[], - language: b"java", - }, - ) - .expect("templating failed"); - assert_eq!(&out, "my_java_service"); + fn test_get_config() { + let process_info: ProcessInfo<'_, &[u8]> = ProcessInfo::<&[u8]> { + args: &[b"-Djava_config_key=my_config", b"-jar", b"HelloWorld.jar"], + envp: &[b"ENV=VAR"], + language: b"java", + }; + let configurator = Configurator::new(true); + let config = configurator.get_config_from_bytes(b" +tags: + cluster_name: my_cluster +rules: +- selectors: + - origin: language + matches: [\"java\"] + operator: equals + - origin: process_arguments + key: \"-Djava_config_key\" + operator: exists + - origin: process_arguments + matches: [\"HelloWorld.jar\"] + operator: equals + configuration: + DD_SERVICE: my_service_{{ tags[cluster_name] }}_{{ process_arguments[-Djava_config_key] }}_{{ language }} +", process_info).unwrap(); + assert_eq!( + config, + vec![LibraryConfig { + name: LibraryConfigName::DdService, + value: "my_service_my_cluster_my_config_java".to_string() + }] + ); } + // #[test] + // fn test_template_config() { + // let config_template = "my_{{ language }}_service"; + // let out = template_config( + // config_template, + // &ProcessInfo::<&[u8]> { + // args: &[], + // envp: &[], + // language: b"java", + // }, + // ) + // .expect("templating failed"); + // assert_eq!(&out, "my_java_service"); + // } + #[test] fn test_match_missing_config() { let configurator = Configurator::new(true); @@ -323,11 +516,14 @@ rules: assert_eq!( cfg, StableConfig { + tags: HashMap::default(), rules: vec![Rule { selectors: vec![Selector { origin: Origin::Language, - matches: vec!["java".to_owned()], - operator: Operator::Equals, + operator: Operator::Equals { + matches: vec!["java".to_owned()] + }, + key: None, }], configuration: map![ (LibraryConfigName::DdProfilingEnabled, "true".to_owned()), @@ -345,32 +541,53 @@ rules: envp: &[b"ENV=VAR"], language: b"java", }; - let selector = Selector { - origin: Origin::Language, - matches: vec!["java".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::ProcessArguments, - matches: vec!["-jar HelloWorld.jar".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::EnvironmentVariable, - matches: vec!["ENV=VAR".to_owned()], - operator: Operator::Equals, - }; - assert!(super::selector_match(&selector, &process_info)); - - let selector = Selector { - origin: Origin::Language, - matches: vec!["python".to_owned()], - operator: Operator::Equals, - }; - assert!(!super::selector_match(&selector, &process_info)); + let tags = HashMap::new(); + let matcher = Matcher::new(&process_info, &tags); + + let test_cases = &[ + ( + Selector { + key: None, + origin: Origin::Language, + operator: Operator::Equals { + matches: vec!["java".to_owned()], + }, + }, + true, + ), + ( + Selector { + key: None, + origin: Origin::ProcessArguments, + operator: Operator::Equals { + matches: vec!["-jar HelloWorld.jar".to_owned()], + }, + }, + true, + ), + ( + Selector { + key: None, + origin: Origin::EnvironmentVariable, + operator: Operator::Equals { + matches: vec!["ENV=VAR".to_owned()], + }, + }, + true, + ), + ( + Selector { + key: None, + origin: Origin::Language, + operator: Operator::Equals { + matches: vec!["python".to_owned()], + }, + }, + false, + ), + ]; + for (i, (selector, matches)) in test_cases.iter().enumerate() { + assert_eq!(matcher.selector_match(&selector), *matches, "case {i}"); + } } } From 6349c50cd4f2fbdc7a10836078842b91334977f5 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 14 Jan 2025 20:37:09 +0100 Subject: [PATCH 16/26] Apply lints --- library-config-ffi/src/lib.rs | 4 +- library-config-ffi/src/static_config.rs | 142 ------------------------ library-config/src/lib.rs | 26 ++--- 3 files changed, 15 insertions(+), 157 deletions(-) delete mode 100644 library-config-ffi/src/static_config.rs diff --git a/library-config-ffi/src/lib.rs b/library-config-ffi/src/lib.rs index d47f998b73..c3c6e19f52 100644 --- a/library-config-ffi/src/lib.rs +++ b/library-config-ffi/src/lib.rs @@ -22,8 +22,8 @@ fn cast_slice_of_slice_ref<'a, 'b>( s: &'b ffi::Slice<'a, ffi::CharSlice<'a>>, ) -> &'b ffi::Slice<'a, ffi::slice::ByteSlice<'a>> { // Safety: - // ffi::CharSlice and ffi::slice::ByteSlice have the same layout, and since they are wrappers around - // *const char i8 and *const char u8 respectively, they are trivially interchangeable + // ffi::CharSlice and ffi::slice::ByteSlice have the same layout, and since they are wrappers + // around *const char i8 and *const char u8 respectively, they are trivially interchangeable unsafe { std::mem::transmute(s) } } diff --git a/library-config-ffi/src/static_config.rs b/library-config-ffi/src/static_config.rs deleted file mode 100644 index bc380c56e7..0000000000 --- a/library-config-ffi/src/static_config.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::anyhow; -use ddcommon::cstr; -use ddcommon_ffi::{self as ffi}; -use std::{io::ErrorKind, path::PathBuf}; - - -#[derive(Debug)] -pub struct Configurator { - pub debug_logs: bool, - #[allow(dead_code)] - pub static_config_file_path: PathBuf, -} - -#[repr(C)] -#[derive(Clone, Copy, serde::Deserialize, Debug, PartialEq, Eq, Hash)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -#[allow(clippy::enum_variant_names)] -pub enum LibraryConfigName { - DdTraceDebug = 0, - DdService = 1, - DdEnv = 2, - DdVersion = 3, - DdProfilingEnabled = 4, -} - -impl LibraryConfigName { - pub fn to_env_name(self) -> &'static std::ffi::CStr { - use LibraryConfigName::*; - match self { - DdTraceDebug => cstr!("DD_TRACE_DEBUG"), - DdService => cstr!("DD_SERVICE"), - DdEnv => cstr!("DD_ENV"), - DdVersion => cstr!("DD_VERSION"), - DdProfilingEnabled => cstr!("DD_PROFILING_ENABLED"), - } - } -} - -#[repr(C)] -pub struct ProcessInfo<'a> { - pub args: ffi::Slice<'a, ffi::CharSlice<'a>>, - pub envp: ffi::Slice<'a, ffi::CharSlice<'a>>, - pub language: ffi::CharSlice<'a>, -} - -impl Configurator { - pub fn new(debug_logs: bool, static_config_file_path: PathBuf) -> Self { - Self { - debug_logs, - static_config_file_path, - } - } - - pub fn get_configuration( - &self, - process_info: ProcessInfo<'_>, - ) -> anyhow::Result> { - let static_config = self.parse_static_config()?; - if self.debug_logs { - eprintln!("Read the following static config: {static_config:?}"); - } - - let Some(configs) = find_static_config(&static_config, &process_info) else { - if self.debug_logs { - eprintln!("No selector matched"); - } - return Ok(Vec::new()); - }; - let library_config = template_configs(configs, &process_info)?; - if self.debug_logs { - eprintln!("Will apply the following configuration: {library_config:?}"); - } - Ok(library_config) - } - - pub fn get_configuration_from_bytes( - &self, - process_info: ProcessInfo<'_>, - config_bytes: ffi::CharSlice<'_>, - ) -> anyhow::Result> { - let static_config = serde_yaml::from_str(config_bytes.to_string().as_str())?; - if self.debug_logs { - eprintln!("Read the following static config: {static_config:?}"); - } - - let Some(configs) = find_static_config(&static_config, &process_info) else { - if self.debug_logs { - eprintln!("No selector matched"); - } - return Ok(Vec::new()); - }; - let library_config = template_configs(configs, &process_info)?; - if self.debug_logs { - eprintln!("Will apply the following configuration: {library_config:?}"); - } - Ok(library_config) - } - - pub fn log_process_info(&self, process_info: &ProcessInfo) { - if self.debug_logs { - eprintln!("Called library_config_common_component:"); - eprintln!("\tconfigurator: {:?}", self); - let args: Vec = process_info - .args - .iter() - .map(|arg| arg.to_string()) - .collect(); - eprintln!("\tprocess args: {:?}", args); - // TODO: this is for testing purpose, we don't want to log env variables - let envs: Vec = process_info - .envp - .iter() - .map(|env| env.to_string()) - .collect(); - eprintln!("\tprocess envs: {:?}", envs); - eprintln!( - "\tprocess language: {:?}", - process_info.language.to_string() - ); - } - } - - fn parse_static_config(&self) -> anyhow::Result { - let mut f = match std::fs::File::open(&self.static_config_file_path) { - Ok(f) => f, - Err(e) if e.kind() == ErrorKind::NotFound => { - if self.debug_logs { - eprintln!( - "Static config file not found at {:?} returning empty rules", - self.static_config_file_path - ); - } - return Ok(StaticConfig::default()); - } - Err(e) => return Err(anyhow!(e)), - }; - Ok(serde_yaml::from_reader(&mut f)?) - } -} diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index 26cd315dd1..e332c597a1 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -15,8 +15,8 @@ use anyhow::Context; /// Maps /// * tags: This one is fairly simple, the format is tag_key: tag_value /// * envs: Splits env variables based on the KEY=VALUE -/// * args: Either splits args base on key=value, or if the argument is a long arg -/// then parses --key value +/// * args: Either splits args base on key=value, or if the argument is a long arg then parses +/// --key value struct MatchMaps<'a> { tags: &'a HashMap, env_map: OnceCell>, @@ -113,14 +113,14 @@ impl<'a, T: Deref> Matcher<'a, T> { Origin::Language => string_selector(selector, self.process_info.language.deref()), Origin::ProcessArguments => match &selector.key { Some(key) => { - let arg_map = self.match_maps.args(&self.process_info); + let arg_map = self.match_maps.args(self.process_info); map_operator_match(selector, arg_map, key) } None => string_list_selector(selector, self.process_info.args), }, Origin::EnvironmentVariable => match &selector.key { Some(key) => { - let env_map = self.match_maps.env(&self.process_info); + let env_map = self.match_maps.env(self.process_info); map_operator_match(selector, env_map, key) } None => string_list_selector(selector, self.process_info.envp), @@ -183,7 +183,7 @@ impl<'a, T: Deref> Matcher<'a, T> { } } -fn map_operator_match<'a, 'b>(selector: &Selector, map: &'a impl Get, key: &'b str) -> bool { +fn map_operator_match(selector: &Selector, map: &impl Get, key: &str) -> bool { let Some(val) = map.get(key) else { return false; }; @@ -273,23 +273,23 @@ struct StableConfig { } trait Get { - fn get<'b>(&self, k: &'b str) -> Option<&str>; + fn get(&self, k: &str) -> Option<&str>; } impl<'a> Get for HashMap<&'a str, &'a str> { - fn get<'b>(&self, k: &'b str) -> Option<&'a str> { - self.get(k).map(|v| *v) + fn get(&self, k: &str) -> Option<&'a str> { + self.get(k).copied() } } impl Get for HashMap { - fn get<'b>(&self, k: &'b str) -> Option<&str> { + fn get(&self, k: &str) -> Option<&str> { self.get(k).map(|v| v.as_str()) } } -fn string_list_selector<'a, B: Deref>(selector: &Selector, l: &'a [B]) -> bool { - l.into_iter().any(|v| string_selector(selector, v.deref())) +fn string_list_selector>(selector: &Selector, l: &[B]) -> bool { + l.iter().any(|v| string_selector(selector, v.deref())) } fn string_selector(selector: &Selector, value: &[u8]) -> bool { @@ -338,7 +338,7 @@ impl Configurator { process_info .args .iter() - .map(|arg| String::from_utf8_lossy(&*arg)) + .map(|arg| String::from_utf8_lossy(arg)) .for_each(|e| eprintln!("\t\t{:?}", e.as_ref())); // TODO: this is for testing purpose, we don't want to log env variables @@ -350,7 +350,7 @@ impl Configurator { // .for_each(|e: std::borrow::Cow<'_, str>| eprintln!(" {:?}", e.as_ref())); eprintln!( "\tprocess language: {:?}", - String::from_utf8_lossy(&*process_info.language).as_ref() + String::from_utf8_lossy(&process_info.language).as_ref() ); } } From 933368f686152aba9a86310489ac504b1492332e Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Tue, 14 Jan 2025 20:40:14 +0100 Subject: [PATCH 17/26] Add license header --- library-config/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index e332c597a1..e051b477d6 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + use std::borrow::Cow; use std::cell::OnceCell; use std::collections::HashMap; From 3279aabaff745ccf0489567fb07754e4b6874750 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Wed, 15 Jan 2025 00:32:40 +0100 Subject: [PATCH 18/26] clippy --- library-config/src/lib.rs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index e051b477d6..a04e6b645f 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -194,9 +194,9 @@ fn map_operator_match(selector: &Selector, map: &impl Get, key: &str) -> bool { } fn parse_template_var(template_var: &str) -> (&str, Option<&str>) { - match template_var.trim().split_once("[") { + match template_var.trim().split_once('[') { Some((template_var, idx)) => { - let Some((index, _)) = idx.split_once("]") else { + let Some((index, _)) = idx.split_once(']') else { return (template_var, None); }; (template_var, Some(index.trim())) @@ -465,21 +465,6 @@ rules: ); } - // #[test] - // fn test_template_config() { - // let config_template = "my_{{ language }}_service"; - // let out = template_config( - // config_template, - // &ProcessInfo::<&[u8]> { - // args: &[], - // envp: &[], - // language: b"java", - // }, - // ) - // .expect("templating failed"); - // assert_eq!(&out, "my_java_service"); - // } - #[test] fn test_match_missing_config() { let configurator = Configurator::new(true); From 7ef913070985ab6b2b9cc7c3165146bb367ff862 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Wed, 15 Jan 2025 00:35:33 +0100 Subject: [PATCH 19/26] Update 3rd party locense file --- LICENSE-3rdparty.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE-3rdparty.yml b/LICENSE-3rdparty.yml index e19a5fb36e..cfc857ebf0 100644 --- a/LICENSE-3rdparty.yml +++ b/LICENSE-3rdparty.yml @@ -1,4 +1,4 @@ -root_name: datadog-alloc, builder, build_common, tools, datadog-crashtracker, ddcommon, ddtelemetry, datadog-ddsketch, datadog-crashtracker-ffi, ddcommon-ffi, datadog-profiling, datadog-profiling-ffi, data-pipeline-ffi, data-pipeline, datadog-trace-normalization, datadog-trace-protobuf, datadog-trace-obfuscation, datadog-trace-utils, tinybytes, dogstatsd-client, datadog-library-config-ffi, ddtelemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, dogstatsd, datadog-ipc, datadog-ipc-macros, tarpc, tarpc-plugins, spawn_worker, cc_utils, datadog-live-debugger, datadog-live-debugger-ffi, datadog-remote-config, datadog-dynamic-configuration, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, sidecar_mockgen, test_spawn_from_lib, datadog-serverless-trace-mini-agent, datadog-trace-mini-agent +root_name: datadog-alloc, builder, build_common, tools, datadog-crashtracker, ddcommon, ddtelemetry, datadog-ddsketch, datadog-crashtracker-ffi, ddcommon-ffi, datadog-profiling, datadog-profiling-ffi, data-pipeline-ffi, data-pipeline, datadog-trace-normalization, datadog-trace-protobuf, datadog-trace-obfuscation, datadog-trace-utils, tinybytes, dogstatsd-client, datadog-library-config-ffi, datadog-library-config, ddtelemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, dogstatsd, datadog-ipc, datadog-ipc-macros, tarpc, tarpc-plugins, spawn_worker, cc_utils, datadog-live-debugger, datadog-live-debugger-ffi, datadog-remote-config, datadog-dynamic-configuration, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, sidecar_mockgen, test_spawn_from_lib, datadog-serverless-trace-mini-agent, datadog-trace-mini-agent third_party_libraries: - package_name: addr2line package_version: 0.24.2 From 2d85a059e10118798a2335688b8cbc246fe671bf Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Wed, 15 Jan 2025 01:01:53 +0100 Subject: [PATCH 20/26] Fix docker build --- tools/docker/Dockerfile.build | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/docker/Dockerfile.build b/tools/docker/Dockerfile.build index d55b9c10bb..3236b54b4e 100644 --- a/tools/docker/Dockerfile.build +++ b/tools/docker/Dockerfile.build @@ -81,6 +81,7 @@ COPY "dogstatsd/Cargo.toml" "dogstatsd/" COPY "dogstatsd-client/Cargo.toml" "dogstatsd-client/" COPY "dynamic-configuration/Cargo.toml" "dynamic-configuration/" COPY "library-config-ffi/Cargo.toml" "library-config-ffi/" +COPY "library-config/Cargo.toml" "library-config/" COPY "live-debugger/Cargo.toml" "live-debugger/" COPY "live-debugger-ffi/Cargo.toml" "live-debugger-ffi/" COPY "profiling/Cargo.toml" "profiling/" From 4c444b9bbe3e61c8b1491a94d3786fd89e4d31cb Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Wed, 15 Jan 2025 14:01:56 +0100 Subject: [PATCH 21/26] lints --- LICENSE-3rdparty.yml | 96 +++++++++++++++++++++++++++++++++++---- library-config/src/lib.rs | 2 +- 2 files changed, 88 insertions(+), 10 deletions(-) diff --git a/LICENSE-3rdparty.yml b/LICENSE-3rdparty.yml index cfc857ebf0..5e2171bf9b 100644 --- a/LICENSE-3rdparty.yml +++ b/LICENSE-3rdparty.yml @@ -22297,16 +22297,16 @@ third_party_libraries: - package_name: ring package_version: 0.16.20 repository: https://github.com/briansmith/ring - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/ring-0.16.20/LICENSE) + license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/ring-0.16.20/LICENSE) licenses: - - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/ring-0.16.20/LICENSE) + - license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/ring-0.16.20/LICENSE) text: "Note that it is easy for this file to get out of sync with the licenses in the\nsource code files. It's recommended to compare the licenses in the source code\nwith what's mentioned here.\n\n*ring* is derived from BoringSSL, so the licensing situation in *ring* is\nsimilar to BoringSSL.\n\n*ring* uses an ISC-style license like BoringSSL for code in new files,\nincluding in particular all the Rust code:\n\n Copyright 2015-2016 Brian Smith.\n\n Permission to use, copy, modify, and/or distribute this software for any\n purpose with or without fee is hereby granted, provided that the above\n copyright notice and this permission notice appear in all copies.\n\n THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\n WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY\n SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nBoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL\nlicensing. Files that are completely new have a Google copyright and an ISC\nlicense. This license is reproduced at the bottom of this file.\n\nContributors to BoringSSL are required to follow the CLA rules for Chromium:\nhttps://cla.developers.google.com/clas\n\nFiles in third_party/ have their own licenses, as described therein. The MIT\nlicense, for third_party/fiat, which, unlike other third_party directories, is\ncompiled into non-test libraries, is included below.\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the\nOpenSSL License and the original SSLeay license apply to the toolkit. See below\nfor the actual license texts. Actually both licenses are BSD-style Open Source\nlicenses. In case of any license issues related to OpenSSL please contact\nopenssl-core@openssl.org.\n\nThe following are Google-internal bug numbers where explicit permission from\nsome authors is recorded for use of their work:\n 27287199\n 27287880\n 27287883\n\n OpenSSL License\n ---------------\n\n/* ====================================================================\n * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer. \n *\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in\n * the documentation and/or other materials provided with the\n * distribution.\n *\n * 3. All advertising materials mentioning features or use of this\n * software must display the following acknowledgment:\n * \"This product includes software developed by the OpenSSL Project\n * for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n *\n * 4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to\n * endorse or promote products derived from this software without\n * prior written permission. For written permission, please contact\n * openssl-core@openssl.org.\n *\n * 5. Products derived from this software may not be called \"OpenSSL\"\n * nor may \"OpenSSL\" appear in their names without prior written\n * permission of the OpenSSL Project.\n *\n * 6. Redistributions of any form whatsoever must retain the following\n * acknowledgment:\n * \"This product includes software developed by the OpenSSL Project\n * for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n *\n * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY\n * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR\n * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n * ====================================================================\n *\n * This product includes cryptographic software written by Eric Young\n * (eay@cryptsoft.com). This product includes software written by Tim\n * Hudson (tjh@cryptsoft.com).\n *\n */\n\n Original SSLeay License\n -----------------------\n\n/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\n * All rights reserved.\n *\n * This package is an SSL implementation written\n * by Eric Young (eay@cryptsoft.com).\n * The implementation was written so as to conform with Netscapes SSL.\n * \n * This library is free for commercial and non-commercial use as long as\n * the following conditions are aheared to. The following conditions\n * apply to all code found in this distribution, be it the RC4, RSA,\n * lhash, DES, etc., code; not just the SSL code. The SSL documentation\n * included with this distribution is covered by the same copyright terms\n * except that the holder is Tim Hudson (tjh@cryptsoft.com).\n * \n * Copyright remains Eric Young's, and as such any Copyright notices in\n * the code are not to be removed.\n * If this package is used in a product, Eric Young should be given attribution\n * as the author of the parts of the library used.\n * This can be in the form of a textual message at program startup or\n * in documentation (online or textual) provided with the package.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. All advertising materials mentioning features or use of this software\n * must display the following acknowledgement:\n * \"This product includes cryptographic software written by\n * Eric Young (eay@cryptsoft.com)\"\n * The word 'cryptographic' can be left out if the rouines from the library\n * being used are not cryptographic related :-).\n * 4. If you include any Windows specific code (or a derivative thereof) from \n * the apps directory (application code) you must include an acknowledgement:\n * \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n * \n * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n * \n * The licence and distribution terms for any publically available version or\n * derivative of this code cannot be changed. i.e. this code cannot simply be\n * copied and put under another distribution licence\n * [including the GNU Public Licence.]\n */\n\n\nISC license used for completely new code in BoringSSL:\n\n/* Copyright (c) 2015, Google Inc.\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */\n\n\nThe code in third_party/fiat carries the MIT license:\n\nCopyright (c) 2015-2016 the fiat-crypto authors (see\nhttps://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - package_name: ring package_version: 0.17.8 repository: https://github.com/briansmith/ring - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.8/LICENSE) + license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/ring-0.17.8/LICENSE) licenses: - - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.8/LICENSE) + - license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/ring-0.17.8/LICENSE) text: "Note that it is easy for this file to get out of sync with the licenses in the\nsource code files. It's recommended to compare the licenses in the source code\nwith what's mentioned here.\n\n*ring* is derived from BoringSSL, so the licensing situation in *ring* is\nsimilar to BoringSSL.\n\n*ring* uses an ISC-style license like BoringSSL for code in new files,\nincluding in particular all the Rust code:\n\n Copyright 2015-2016 Brian Smith.\n\n Permission to use, copy, modify, and/or distribute this software for any\n purpose with or without fee is hereby granted, provided that the above\n copyright notice and this permission notice appear in all copies.\n\n THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\n WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY\n SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nBoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL\nlicensing. Files that are completely new have a Google copyright and an ISC\nlicense. This license is reproduced at the bottom of this file.\n\nContributors to BoringSSL are required to follow the CLA rules for Chromium:\nhttps://cla.developers.google.com/clas\n\nFiles in third_party/ have their own licenses, as described therein. The MIT\nlicense, for third_party/fiat, which, unlike other third_party directories, is\ncompiled into non-test libraries, is included below.\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the\nOpenSSL License and the original SSLeay license apply to the toolkit. See below\nfor the actual license texts. Actually both licenses are BSD-style Open Source\nlicenses. In case of any license issues related to OpenSSL please contact\nopenssl-core@openssl.org.\n\nThe following are Google-internal bug numbers where explicit permission from\nsome authors is recorded for use of their work:\n 27287199\n 27287880\n 27287883\n\n OpenSSL License\n ---------------\n\n/* ====================================================================\n * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer. \n *\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in\n * the documentation and/or other materials provided with the\n * distribution.\n *\n * 3. All advertising materials mentioning features or use of this\n * software must display the following acknowledgment:\n * \"This product includes software developed by the OpenSSL Project\n * for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n *\n * 4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to\n * endorse or promote products derived from this software without\n * prior written permission. For written permission, please contact\n * openssl-core@openssl.org.\n *\n * 5. Products derived from this software may not be called \"OpenSSL\"\n * nor may \"OpenSSL\" appear in their names without prior written\n * permission of the OpenSSL Project.\n *\n * 6. Redistributions of any form whatsoever must retain the following\n * acknowledgment:\n * \"This product includes software developed by the OpenSSL Project\n * for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n *\n * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY\n * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR\n * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n * ====================================================================\n *\n * This product includes cryptographic software written by Eric Young\n * (eay@cryptsoft.com). This product includes software written by Tim\n * Hudson (tjh@cryptsoft.com).\n *\n */\n\n Original SSLeay License\n -----------------------\n\n/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\n * All rights reserved.\n *\n * This package is an SSL implementation written\n * by Eric Young (eay@cryptsoft.com).\n * The implementation was written so as to conform with Netscapes SSL.\n * \n * This library is free for commercial and non-commercial use as long as\n * the following conditions are aheared to. The following conditions\n * apply to all code found in this distribution, be it the RC4, RSA,\n * lhash, DES, etc., code; not just the SSL code. The SSL documentation\n * included with this distribution is covered by the same copyright terms\n * except that the holder is Tim Hudson (tjh@cryptsoft.com).\n * \n * Copyright remains Eric Young's, and as such any Copyright notices in\n * the code are not to be removed.\n * If this package is used in a product, Eric Young should be given attribution\n * as the author of the parts of the library used.\n * This can be in the form of a textual message at program startup or\n * in documentation (online or textual) provided with the package.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. All advertising materials mentioning features or use of this software\n * must display the following acknowledgement:\n * \"This product includes cryptographic software written by\n * Eric Young (eay@cryptsoft.com)\"\n * The word 'cryptographic' can be left out if the rouines from the library\n * being used are not cryptographic related :-).\n * 4. If you include any Windows specific code (or a derivative thereof) from \n * the apps directory (application code) you must include an acknowledgement:\n * \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n * \n * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n * \n * The licence and distribution terms for any publically available version or\n * derivative of this code cannot be changed. i.e. this code cannot simply be\n * copied and put under another distribution licence\n * [including the GNU Public Licence.]\n */\n\n\nISC license used for completely new code in BoringSSL:\n\n/* Copyright (c) 2015, Google Inc.\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */\n\n\nThe code in third_party/fiat carries the MIT license:\n\nCopyright (c) 2015-2016 the fiat-crypto authors (see\nhttps://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - package_name: rmp package_version: 0.8.14 @@ -24372,7 +24372,37 @@ third_party_libraries: limitations under the License. - license: BSD-3-Clause - text: NOT FOUND + text: |+ + BSD 3-Clause License + + Copyright (c) 2019, Standard Cognition + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + - package_name: serde package_version: 1.0.214 repository: https://github.com/serde-rs/serde @@ -33872,9 +33902,9 @@ third_party_libraries: - package_name: webpki package_version: 0.21.4 repository: https://github.com/briansmith/webpki - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/webpki-0.21.4/LICENSE) + license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/webpki-0.21.4/LICENSE) licenses: - - license: License specified in file ($CARGO_HOME/registry/src/index.crates.io-6f17d22bba15001f/webpki-0.21.4/LICENSE) + - license: License specified in file ($CARGO_HOME/registry/src/github.com-1ecc6299db9ec823/webpki-0.21.4/LICENSE) text: | Except as otherwise noted, this project is licensed under the following (ISC-style) terms: @@ -42545,7 +42575,31 @@ third_party_libraries: license: BSD-2-Clause OR Apache-2.0 OR MIT licenses: - license: BSD-2-Clause - text: NOT FOUND + text: | + Copyright 2019 The Fuchsia Authors. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - license: Apache-2.0 text: |2+ Apache License @@ -42784,7 +42838,31 @@ third_party_libraries: license: BSD-2-Clause OR Apache-2.0 OR MIT licenses: - license: BSD-2-Clause - text: NOT FOUND + text: | + Copyright 2019 The Fuchsia Authors. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - license: Apache-2.0 text: |2+ Apache License diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index a04e6b645f..7bf9155617 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -575,7 +575,7 @@ rules: ), ]; for (i, (selector, matches)) in test_cases.iter().enumerate() { - assert_eq!(matcher.selector_match(&selector), *matches, "case {i}"); + assert_eq!(matcher.selector_match(selector), *matches, "case {i}"); } } } From 08fa3d9bfe8f698bfde20b5ec30fce6816a07319 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Wed, 15 Jan 2025 14:26:07 +0100 Subject: [PATCH 22/26] impl LibraryConfigName --- library-config/src/lib.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index 7bf9155617..0ee1b7348c 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -121,7 +121,7 @@ impl<'a, T: Deref> Matcher<'a, T> { } None => string_list_selector(selector, self.process_info.args), }, - Origin::EnvironmentVariable => match &selector.key { + Origin::EnvironmentVariables => match &selector.key { Some(key) => { let env_map = self.match_maps.env(self.process_info); map_operator_match(selector, env_map, key) @@ -173,7 +173,9 @@ impl<'a, T: Deref> Matcher<'a, T> { let (template_var, index) = parse_template_var(template_var.trim()); let val = match template_var { "language" => String::from_utf8_lossy(self.process_info.language.deref()), - "environment" => template_map_key(index, self.match_maps.env(self.process_info)), + "environment_variables" => { + template_map_key(index, self.match_maps.env(self.process_info)) + } "process_arguments" => { template_map_key(index, self.match_maps.args(self.process_info)) } @@ -231,11 +233,24 @@ pub enum LibraryConfigName { DdProfilingEnabled = 4, } +impl LibraryConfigName { + pub fn to_str(&self) -> &'static str { + use LibraryConfigName::*; + match self { + DdTraceDebug => "DD_TRACE_DEBUG", + DdService => "DD_SERVICE", + DdEnv => "DD_ENV", + DdVersion => "DD_VERSION", + DdProfilingEnabled => "DD_PROFILING_ENABLED", + } + } +} + #[derive(serde::Deserialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] enum Origin { ProcessArguments, - EnvironmentVariable, + EnvironmentVariables, Language, Tags, } @@ -556,7 +571,7 @@ rules: ( Selector { key: None, - origin: Origin::EnvironmentVariable, + origin: Origin::EnvironmentVariables, operator: Operator::Equals { matches: vec!["ENV=VAR".to_owned()], }, From 16a118e50c26a47286bf287df1c577ffd1a92eae Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Thu, 16 Jan 2025 15:44:49 +0100 Subject: [PATCH 23/26] * Fix typos * Change builder feature name to be consistent with other ones --- build-profiling-ffi.sh | 2 +- builder/Cargo.toml | 2 +- builder/src/bin/release.rs | 2 +- builder/src/profiling.rs | 2 +- profiling-ffi/src/lib.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build-profiling-ffi.sh b/build-profiling-ffi.sh index 12a6497fb3..a0738ace09 100755 --- a/build-profiling-ffi.sh +++ b/build-profiling-ffi.sh @@ -228,7 +228,7 @@ rm -r $destdir/include/datadog/ mkdir $destdir/include/datadog/ CBINDGEN_HEADERS="common.h profiling.h telemetry.h crashtracker.h data-pipeline.h" -# When optional features are added, don't forget to also include thei headers here +# When optional features are added, don't forget to also include the headers here case $ARG_FEATURES in datadog-library-config-ffi) CBINDGEN_HEADERS="$CBINDGEN_HEADERS library-config.h" esac diff --git a/builder/Cargo.toml b/builder/Cargo.toml index 5c6e44d5af..fefd2639fa 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -13,7 +13,7 @@ profiling = [] telemetry = [] data-pipeline = [] symbolizer = [] -datadog-library-config-ffi = [] +library-config = [] [lib] bench = false diff --git a/builder/src/bin/release.rs b/builder/src/bin/release.rs index 91fabc5ad1..3168b5a36b 100644 --- a/builder/src/bin/release.rs +++ b/builder/src/bin/release.rs @@ -72,7 +72,7 @@ pub fn main() { f.push("crashtracker-ffi".to_string()); #[cfg(feature = "symbolizer")] f.push("symbolizer".to_string()); - #[cfg(feature = "datadog-library-config-ffi")] + #[cfg(feature = "library-config")] f.push("datadog-library-config-ffi".to_string()); f }; diff --git a/builder/src/profiling.rs b/builder/src/profiling.rs index 80463fcb39..a6b9487174 100644 --- a/builder/src/profiling.rs +++ b/builder/src/profiling.rs @@ -37,7 +37,7 @@ impl Profiling { headers.push("data-pipeline.h"); #[cfg(feature = "symbolizer")] headers.push("blazesym.h"); - #[cfg(feature = "datadog-library-config-ffi")] + #[cfg(feature = "library-config")] headers.push("library-config.h"); let mut origin_path: PathBuf = [&self.source_include, "dummy.h"].iter().collect(); diff --git a/profiling-ffi/src/lib.rs b/profiling-ffi/src/lib.rs index 6cb53579ff..465547b1cd 100644 --- a/profiling-ffi/src/lib.rs +++ b/profiling-ffi/src/lib.rs @@ -19,6 +19,6 @@ pub use ddtelemetry_ffi::*; #[allow(unused_imports)] pub use data_pipeline_ffi::*; -// re-export crashtracker ffi +// re-export library-config ffi #[cfg(feature = "datadog-library-config-ffi")] pub use datadog_library_config_ffi::*; From f6856271459a98fce3ddce07ee4d2836d9056f8f Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Thu, 16 Jan 2025 16:40:34 +0100 Subject: [PATCH 24/26] Add a few more comments --- library-config/src/lib.rs | 40 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/library-config/src/lib.rs b/library-config/src/lib.rs index 0ee1b7348c..e2eadc1bba 100644 --- a/library-config/src/lib.rs +++ b/library-config/src/lib.rs @@ -15,11 +15,10 @@ use anyhow::Context; /// They are computed lazily so that if the templating feature is not necessary, we don't /// have to create the maps. /// -/// Maps +/// These maps come from one of three origins: /// * tags: This one is fairly simple, the format is tag_key: tag_value -/// * envs: Splits env variables based on the KEY=VALUE -/// * args: Either splits args base on key=value, or if the argument is a long arg then parses -/// --key value +/// * envs: Splits env variables with format KEY=VALUE +/// * args: Splits args with format key=value. If the arg doesn't contain an '=', skip it struct MatchMaps<'a> { tags: &'a HashMap, env_map: OnceCell>, @@ -61,17 +60,10 @@ impl<'a> MatchMaps<'a> { let Ok(arg) = std::str::from_utf8(arg.deref()) else { continue; }; + // Split args between key and value on '=' if let Some((k, v)) = arg.split_once('=') { map.insert(k, v); - } else if args - .peek() - .map(|next_arg| next_arg.starts_with(b"-")) - .unwrap_or(false) - { - let Ok(next) = std::str::from_utf8(args.next().unwrap()) else { - continue; - }; - map.insert(arg, next); + continue; } } map @@ -96,6 +88,7 @@ impl<'a, T: Deref> Matcher<'a, T> { } } + /// Returns the first set of configurations that match the current process fn find_stable_config<'b>( &'a self, cfg: &'b StableConfig, @@ -108,9 +101,10 @@ impl<'a, T: Deref> Matcher<'a, T> { None } - // Returns true if the selector matches the process info - // Any element in the "matches" section of the selector must match, they are ORed, - // as selectors are ANDed. + /// Returns true if the selector matches the process + /// + /// Any element in the "matches" section of the selector must match, they are ORed, + /// as selectors are ANDed. fn selector_match(&'a self, selector: &Selector) -> bool { match selector.origin { Origin::Language => string_selector(selector, self.process_info.language.deref()), @@ -272,7 +266,6 @@ struct Selector { origin: Origin, #[serde(default)] key: Option, - // matches: Vec, #[serde(flatten)] operator: Operator, } @@ -290,12 +283,14 @@ struct StableConfig { rules: Vec, } +/// Helper trait so we don't have to duplicate code for +/// HashMap<&str, &str> and HashMap trait Get { fn get(&self, k: &str) -> Option<&str>; } -impl<'a> Get for HashMap<&'a str, &'a str> { - fn get(&self, k: &str) -> Option<&'a str> { +impl Get for HashMap<&str, &str> { + fn get(&self, k: &str) -> Option<&str> { self.get(k).copied() } } @@ -359,13 +354,6 @@ impl Configurator { .map(|arg| String::from_utf8_lossy(arg)) .for_each(|e| eprintln!("\t\t{:?}", e.as_ref())); - // TODO: this is for testing purpose, we don't want to log env variables - // eprintln!("\tprocess envs:"); - // process_info - // .envp - // .iter() - // .map(|arg| String::from_utf8_lossy(&*arg)) - // .for_each(|e: std::borrow::Cow<'_, str>| eprintln!(" {:?}", e.as_ref())); eprintln!( "\tprocess language: {:?}", String::from_utf8_lossy(&process_info.language).as_ref() From b70b834b029665180a2ce3212b69bebbda0e63c3 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Thu, 16 Jan 2025 19:01:58 +0100 Subject: [PATCH 25/26] Change builder feature name in tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ccd08f03d..e38139deb5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,7 @@ jobs: env: RUSTFLAGS: "${{ matrix.flags }}" run: | - cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker,datadog-library-config-ffi --release -- --out $LIBDD_OUTPUT_FOLDER + cargo run --bin release --features profiling,telemetry,data-pipeline,symbolizer,crashtracker,library-config --release -- --out $LIBDD_OUTPUT_FOLDER - name: 'Publish libdatadog' uses: actions/upload-artifact@v4 From 458ea7d9ab58b27c9715b43e49b034bff3ebd097 Mon Sep 17 00:00:00 2001 From: paullegranddc Date: Thu, 16 Jan 2025 19:52:07 +0100 Subject: [PATCH 26/26] Undelete build telemetry script --- build-telemetry-ffi.sh | 134 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100755 build-telemetry-ffi.sh diff --git a/build-telemetry-ffi.sh b/build-telemetry-ffi.sh new file mode 100755 index 0000000000..a2e7cd803c --- /dev/null +++ b/build-telemetry-ffi.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +# Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +destdir="$1" + +mkdir -v -p "$destdir/include/datadog" "$destdir/lib/pkgconfig" "$destdir/cmake" + +version=$(awk -F\" '$1 ~ /^version/ { print $2 }' < ddtelemetry-ffi/Cargo.toml) +target="$(rustc -vV | awk '/^host:/ { print $2 }')" +shared_library_suffix=".so" +static_library_suffix=".a" +library_prefix="lib" +remove_rpath=0 +fix_macos_rpath=0 + +# Rust provides this note about the link libraries: +# note: Link against the following native artifacts when linking against this +# static library. The order and any duplication can be significant on some +# platforms. +# +# We've decided to strip out -lgcc_s because if it's provided then it will +# always make it into the final runtime dependencies, even if -static-libgcc is +# provided. At least on Alpine, libgcc_s may not even exist in the users' +# images, so -static-libgcc is recommended there. +case "$target" in + "x86_64-alpine-linux-musl"|"aarch64-alpine-linux-musl") + expected_native_static_libs=" -lssp_nonshared -lgcc_s -lc" + native_static_libs=" -lssp_nonshared -lc" + # on alpine musl, Rust adds some weird runpath to cdylibs + remove_rpath=1 + ;; + + "x86_64-apple-darwin"|"aarch64-apple-darwin") + expected_native_static_libs=" -framework Security -framework CoreFoundation -liconv -lSystem -lresolv -lc -lm -liconv" + native_static_libs="${expected_native_static_libs}" + shared_library_suffix=".dylib" + # fix usage of library in macos via rpath + fix_macos_rpath=1 + ;; + + "x86_64-unknown-linux-gnu"|"aarch64-unknown-linux-gnu") + expected_native_static_libs=" -ldl -lrt -lpthread -lgcc_s -lc -lm -lrt -lpthread -lutil -ldl -lutil" + native_static_libs=" -ldl -lrt -lpthread -lc -lm -lrt -lpthread -lutil -ldl -lutil" + ;; + + "x86_64-pc-windows-msvc") + expected_native_static_libs="" # I don't know what to expect + native_static_libs="" # I don't know what to expect + shared_library_suffix=".dll" + static_library_suffix=".lib" + library_prefix="" + ;; + + *) + >&2 echo "Unknown platform '${target}'" + exit 1 + ;; +esac + +cp -v LICENSE LICENSE-3rdparty.yml NOTICE "$destdir/" + +export RUSTFLAGS="${RUSTFLAGS:- -C relocation-model=pic}" + +datadog_telemetry_ffi="ddtelemetry-ffi" +echo "Building the ${datadog_telemetry_ffi} crate (may take some time)..." +DESTDIR="$destdir" cargo build --package="${datadog_telemetry_ffi}" --release --target "${target}" + +# Remove _ffi suffix when copying +shared_library_name="${library_prefix}ddtelemetry_ffi${shared_library_suffix}" +shared_library_rename="${library_prefix}ddtelemetry${shared_library_suffix}" + +static_library_name="${library_prefix}ddtelemetry_ffi${static_library_suffix}" +static_library_rename="${library_prefix}ddtelemetry${static_library_suffix}" + +cp -v "target/${target}/release/${shared_library_name}" "$destdir/lib/${shared_library_rename}" +cp -v "target/${target}/release/${static_library_name}" "$destdir/lib/${static_library_rename}" + +shared_library_name="${shared_library_rename}" +static_library_name="${static_library_rename}" + +if [[ "$remove_rpath" -eq 1 ]]; then + patchelf --remove-rpath "$destdir/lib/${shared_library_name}" +fi + +if [[ "$fix_macos_rpath" -eq 1 ]]; then + install_name_tool -id @rpath/${shared_library_name} "$destdir/lib/${shared_library_name}" +fi + +# objcopy might not be available on macOS +if command -v objcopy > /dev/null && [[ "$target" != "x86_64-pc-windows-msvc" ]]; then + # Remove .llvmbc section which is not useful for clients + objcopy --remove-section .llvmbc "$destdir/lib/${static_library_name}" + + # Ship debug information separate from shared library, so that downstream packages can selectively include it + # https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + objcopy --only-keep-debug "$destdir/lib/$shared_library_name" "$destdir/lib/$shared_library_name.debug" + strip -S "$destdir/lib/$shared_library_name" + objcopy --add-gnu-debuglink="$destdir/lib/$shared_library_name.debug" "$destdir/lib/$shared_library_name" +fi + +echo "Checking that native-static-libs are as expected for this platform..." +cd ddtelemetry-ffi +actual_native_static_libs="$(cargo rustc --release --target "${target}" -- --print=native-static-libs 2>&1 | awk -F ':' '/note: native-static-libs:/ { print $3 }')" +echo "Actual native-static-libs:${actual_native_static_libs}" +echo "Expected native-static-libs:${expected_native_static_libs}" + +# Compare unique elements between expected and actual native static libs. +# If actual libs is different from expected libs but still a subset of expected libs +# (ie. we will overlink compared to what is actually needed), this is not considered as an error. +# Raise an error only if some libs are in actual libs but not in expected libs. + +# trim leading and trailing spaces, then split the string on " -" by inserting new lines and sort lines while removing duplicates +unique_expected_libs=$(echo "$expected_native_static_libs "| awk '{ gsub(/^[ \t]+|[ \t]+$/, "");gsub(/ +-/,"\n-")};1' | sort -u) +unique_libs=$(echo "$actual_native_static_libs "| awk '{ gsub(/^[ \t]+|[ \t]+$/, "");gsub(/ +-/,"\n-")};1' | sort -u) + +unexpected_native_libs=$(comm -13 <(echo "$unique_expected_libs") <(echo "$unique_libs")) +if [ -n "$unexpected_native_libs" ]; then + echo "Error - More native static libraries are required for linking than expected:" 1>&2 + echo "$unexpected_native_libs" 1>&2 + exit 1 +fi +cd - + +echo "Building tools" +cargo build --package tools --bins + +echo "Generating $destdir/include/libdatadog headers..." +./target/debug/dedup_headers "${destdir}/include/datadog/common.h" "${destdir}/include/datadog/telemetry.h" + +echo "Done."