diff --git a/.github/workflows/prof_asan.yml b/.github/workflows/prof_asan.yml index 8d39889edb..8b7ce23b3e 100644 --- a/.github/workflows/prof_asan.yml +++ b/.github/workflows/prof_asan.yml @@ -6,8 +6,11 @@ on: jobs: prof-asan: runs-on: ubuntu-latest + strategy: + matrix: + php-version: [8.3, 8.4] container: - image: datadog/dd-trace-ci:php-8.3_bookworm-4 + image: datadog/dd-trace-ci:php-${{matrix.php-version}}_bookworm-5 # https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions#user options: --user root --privileged @@ -33,14 +36,15 @@ jobs: run: | set -eux switch-php nts-asan + rm $(php-config --ini-dir)/sqlsrv.ini #sqlsrv leaks memory and triggers asan cd profiling export CARGO_TARGET_DIR=/tmp/build-cargo export CC=clang-17 export CFLAGS='-fsanitize=address -fno-omit-frame-pointer' - export LDFLAGS='-fsanitize=address' + export LDFLAGS='-fsanitize=address -shared-libasan' export RUSTC_LINKER=lld-17 triplet=$(uname -m)-unknown-linux-gnu - RUST_NIGHTLY_VERSION="-2024-02-27" + RUST_NIGHTLY_VERSION="-2024-11-04" RUSTFLAGS='-Zsanitizer=address' cargo +nightly${RUST_NIGHTLY_VERSION} build -Zbuild-std --target $triplet --release cp -v "$CARGO_TARGET_DIR/$triplet/release/libdatadog_php_profiling.so" "$(php-config --extension-dir)/datadog-profiling.so" diff --git a/.gitlab/ci-images.yml b/.gitlab/ci-images.yml index b2130f42ce..8b76d3952f 100644 --- a/.gitlab/ci-images.yml +++ b/.gitlab/ci-images.yml @@ -35,7 +35,7 @@ CentOS: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_TOKEN" $CI_REGISTRY - docker buildx bake --no-cache --pull --push $PHP_VERSION -Alpine Compile Extension: +Alpine: stage: ci-build rules: - when: manual @@ -62,7 +62,7 @@ Alpine Compile Extension: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_TOKEN" $CI_REGISTRY - docker buildx bake --no-cache --pull --push $PHP_VERSION -Ubuntu Bookworm: +Bookworm: stage: ci-build rules: - when: manual @@ -91,7 +91,7 @@ Ubuntu Bookworm: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_TOKEN" $CI_REGISTRY - docker buildx bake --no-cache --pull --push $PHP_VERSION -Ubuntu Buster: +Buster: stage: ci-build rules: - when: manual diff --git a/appsec/src/extension/ddtrace.c b/appsec/src/extension/ddtrace.c index f26927803b..deff440f7e 100644 --- a/appsec/src/extension/ddtrace.c +++ b/appsec/src/extension/ddtrace.c @@ -157,7 +157,7 @@ static zend_module_entry *_find_ddtrace_module() void dd_trace_shutdown() { zend_module_entry *mod = _find_ddtrace_module(); - if (mod) { + if (mod && _orig_ddtrace_shutdown) { mod->request_shutdown_func = _orig_ddtrace_shutdown; } _orig_ddtrace_shutdown = NULL; diff --git a/appsec/src/helper/client.cpp b/appsec/src/helper/client.cpp index 545c6005bb..4e98218bb6 100644 --- a/appsec/src/helper/client.cpp +++ b/appsec/src/helper/client.cpp @@ -9,12 +9,9 @@ #include #include #include -#include #include "action.hpp" -#include "base64.h" #include "client.hpp" -#include "compression.hpp" #include "exception.hpp" #include "network/broker.hpp" #include "network/proto.hpp" diff --git a/appsec/src/helper/engine_settings.hpp b/appsec/src/helper/engine_settings.hpp index b61fc52b41..c22df8e8d0 100644 --- a/appsec/src/helper/engine_settings.hpp +++ b/appsec/src/helper/engine_settings.hpp @@ -9,7 +9,7 @@ #include "utils.hpp" #include #include -#include +#include #include namespace dds { @@ -71,21 +71,25 @@ struct engine_settings { schema_extraction.sample_rate == oth.schema_extraction.sample_rate; } +}; + +} // namespace dds - friend auto &operator<<(std::ostream &os, const engine_settings &c) +template <> struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); } + + template + auto format(const dds::engine_settings &c, FormatContext &ctx) const { - return os << "{rules_file=" << c.rules_file - << ", waf_timeout_us=" << c.waf_timeout_us - << ", trace_rate_limit=" << c.trace_rate_limit - << ", obfuscator_key_regex=" << c.obfuscator_key_regex - << ", obfuscator_value_regex=" << c.obfuscator_value_regex - << ", schema_extraction.enabled=" - << c.schema_extraction.enabled - << ", schema_extraction.sample_rate=" << std::fixed - << c.schema_extraction.sample_rate << "}"; + return format_to(ctx.out(), + "{{rules_file={}, waf_timeout_us={}, trace_rate_limit={}, " + "obfuscator_key_regex={}, obfuscator_value_regex={}, " + "schema_extraction.enabled={}, schema_extraction.sample_rate={}}}", + c.rules_file, c.waf_timeout_us, c.trace_rate_limit, + c.obfuscator_key_regex, c.obfuscator_value_regex, + c.schema_extraction.enabled, c.schema_extraction.sample_rate); } }; -} // namespace dds namespace std { template <> struct hash { diff --git a/appsec/src/helper/json_helper.cpp b/appsec/src/helper/json_helper.cpp index 9a785ddc7e..a9c8bc7851 100644 --- a/appsec/src/helper/json_helper.cpp +++ b/appsec/src/helper/json_helper.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace std::literals; @@ -176,8 +177,9 @@ json_helper::get_field_of_type(const rapidjson::Value &parent_field, } if (type != output_itr->value.GetType()) { - SPDLOG_DEBUG("Field {} is not of type {}. Instead {}", key, type, - output_itr->value.GetType()); + SPDLOG_DEBUG("Field {} is not of type {}. Instead {}", key, + fmt::underlying(type), + fmt::underlying(output_itr->value.GetType())); return std::nullopt; } diff --git a/appsec/src/helper/remote_config/config.hpp b/appsec/src/helper/remote_config/config.hpp index 05e5e226c7..d9a5707008 100644 --- a/appsec/src/helper/remote_config/config.hpp +++ b/appsec/src/helper/remote_config/config.hpp @@ -7,9 +7,9 @@ #include "../utils.hpp" #include "product.hpp" +#include #include #include -#include extern "C" { #include @@ -70,15 +70,19 @@ struct config { { return shm_path == b.shm_path && rc_path == b.rc_path; } +}; + +} // namespace dds::remote_config - friend std::ostream &operator<<(std::ostream &os, const config &c) +template <> struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); } + + auto format(const dds::remote_config::config &c, format_context &ctx) const { - return os << c.shm_path << ":" << c.rc_path; + return fmt::format_to(ctx.out(), "{}:{}", c.shm_path, c.rc_path); } }; -} // namespace dds::remote_config - namespace std { template <> struct hash { std::size_t operator()(const dds::remote_config::config &key) const diff --git a/appsec/src/helper/remote_config/product.hpp b/appsec/src/helper/remote_config/product.hpp index a9b70fe60f..80aeb24831 100644 --- a/appsec/src/helper/remote_config/product.hpp +++ b/appsec/src/helper/remote_config/product.hpp @@ -5,8 +5,8 @@ // (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. #pragma once -#include "../config.hpp" #include "../utils.hpp" +#include #include namespace dds::remote_config { @@ -19,11 +19,6 @@ class product { bool operator==(const product &other) const { return name_ == other.name_; } - friend std::ostream &operator<<(std::ostream &os, const product &p) - { - return os << p.name_; - } - private: std::string_view name_; }; @@ -56,6 +51,17 @@ struct known_products { }; } // namespace dds::remote_config +template <> +struct fmt::formatter + : fmt::formatter { + + auto format(const dds::remote_config::product &p, format_context &ctx) const + { + auto name = p.name(); + return formatter::format(name, ctx); + } +}; + namespace std { template <> struct hash { std::size_t operator()(const dds::remote_config::product &product) const diff --git a/appsec/src/helper/remote_config/settings.hpp b/appsec/src/helper/remote_config/settings.hpp index 43a7b8340d..5d66ff16a5 100644 --- a/appsec/src/helper/remote_config/settings.hpp +++ b/appsec/src/helper/remote_config/settings.hpp @@ -7,10 +7,9 @@ #pragma once #include "../utils.hpp" -#include -#include #include #include +#include #include namespace dds::remote_config { @@ -32,8 +31,20 @@ struct settings { MSGPACK_DEFINE_MAP(enabled, shmem_path); }; + } // namespace dds::remote_config +template <> struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context &ctx) { return ctx.begin(); } + + template + auto format(const dds::remote_config::settings &s, FormatContext &ctx) const + { + return fmt::format_to(ctx.out(), "{{enabled={}, shmem_path={}}}", + s.enabled, s.shmem_path); + } +}; + namespace std { template <> struct hash { std::size_t operator()(const dds::remote_config::settings &s) const noexcept diff --git a/appsec/src/helper/service_config.hpp b/appsec/src/helper/service_config.hpp index 50f277fb45..7e2684e779 100644 --- a/appsec/src/helper/service_config.hpp +++ b/appsec/src/helper/service_config.hpp @@ -6,14 +6,37 @@ #pragma once #include -#include -#include #include -#include namespace dds { - enum class enable_asm_status : unsigned { NOT_SET = 0, ENABLED, DISABLED }; +} // namespace dds + +template <> +struct fmt::formatter> + : fmt::formatter { + auto format(const std::atomic &status, + format_context &ctx) const + { + auto val = status.load(); + std::string_view name{"UNKNOWN"}; + switch (val) { + case dds::enable_asm_status::NOT_SET: + name = "NOT_SET"; + break; + case dds::enable_asm_status::ENABLED: + name = "ENABLED"; + break; + case dds::enable_asm_status::DISABLED: + name = "DISABLED"; + break; + } + + return fmt::formatter::format(name, ctx); + } +}; + +namespace dds { inline std::string_view to_string_view(enable_asm_status status) { diff --git a/appsec/src/helper/subscriber/waf.cpp b/appsec/src/helper/subscriber/waf.cpp index 0b843f63a7..edffb4691e 100644 --- a/appsec/src/helper/subscriber/waf.cpp +++ b/appsec/src/helper/subscriber/waf.cpp @@ -218,7 +218,8 @@ void instance::listener::call(dds::parameter_view &data, event &event) parameter_to_json(parameter_view{res.events}), res.total_runtime / millis); SPDLOG_DEBUG("Waf response: code {} - actions {} - derivatives {}", - code, parameter_to_json(parameter_view{res.actions}), + fmt::underlying(code), + parameter_to_json(parameter_view{res.actions}), parameter_to_json(parameter_view{res.derivatives})); } else { diff --git a/appsec/third_party/CMakeLists.txt b/appsec/third_party/CMakeLists.txt index d925ea6a5f..5a7a5c9faa 100644 --- a/appsec/third_party/CMakeLists.txt +++ b/appsec/third_party/CMakeLists.txt @@ -45,7 +45,7 @@ endif() FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG eb3220622e73a4889eee355ffa37972b3cac3df5) + GIT_TAG 5fd32e1a70871e2f6a52734e36bc33cb7ac022a5) FetchContent_MakeAvailable(spdlog) set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE 1) diff --git a/config.m4 b/config.m4 index 21b6e597a0..88ea035020 100644 --- a/config.m4 +++ b/config.m4 @@ -14,7 +14,7 @@ PHP_ARG_WITH(ddtrace-cargo, where cargo is located for rust code compilation, [ --with-ddtrace-cargo Location to cargo binary for rust compilation], cargo, not found) PHP_ARG_ENABLE(ddtrace-rust-debug, whether to compile rust in debug mode, - [ --enable-ddtrace-rust-debug Build rust code in debug mode (significantly slower)], [[$($GREP -q "ZEND_DEBUG 1" $("$PHP_CONFIG" --include-dir)/main/php_config.h && echo yes || echo no)]], [no]) + [ --enable-ddtrace-rust-debug Build rust code in debug mode (significantly slower)], [[$( (if test x"$ext_shared" = x"yes"; then $GREP -q "ZEND_DEBUG 1" $("$PHP_CONFIG" --include-dir)/main/php_config.h; else test x"$PHP_DEBUG" = x"yes"; fi) && echo yes || echo no)]], [no]) PHP_ARG_ENABLE(ddtrace-rust-library-split, whether to not link the rust library against the extension at compile time, [ --enable-ddtrace-rust-library-split Do not build nor link against the rust code], no, no) @@ -78,25 +78,23 @@ if test "$PHP_DDTRACE" != "no"; then CFLAGS="$CFLAGS -fms-extensions" EXTRA_CFLAGS="$EXTRA_CFLAGS -fms-extensions" - AC_MSG_CHECKING([whether the compiler is LLVM]) - - - AC_RUN_IFELSE([AC_LANG_SOURCE([[int main(void) { -#ifdef __clang__ - return 0; -#else - return 1; -#endif -}]])],[llvm=yes],[llvm=no],[llvm=yes]) - - if test "$llvm" = "yes"; then - AC_MSG_RESULT([yes]) - CFLAGS="$CFLAGS -Wno-microsoft-anon-tag" - EXTRA_CFLAGS="$EXTRA_CFLAGS -Wno-microsoft-anon-tag" - LDFLAGS="$LDFLAGS -Wl,--undefined-version" - else - AC_MSG_RESULT([no]) - fi + rm="rm -f" + lt_simple_link_test_code='int main(void){return(0);}' + AC_MSG_CHECKING([whether --undefined-version is a valid linker argument]) + AC_LIBTOOL_LINKER_OPTION([whether --undefined-version is a valid linker argument], + lt_cv_ddtrace_undefined_version, + [-Wl,--undefined-version], + [LDFLAGS="$LDFLAGS -Wl,--undefined-version"]) + + lt_simple_compile_test_code="int some_variable = 0;" + Xsed="$SED -e 1s/^X//" + AC_LIBTOOL_COMPILER_OPTION([whether -Wno-microsoft-anon-tag is a valid compiler argument], + lt_cv_ddtrace_no_microsoft_anon_tag, + [-Wno-microsoft-anon-tag], [], + [ + CFLAGS="$CFLAGS -Wno-microsoft-anon-tag" + EXTRA_CFLAGS="$EXTRA_CFLAGS -Wno-microsoft-anon-tag" + ]) DD_TRACE_VENDOR_SOURCES="\ ext/vendor/mpack/mpack.c \ diff --git a/docker-compose.yml b/docker-compose.yml index 877c9e4445..41b7c3a1db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,8 +85,8 @@ services: '8.4-buster': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.4_buster' } 'php-master-buster': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-master_buster' } # --- Bookworm --- - '8.2-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.2_bookworm-3' } - '8.3-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.3_bookworm-3' } + '8.2-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.2_bookworm-5' } + '8.3-bookworm': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-8.3_bookworm-5' } # --- CentOS 6 --- '7.0-centos7': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-7.0_centos-7' } '7.1-centos7': { <<: *linux_php_service, image: 'datadog/dd-trace-ci:php-7.1_centos-7' } diff --git a/dockerfiles/ci/alpine_compile_extension/docker-compose.yml b/dockerfiles/ci/alpine_compile_extension/docker-compose.yml index 026aedf606..840f43f519 100644 --- a/dockerfiles/ci/alpine_compile_extension/docker-compose.yml +++ b/dockerfiles/ci/alpine_compile_extension/docker-compose.yml @@ -129,8 +129,8 @@ services: x-bake: *bake args: php_version: 8.4.0 - php_url: https://downloads.php.net/~saki/php-8.4.0RC1.tar.gz - php_sha: ff924ce24493cd546c7227c2cedf75c32d23815b0a6e5c1658d9b005837631ff + php_url: https://downloads.php.net/~calvinb/php-8.4.0RC4.tar.gz + php_sha: 3964fee49465e6fcae77ad030ae31aa720ade0a51c53df6e255e36eb867fd4b2 php_api: 20240924 volumes: - ../../:/app diff --git a/dockerfiles/ci/bookworm/Dockerfile b/dockerfiles/ci/bookworm/Dockerfile index 5508d9fe81..dd7fa1d902 100644 --- a/dockerfiles/ci/bookworm/Dockerfile +++ b/dockerfiles/ci/bookworm/Dockerfile @@ -68,10 +68,7 @@ ENV PHPIZE_DEPS \ clang-17 \ cmake \ dpkg-dev \ - g++ \ - gcc \ file \ - lcov \ libc-dev \ make \ pkg-config \ @@ -128,7 +125,54 @@ RUN set -eux; \ echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep; \ \ # Allow nginx to be run as non-root for tests - chown -R circleci:circleci /var/log/nginx/ /var/lib/nginx/; + chown -R circleci:circleci /var/log/nginx/ /var/lib/nginx/; \ + \ +# Make clang the default compiler + update-alternatives --install /usr/bin/cc cc /usr/bin/clang-17 100; \ + update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-17 100; \ + update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 100; \ + update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 100; \ + update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld-17 100; \ + echo "-L /usr/lib/llvm-17/lib/clang/17/lib/linux" > /usr/lib/llvm-17/bin/clang.cfg; \ +# Include libasan library path + echo /usr/lib/llvm-17/lib/clang/17/lib/linux > /etc/ld.so.conf.d/libasan.conf && ldconfig + +# Install lcov +RUN set -eux; \ + LCOV_VERSION="1.15"; \ + LCOV_SHA256="c1cda2fa33bec9aa2c2c73c87226cfe97de0831887176b45ee523c5e30f8053a"; \ + cd /tmp && curl -OL https://github.com/linux-test-project/lcov/releases/download/v${LCOV_VERSION}/lcov-${LCOV_VERSION}.tar.gz; \ + (echo "${LCOV_SHA256} lcov-${LCOV_VERSION}.tar.gz" | sha256sum -c -); \ + mkdir lcov && cd lcov; \ + tar -xf ../lcov-${LCOV_VERSION}.tar.gz --strip 1; \ + make install; \ + lcov --version; \ + rm -rfv /tmp/* + +# Install gdb +RUN set -eux; \ + apt install -y libmpfr-dev libgmp-dev; \ + GDB_VERSION="15.2"; \ + GDB_SHA256="9d16bc2539a2a20dc3ef99b48b8414d51c51305c8577eb7a1da00996f6dea223";\ + cd /tmp && curl -OL https://ftp.gnu.org/gnu/gdb/gdb-${GDB_VERSION}.tar.gz; \ + (echo "${GDB_SHA256} gdb-${GDB_VERSION}.tar.gz" | sha256sum -c -); \ + mkdir gdb && cd gdb; \ + tar -xf ../gdb-${GDB_VERSION}.tar.gz --strip 1;\ + ./configure; \ + make -j "$((`nproc`+1))"; \ + make install + +# Install valgrind +RUN set -eux; \ + VALGRIND_VERSION="3.23.0"; \ + VALGRIND_SHA1="ec410c75d3920d4f9249a5cfa2cac31e1bf6d586"; \ + cd /tmp && curl -OL https://sourceware.org/pub/valgrind/valgrind-${VALGRIND_VERSION}.tar.bz2; \ + (echo "${VALGRIND_SHA1}" valgrind-${VALGRIND_VERSION}.tar.bz2 | sha1sum -c -); \ + mkdir valgrind && cd valgrind; \ + tar -xjf ../valgrind-${VALGRIND_VERSION}.tar.bz2 --strip 1;\ + ./configure; \ + make -j "$((`nproc`+1))"; \ + make install # Install SqlServer PHP Driver # https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server @@ -217,8 +261,7 @@ RUN set -eux; \ chown -R circleci:circleci /opt; ARG RUST_VERSION="1.76.0" -# Need a nightly that is at least 1.71.1 -ARG RUST_NIGHTLY_VERSION="-2024-02-27" +ARG RUST_NIGHTLY_VERSION="-2024-11-04" # Mount a cache into /rust/cargo if you want to pre-fetch packages or something ENV CARGO_HOME=/rust/cargo ENV RUSTUP_HOME=/rust/rustup diff --git a/dockerfiles/ci/bookworm/build-extensions.sh b/dockerfiles/ci/bookworm/build-extensions.sh index 01328c10c5..6a3f123ade 100755 --- a/dockerfiles/ci/bookworm/build-extensions.sh +++ b/dockerfiles/ci/bookworm/build-extensions.sh @@ -6,6 +6,9 @@ SHARED_BUILD=$(if php -i | grep -q enable-pcntl=shared; then echo 1; else echo 0 PHP_VERSION_ID=$(php -r 'echo PHP_MAJOR_VERSION . PHP_MINOR_VERSION;') PHP_ZTS=$(php -r 'echo PHP_ZTS;') +# This make `pecl install` use all available cores +export MAKEFLAGS="-j $(nproc)" + XDEBUG_VERSIONS=(-3.1.2) if [[ $PHP_VERSION_ID -le 70 ]]; then XDEBUG_VERSIONS=(-2.7.2) @@ -56,8 +59,8 @@ fi HOST_ARCH=$(if [[ $(file $(readlink -f $(which php))) == *aarch64* ]]; then echo "aarch64"; else echo "x86_64"; fi) export PKG_CONFIG=/usr/bin/$HOST_ARCH-linux-gnu-pkg-config -export CC=$HOST_ARCH-linux-gnu-gcc -export CXX=$HOST_ARCH-linux-gnu-g++ +# export CC=$HOST_ARCH-linux-gnu-gcc +# export CXX=$HOST_ARCH-linux-gnu-g++ iniDir=$(php -i | awk -F"=> " '/Scan this dir for additional .ini files/ {print $2}'); diff --git a/dockerfiles/ci/bookworm/build-php.sh b/dockerfiles/ci/bookworm/build-php.sh index a15800e999..57a841df98 100755 --- a/dockerfiles/ci/bookworm/build-php.sh +++ b/dockerfiles/ci/bookworm/build-php.sh @@ -13,7 +13,16 @@ INSTALL_DIR=$BASE_INSTALL_DIR/$INSTALL_VERSION if [[ ${INSTALL_VERSION} == *asan* ]]; then export CFLAGS='-fsanitize=address -DZEND_TRACK_ARENA_ALLOC' - export LDFLAGS='-fsanitize=address' + # GCC has `-shared-libasan` default, clang has '-static-libasan' default + export LDFLAGS='-fsanitize=address -shared-libasan' +fi + +if [[ ${PHP_VERSION_ID} -le 73 ]]; then + if [[ -z "${CFLAGS:-}" ]]; then + export CFLAGS="-Wno-implicit-function-declaration -DHAVE_POSIX_READDIR_R=1 -DHAVE_OLD_READDIR_R=0" + else + export CFLAGS="${CFLAGS} -Wno-implicit-function-declaration -DHAVE_POSIX_READDIR_R=1 -DHAVE_OLD_READDIR_R=0" + fi fi mkdir -p /tmp/build-php && cd /tmp/build-php @@ -23,7 +32,7 @@ mkdir -p ${INSTALL_DIR}/conf.d HOST_ARCH=$(if [[ $TARGETPLATFORM == "linux/arm64" ]]; then echo "aarch64"; else echo "x86_64"; fi) PKG_CONFIG=/usr/bin/$HOST_ARCH-linux-gnu-pkg-config \ -CC=$HOST_ARCH-linux-gnu-gcc \ +# CC=$HOST_ARCH-linux-gnu-gcc \ LIBS=-ldl \ ${PHP_SRC_DIR}/configure \ $(if [[ $SHARED_BUILD -ne 0 ]]; then echo \ diff --git a/dockerfiles/ci/bookworm/docker-compose.yml b/dockerfiles/ci/bookworm/docker-compose.yml index 1f3f3b1e60..dc980743ef 100644 --- a/dockerfiles/ci/bookworm/docker-compose.yml +++ b/dockerfiles/ci/bookworm/docker-compose.yml @@ -22,8 +22,8 @@ services: args: <<: *build-base phpVersion: "8.4" - phpTarGzUrl: https://downloads.php.net/~saki/php-8.4.0RC3.tar.gz - phpSha256Hash: "1937d125c9bb42bc4d8dcd3b8bb25d377814aea50be492bbc64b3554b73af371" + phpTarGzUrl: https://downloads.php.net/~calvinb/php-8.4.0RC4.tar.gz + phpSha256Hash: "3964fee49465e6fcae77ad030ae31aa720ade0a51c53df6e255e36eb867fd4b2" php-8.3: image: datadog/dd-trace-ci:php-8.3_bookworm-$BOOKWORM_NEXT_VERSION diff --git a/dockerfiles/ci/buster/docker-compose.yml b/dockerfiles/ci/buster/docker-compose.yml index 8a553367e5..b900ee38f5 100644 --- a/dockerfiles/ci/buster/docker-compose.yml +++ b/dockerfiles/ci/buster/docker-compose.yml @@ -17,8 +17,8 @@ services: x-bake: *bake args: phpVersion: "8.4" - phpTarGzUrl: https://downloads.php.net/~saki/php-8.4.0RC3.tar.gz - phpSha256Hash: "1937d125c9bb42bc4d8dcd3b8bb25d377814aea50be492bbc64b3554b73af371" + phpTarGzUrl: https://downloads.php.net/~calvinb/php-8.4.0RC4.tar.gz + phpSha256Hash: "3964fee49465e6fcae77ad030ae31aa720ade0a51c53df6e255e36eb867fd4b2" php-8.3: image: datadog/dd-trace-ci:php-8.3_buster diff --git a/dockerfiles/ci/centos/7/docker-compose.yml b/dockerfiles/ci/centos/7/docker-compose.yml index 20e9a2a83a..d37da8a6c8 100644 --- a/dockerfiles/ci/centos/7/docker-compose.yml +++ b/dockerfiles/ci/centos/7/docker-compose.yml @@ -117,6 +117,6 @@ services: x-bake: *bake args: phpVersion: "8.4" - phpTarGzUrl: https://downloads.php.net/~saki/php-8.4.0RC3.tar.gz - phpSha256Hash: "1937d125c9bb42bc4d8dcd3b8bb25d377814aea50be492bbc64b3554b73af371" + phpTarGzUrl: https://downloads.php.net/~calvinb/php-8.4.0RC4.tar.gz + phpSha256Hash: "3964fee49465e6fcae77ad030ae31aa720ade0a51c53df6e255e36eb867fd4b2" image: 'datadog/dd-trace-ci:php-8.4_centos-7' diff --git a/dockerfiles/ci/windows/docker-compose.yml b/dockerfiles/ci/windows/docker-compose.yml index db9d073d47..3e7bfb0e37 100644 --- a/dockerfiles/ci/windows/docker-compose.yml +++ b/dockerfiles/ci/windows/docker-compose.yml @@ -87,8 +87,8 @@ services: args: phpVersion: "8.4.0RC3" vsVersion: "vs17" - phpTarGzUrl: https://downloads.php.net/~saki/php-8.4.0RC3.tar.gz - phpSha256Hash: "1937d125c9bb42bc4d8dcd3b8bb25d377814aea50be492bbc64b3554b73af371" + phpTarGzUrl: https://downloads.php.net/~calvinb/php-8.4.0RC4.tar.gz + phpSha256Hash: "3964fee49465e6fcae77ad030ae31aa720ade0a51c53df6e255e36eb867fd4b2" php-8.3: image: datadog/dd-trace-ci:php-8.3_windows diff --git a/ext/collect_backtrace.c b/ext/collect_backtrace.c index 4ea860a687..43cd3f3eef 100644 --- a/ext/collect_backtrace.c +++ b/ext/collect_backtrace.c @@ -12,6 +12,12 @@ void ddtrace_call_get_locals(zend_execute_data *call, zval *locals_array, bool skip_args) { zend_op_array *op_array = &call->func->op_array; +#if PHP_VERSION_ID >= 80000 // Pre-PHP 8 there was "safe destruction" of CVs, whereby they were ZVAL_NULL()'ed, avoiding the need for this handling. + if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0) && ((zend_generator*)call->return_value)->execute_data == NULL) { + return; // Prevent locals collection in generator close, they may already be freed now. + } +#endif + #if PHP_VERSION_ID >= 70100 if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_SYMBOL_TABLE) #else diff --git a/ext/configuration.h b/ext/configuration.h index 57dd53cc7e..ee234fd0e5 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -149,14 +149,14 @@ enum ddtrace_sampling_rules_format { CONFIG(SET, DD_TRACE_RESOURCE_URI_QUERY_PARAM_ALLOWED, "") \ CONFIG(SET, DD_TRACE_HTTP_URL_QUERY_PARAM_ALLOWED, "*") \ CONFIG(SET, DD_TRACE_HTTP_POST_DATA_PARAM_ALLOWED, "") \ - CONFIG(INT, DD_TRACE_RATE_LIMIT, "0", .ini_change = zai_config_system_ini_change) \ + CONFIG(INT, DD_TRACE_RATE_LIMIT, "100", .ini_change = zai_config_system_ini_change) \ CONFIG(DOUBLE, DD_TRACE_SAMPLE_RATE, "-1", .ini_change = ddtrace_alter_DD_TRACE_SAMPLE_RATE, \ .env_config_fallback = ddtrace_conf_otel_sample_rate) \ CONFIG(JSON, DD_TRACE_SAMPLING_RULES, "[]") \ CONFIG(CUSTOM(INT), DD_TRACE_SAMPLING_RULES_FORMAT, "glob", .parser = dd_parse_sampling_rules_format) \ CONFIG(JSON, DD_SPAN_SAMPLING_RULES, "[]") \ CONFIG(STRING, DD_SPAN_SAMPLING_RULES_FILE, "", .ini_change = ddtrace_alter_sampling_rules_file_config) \ - CONFIG(SET_LOWERCASE, DD_TRACE_HEADER_TAGS, "", .ini_change = ddtrace_alter_DD_TRACE_HEADER_TAGS) \ + CONFIG(SET_OR_MAP_LOWERCASE, DD_TRACE_HEADER_TAGS, "", .ini_change = ddtrace_alter_DD_TRACE_HEADER_TAGS) \ CONFIG(INT, DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH, "512") \ CONFIG(MAP, DD_TRACE_PEER_SERVICE_MAPPING, "") \ CONFIG(BOOL, DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED, "false") \ @@ -274,6 +274,7 @@ typedef enum { DD_CONFIGURATION } ddtrace_config_id; } #define SET MAP #define SET_LOWERCASE MAP +#define SET_OR_MAP_LOWERCASE MAP #define JSON MAP #define MAP(id) \ static inline zend_array *get_##id(void) { return Z_ARR_P(zai_config_get_value(DDTRACE_CONFIG_##id)); } \ @@ -294,6 +295,7 @@ static inline bool get_global_DD_TRACE_SIDECAR_TRACE_SENDER(void) { return true; #undef MAP #undef SET #undef SET_LOWERCASE +#undef SET_OR_MAP_LOWERCASE #undef JSON #undef BOOL #undef INT diff --git a/ext/ddtrace.c b/ext/ddtrace.c index dce2a30a28..0ec0fbae7a 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -1049,6 +1049,19 @@ static zval *ddtrace_span_data_readonly(zend_object *object, zend_string *member #endif } + ddtrace_span_data *span = OBJ_SPANDATA(obj); + // As per unified service tagging spec if a span is created with a service name different from the global + // service name it will not inherit the global version value + if (zend_string_equals_literal(prop_name, "service")) { + cache_slot = NULL; + if (ZSTR_LEN(get_DD_SERVICE()) || !ddtrace_span_is_entrypoint_root(span)) { + if (!zend_is_identical(&span->property_service, value)) { + zval_ptr_dtor(&span->property_version); + ZVAL_EMPTY_STRING(&span->property_version); + } + } + } + #if PHP_VERSION_ID >= 70400 return zend_std_write_property(object, member, value, cache_slot); #else diff --git a/ext/hook/uhook.c b/ext/hook/uhook.c index 039e60f02c..e186583ecc 100644 --- a/ext/hook/uhook.c +++ b/ext/hook/uhook.c @@ -491,7 +491,7 @@ static void dd_uhook_dtor(void *data) { #define _error_code error_code #endif -/* {{{ proto int DDTrace\install_hook(string|Closure|Generator target, ?Closure begin = null, ?Closure end = null) */ +/* {{{ proto int DDTrace\install_hook(string|Closure|Generator target, ?Closure begin = null, ?Closure end = null, int flags = 0) */ PHP_FUNCTION(DDTrace_install_hook) { zend_string *name = NULL; zend_function *resolved = NULL; @@ -583,17 +583,29 @@ PHP_FUNCTION(DDTrace_install_hook) { def->function = NULL; // Fetch the base function for fake closures: we need to do fake closure operations on the proper original function: - // - inheritance handling requires having an op_array which stays alive for the whole remainder of the rquest + // - inheritance handling requires having an op_array which stays alive for the whole remainder of the request // - internal functions are referenced by their zend_internal_function, not the closure copy if ((resolved->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && !(flags & HOOK_INSTANCE)) { - HashTable *baseTable = resolved->common.scope ? &resolved->common.scope->function_table : EG(function_table); + zend_class_entry *resolved_ce = resolved->common.scope; + HashTable *baseTable = resolved_ce ? &resolved_ce->function_table : EG(function_table); zend_function *original = zend_hash_find_ptr(baseTable, resolved->common.function_name); + if (!ZEND_USER_CODE(resolved->type)) { + if (original && original->type != resolved->type /* check for trampoline */) { + original = NULL; // this may happen with e.g. __call() where the function is defined as private + } + if (!original && resolved_ce) { + // Assume it's a __call or __callStatic trampoline + // Execute logic from zend_closure_call_magic here. + original = (resolved->common.fn_flags & ZEND_ACC_STATIC) ? resolved_ce->__callstatic : resolved_ce->__call; + } + } if (!original) { - LOG(ERROR, - "Could not find original function for fake closure ", + LOG(WARN, + "Could not find original function for fake closure %s%s%s at %s:%d", resolved->common.scope ? ZSTR_VAL(resolved->common.scope->name) : "", resolved->common.scope ? "::" : "", - ZSTR_VAL(resolved->common.function_name)); + ZSTR_VAL(resolved->common.function_name), + zend_get_executed_filename(), zend_get_executed_lineno()); goto error; } resolved = original; diff --git a/ext/limiter/limiter.c b/ext/limiter/limiter.c index 389a16bf60..cc6db08a1c 100644 --- a/ext/limiter/limiter.c +++ b/ext/limiter/limiter.c @@ -27,6 +27,10 @@ static ddtrace_limiter* dd_limiter; void ddtrace_limiter_create() { + if (zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_SAMPLE_RATE].name_index < 0) { + return; + } + uint32_t limit = (uint32_t) get_global_DD_TRACE_RATE_LIMIT(); if (!limit) { diff --git a/ext/serializer.c b/ext/serializer.c index 173a87dd3b..328e140ed8 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -324,14 +324,20 @@ static zend_result dd_add_meta_array(void *context, ddtrace_string key, ddtrace_ static void dd_add_header_to_meta(zend_array *meta, const char *type, zend_string *lowerheader, zend_string *headerval) { - if (zend_hash_exists(get_DD_TRACE_HEADER_TAGS(), lowerheader)) { - for (char *ptr = ZSTR_VAL(lowerheader); *ptr; ++ptr) { - if ((*ptr < 'a' || *ptr > 'z') && *ptr != '-' && (*ptr < '0' || *ptr > '9')) { - *ptr = '_'; + zval *header_config = zend_hash_find(get_DD_TRACE_HEADER_TAGS(), lowerheader); + if (header_config != NULL && Z_TYPE_P(header_config) == IS_STRING) { + zend_string *header_config_str = Z_STR_P(header_config); + zend_string *headertag; + if (ZSTR_LEN(header_config_str) == 0) { + for (char *ptr = ZSTR_VAL(lowerheader); *ptr; ++ptr) { + if ((*ptr < 'a' || *ptr > 'z') && *ptr != '-' && (*ptr < '0' || *ptr > '9')) { + *ptr = '_'; + } } + headertag = zend_strpprintf(0, "http.%s.headers.%s", type, ZSTR_VAL(lowerheader)); + } else { + headertag = zend_string_copy(header_config_str); } - - zend_string *headertag = zend_strpprintf(0, "http.%s.headers.%s", type, ZSTR_VAL(lowerheader)); zval headerzv; ZVAL_STR_COPY(&headerzv, headerval); zend_hash_update(meta, headertag, &headerzv); diff --git a/libdatadog b/libdatadog index 085a91abb8..356f76a818 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 085a91abb8fe6700a6faa528314ada665bc36852 +Subproject commit 356f76a818d164c954d2a09e54e2e8c41537a053 diff --git a/profiling/build.rs b/profiling/build.rs index b5b35294ea..a87476a460 100644 --- a/profiling/build.rs +++ b/profiling/build.rs @@ -30,8 +30,9 @@ fn main() { let fibers = cfg_fibers(vernum); let run_time_cache = cfg_run_time_cache(vernum); let trigger_time_sample = cfg_trigger_time_sample(); + let zend_error_observer = cfg_zend_error_observer(vernum); - generate_bindings(php_config_includes, fibers); + generate_bindings(php_config_includes, fibers, zend_error_observer); build_zend_php_ffis( php_config_includes, post_startup_cb, @@ -39,6 +40,7 @@ fn main() { run_time_cache, fibers, trigger_time_sample, + zend_error_observer, vernum, ); @@ -83,6 +85,7 @@ const ZAI_H_FILES: &[&str] = &[ "../zend_abstract_interface/json/json.h", ]; +#[allow(clippy::too_many_arguments)] fn build_zend_php_ffis( php_config_includes: &str, post_startup_cb: bool, @@ -90,6 +93,7 @@ fn build_zend_php_ffis( run_time_cache: bool, fibers: bool, trigger_time_sample: bool, + zend_error_observer: bool, vernum: u64, ) { println!("cargo:rerun-if-changed=src/php_ffi.h"); @@ -135,6 +139,7 @@ fn build_zend_php_ffis( let fibers = if fibers { "1" } else { "0" }; let run_time_cache = if run_time_cache { "1" } else { "0" }; let trigger_time_sample = if trigger_time_sample { "1" } else { "0" }; + let zend_error_observer = if zend_error_observer { "1" } else { "0" }; #[cfg(feature = "stack_walking_tests")] let stack_walking_tests = "1"; @@ -150,6 +155,7 @@ fn build_zend_php_ffis( .define("CFG_RUN_TIME_CACHE", run_time_cache) .define("CFG_STACK_WALKING_TESTS", stack_walking_tests) .define("CFG_TRIGGER_TIME_SAMPLE", trigger_time_sample) + .define("CFG_ZEND_ERROR_OBSERVER", zend_error_observer) .includes([Path::new("../ext")]) .includes( str::replace(php_config_includes, "-I", "") @@ -190,7 +196,7 @@ impl bindgen::callbacks::ParseCallbacks for IgnoreMacros { } } -fn generate_bindings(php_config_includes: &str, fibers: bool) { +fn generate_bindings(php_config_includes: &str, fibers: bool, zend_error_observer: bool) { println!("cargo:rerun-if-changed=src/php_ffi.h"); println!("cargo:rerun-if-changed=../ext/handlers_api.h"); let ignored_macros = IgnoreMacros( @@ -206,12 +212,20 @@ fn generate_bindings(php_config_includes: &str, fibers: bool) { .collect(), ); - let clang_args = if fibers { + let mut clang_args = if fibers { vec!["-D", "CFG_FIBERS=1"] } else { vec!["-D", "CFG_FIBERS=0"] }; + if zend_error_observer { + clang_args.push("-D"); + clang_args.push("CFG_ZEND_ERROR_OBSERVER=1"); + } else { + clang_args.push("-D"); + clang_args.push("CFG_ZEND_ERROR_OBSERVER=0"); + } + let bindings = bindgen::Builder::default() .ctypes_prefix("libc") .clang_args(clang_args) @@ -296,6 +310,18 @@ fn cfg_trigger_time_sample() -> bool { env::var("CARGO_FEATURE_TRIGGER_TIME_SAMPLE").is_ok() } +fn cfg_zend_error_observer(vernum: u64) -> bool { + if vernum >= 80000 { + println!("cargo:rustc-cfg=zend_error_observer"); + if vernum < 80100 { + println!("cargo:rustc-cfg=zend_error_observer_80"); + } + true + } else { + false + } +} + fn cfg_php_major_version(vernum: u64) { let major_version = match vernum { 70000..=79999 => 7, diff --git a/profiling/src/bindings/mod.rs b/profiling/src/bindings/mod.rs index b4b9fe0814..b1734cc87c 100644 --- a/profiling/src/bindings/mod.rs +++ b/profiling/src/bindings/mod.rs @@ -169,12 +169,25 @@ pub struct ModuleEntry { Option ZendResult>, pub info_func: Option, pub version: *const u8, + /// Size of the module globals in bytes. In ZTS this will be the size TSRM will allocate per + /// thread for module globals. The function pointers in [`ModuleEntry::globals_ctor`] and + /// [`ModuleEntry::globals_dtor`] will only be called if this is a non-zero. pub globals_size: size_t, #[cfg(php_zts)] + /// Pointer to a `ts_rsrc_id` (which is a [`i32`]). For C-Extension this is created using the + /// `ZEND_DECLARE_MODULE_GLOBALS(module_name)` macro. + /// See pub globals_id_ptr: *mut ts_rsrc_id, #[cfg(not(php_zts))] + /// Pointer to the module globals struct in NTS mode pub globals_ptr: *mut c_void, + /// Constructor for module globals. + /// Be aware this will only be called in case [`ModuleEntry::globals_size`] is non-zero and for + /// ZTS you need to make sure [`ModuleEntry::globals_id_ptr`] is a valid, non-null pointer. pub globals_ctor: Option, + /// Destructor for module globals. + /// Be aware this will only be called in case [`ModuleEntry::globals_size`] is non-zero and for + /// ZTS you need to make sure [`ModuleEntry::globals_id_ptr`] is a valid, non-null pointer. pub globals_dtor: Option, pub post_deactivate_func: Option ZendResult>, pub module_started: c_int, diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs index 02cb66a8e8..d3232c2898 100644 --- a/profiling/src/lib.rs +++ b/profiling/src/lib.rs @@ -31,6 +31,8 @@ use core::ptr; use ddcommon::{cstr, tag, tag::Tag}; use lazy_static::lazy_static; use libc::c_char; +#[cfg(php_zts)] +use libc::c_void; use log::{debug, error, info, trace, warn}; use once_cell::sync::{Lazy, OnceCell}; use profiling::{LocalRootSpanResourceMessage, Profiler, VmInterrupt}; @@ -128,6 +130,13 @@ extern "C" { pub static ddtrace_runtime_id: *const Uuid; } +/// We do not have any globals, but we need TSRM to call into GINIT and GSHUTDOWN to observe +/// spawning and joining threads. This will be pointed to by the [`ModuleEntry::globals_id_ptr`] in +/// the `zend_module_entry` and the TSRM will store it's thread-safe-resource id here. +/// See: +#[cfg(php_zts)] +static mut GLOBALS_ID_PTR: i32 = 0; + /// The function `get_module` is what makes this a PHP module. Please do not /// call this directly; only let it be called by the engine. Generally it is /// only called once, but if someone accidentally loads the module twice then @@ -160,6 +169,14 @@ pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry { version: PROFILER_VERSION.as_ptr(), post_deactivate_func: Some(prshutdown), deps: DEPS.as_ptr(), + #[cfg(php_zts)] + globals_ctor: Some(ginit), + #[cfg(php_zts)] + globals_dtor: Some(gshutdown), + #[cfg(php_zts)] + globals_size: 1, + #[cfg(php_zts)] + globals_id_ptr: unsafe { &mut GLOBALS_ID_PTR }, ..Default::default() }); @@ -167,6 +184,18 @@ pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry { unsafe { &mut *ptr::addr_of_mut!(MODULE) } } +#[cfg(php_zts)] +unsafe extern "C" fn ginit(_globals_ptr: *mut c_void) { + #[cfg(all(feature = "timeline", php_zts))] + timeline::timeline_ginit(); +} + +#[cfg(php_zts)] +unsafe extern "C" fn gshutdown(_globals_ptr: *mut c_void) { + #[cfg(all(feature = "timeline", php_zts))] + timeline::timeline_gshutdown(); +} + /* Important note on the PHP lifecycle: * Based on how some SAPIs work and the documentation, one might expect that * MINIT is called once per process, but this is only sort-of true. Some SAPIs diff --git a/profiling/src/php_ffi.h b/profiling/src/php_ffi.h index d2c1123e9b..15de64ec0a 100644 --- a/profiling/src/php_ffi.h +++ b/profiling/src/php_ffi.h @@ -16,6 +16,12 @@ #include +// Needed for `zend_observer_error_register` starting from PHP 8 +#if CFG_ZEND_ERROR_OBSERVER // defined by build.rs +#include +#include +#endif + // Profiling needs ZAI config for INI support. #include // And json to cleanup json state for graceful restart diff --git a/profiling/src/profiling/mod.rs b/profiling/src/profiling/mod.rs index f98fee3484..9b59d4fd9c 100644 --- a/profiling/src/profiling/mod.rs +++ b/profiling/src/profiling/mod.rs @@ -976,6 +976,80 @@ impl Profiler { } } + /// This function will collect a thread start or stop timeline event + #[cfg(all(feature = "timeline", php_zts))] + pub fn collect_thread_start_end(&self, now: i64, event: &'static str) { + let mut labels = Profiler::common_labels(1); + + labels.push(Label { + key: "event", + value: LabelValue::Str(std::borrow::Cow::Borrowed(event)), + }); + + let n_labels = labels.len(); + + match self.prepare_and_send_message( + vec![ZendFrame { + function: format!("[{event}]").into(), + file: None, + line: 0, + }], + SampleValues { + timeline: 1, + ..Default::default() + }, + labels, + now, + ) { + Ok(_) => { + trace!("Sent event '{event}' with {n_labels} labels to profiler.") + } + Err(err) => { + warn!("Failed to send event '{event}' with {n_labels} labels to profiler: {err}") + } + } + } + + /// This function can be called to collect any fatal errors + #[cfg(feature = "timeline")] + pub fn collect_fatal(&self, now: i64, file: String, line: u32, message: String) { + let mut labels = Profiler::common_labels(2); + + labels.push(Label { + key: "event", + value: LabelValue::Str("fatal".into()), + }); + labels.push(Label { + key: "message", + value: LabelValue::Str(message.into()), + }); + + let n_labels = labels.len(); + + match self.prepare_and_send_message( + vec![ZendFrame { + function: "[fatal]".into(), + file: Some(file), + line, + }], + SampleValues { + timeline: 1, + ..Default::default() + }, + labels, + now, + ) { + Ok(_) => { + trace!("Sent event 'fatal error' with {n_labels} labels to profiler.") + } + Err(err) => { + warn!( + "Failed to send event 'fatal error' with {n_labels} labels to profiler: {err}" + ) + } + } + } + /// This function can be called to collect any kind of inactivity that is happening #[cfg(feature = "timeline")] pub fn collect_idle(&self, now: i64, duration: i64, reason: &'static str) { diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index 596c887a14..ab69528ca1 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -6,6 +6,8 @@ use crate::zend::{ use crate::REQUEST_LOCALS; use libc::c_char; use log::{error, trace}; +#[cfg(php_zts)] +use std::cell::Cell; use std::cell::RefCell; use std::ffi::CStr; use std::ptr; @@ -30,11 +32,17 @@ static mut FRANKENPHP_HANDLE_REQUEST_HANDLER: InternalFunctionHandler = None; thread_local! { static IDLE_SINCE: RefCell = RefCell::new(Instant::now()); + #[cfg(php_zts)] + static IS_NEW_THREAD: Cell = const { Cell::new(false) }; } enum State { Idle, Sleeping, + #[cfg(php_zts)] + ThreadStart, + #[cfg(php_zts)] + ThreadStop, } impl State { @@ -42,6 +50,10 @@ impl State { match self { State::Idle => "idle", State::Sleeping => "sleeping", + #[cfg(php_zts)] + State::ThreadStart => "thread start", + #[cfg(php_zts)] + State::ThreadStop => "thread stop", } } } @@ -144,9 +156,48 @@ unsafe extern "C" fn ddog_php_prof_frankenphp_handle_request( } } +/// Will be called by the ZendEngine on all errors happening. This is a PHP 8 API +#[cfg(zend_error_observer)] +unsafe extern "C" fn ddog_php_prof_zend_error_observer( + _type: i32, + #[cfg(zend_error_observer_80)] file: *const c_char, + #[cfg(not(zend_error_observer_80))] file: *mut zend::ZendString, + line: u32, + message: *mut zend::ZendString, +) { + // we are only interested in FATAL errors + + if _type & zend::E_FATAL_ERRORS as i32 == 0 { + return; + } + + #[cfg(zend_error_observer_80)] + let file = unsafe { + let mut len = 0; + let file = file as *const u8; + while *file.add(len) != 0 { + len += 1; + } + std::str::from_utf8_unchecked(std::slice::from_raw_parts(file, len)).to_string() + }; + #[cfg(not(zend_error_observer_80))] + let file = unsafe { zend::zai_str_from_zstr(file.as_mut()).into_string() }; + + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + if let Some(profiler) = Profiler::get() { + let now = now.as_nanos() as i64; + profiler.collect_fatal(now, file, line, unsafe { + zend::zai_str_from_zstr(message.as_mut()).into_string() + }); + } +} + /// This functions needs to be called in MINIT of the module pub fn timeline_minit() { unsafe { + #[cfg(zend_error_observer)] + zend::zend_observer_error_register(Some(ddog_php_prof_zend_error_observer)); + // register our function in the `gc_collect_cycles` pointer PREV_GC_COLLECT_CYCLES = zend::gc_collect_cycles; zend::gc_collect_cycles = Some(ddog_php_prof_gc_collect_cycles); @@ -231,6 +282,35 @@ pub unsafe fn timeline_rinit() { } }); }); + + #[cfg(php_zts)] + IS_NEW_THREAD.with(|cell| { + if !cell.get() { + return; + } + cell.set(false); + REQUEST_LOCALS.with(|cell| { + let is_timeline_enabled = cell + .try_borrow() + .map(|locals| locals.system_settings().profiling_timeline_enabled) + .unwrap_or(false); + + if !is_timeline_enabled { + return; + } + + if let Some(profiler) = Profiler::get() { + profiler.collect_thread_start_end( + // Safety: checked for `is_err()` above + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as i64, + State::ThreadStart.as_str(), + ); + } + }); + }); } /// This function is run during the P-RSHUTDOWN phase and resets the `IDLE_SINCE` thread local to @@ -290,6 +370,43 @@ pub(crate) unsafe fn timeline_mshutdown() { } }); }); + #[cfg(php_zts)] + timeline_gshutdown(); +} + +#[cfg(php_zts)] +pub(crate) fn timeline_ginit() { + // During GINIT in "this" thread, the request locals are not initialized, which happens in + // RINIT, so we currently do not know if profile is enabled at all and if, if timeline is + // enabled. That's why we raise this flag here and read it in RINIT. + IS_NEW_THREAD.with(|cell| { + cell.set(true); + }); +} + +#[cfg(php_zts)] +pub(crate) fn timeline_gshutdown() { + REQUEST_LOCALS.with(|cell| { + let is_timeline_enabled = cell + .try_borrow() + .map(|locals| locals.system_settings().profiling_timeline_enabled) + .unwrap_or(false); + + if !is_timeline_enabled { + return; + } + + if let Some(profiler) = Profiler::get() { + profiler.collect_thread_start_end( + // Safety: checked for `is_err()` above + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as i64, + State::ThreadStop.as_str(), + ); + } + }); } /// This function gets called when a `eval()` is being called. This is done by letting the diff --git a/src/DDTrace/Integrations/Mysqli/MysqliIntegration.php b/src/DDTrace/Integrations/Mysqli/MysqliIntegration.php index a6a603b7fe..e1b374e725 100644 --- a/src/DDTrace/Integrations/Mysqli/MysqliIntegration.php +++ b/src/DDTrace/Integrations/Mysqli/MysqliIntegration.php @@ -376,7 +376,9 @@ public function trackPotentialError(SpanData $span) { $errorCode = mysqli_connect_errno(); if ($errorCode > 0) { - $span->exception = new \Exception(mysqli_connect_error()); + $span->meta[Tag::ERROR_MSG] = mysqli_connect_error(); + $span->meta[Tag::ERROR_TYPE] = 'mysqli error'; + $span->meta[Tag::ERROR_STACK] = \DDTrace\get_sanitized_exception_trace(new \Exception, 2); } } } diff --git a/src/DDTrace/Integrations/Psr18/Psr18Integration.php b/src/DDTrace/Integrations/Psr18/Psr18Integration.php index 31df56741a..2287036bbb 100644 --- a/src/DDTrace/Integrations/Psr18/Psr18Integration.php +++ b/src/DDTrace/Integrations/Psr18/Psr18Integration.php @@ -27,7 +27,6 @@ public function init(): int function (SpanData $span, $args, $retval) use ($integration) { $span->resource = 'sendRequest'; $span->name = 'Psr\Http\Client\ClientInterface.sendRequest'; - $span->service = 'psr18'; $span->type = Type::HTTP_CLIENT; $span->meta[Tag::SPAN_KIND] = 'client'; $span->meta[Tag::COMPONENT] = Psr18Integration::NAME; diff --git a/src/DDTrace/Util/Normalizer.php b/src/DDTrace/Util/Normalizer.php index 2c3f67ec4a..70fe823b31 100644 --- a/src/DDTrace/Util/Normalizer.php +++ b/src/DDTrace/Util/Normalizer.php @@ -216,10 +216,9 @@ private static function generateFilteredPostFields($postKey, $postVal, array $wh // Concatenate the postkey and postval into '=' $postField = "$postKey=$postVal"; - // Match it with the regex to redact if needed + // Match it with the regex to redact if needed and regex is not empty $obfuscationRegex = \ini_get('datadog.trace.obfuscation_query_string_regexp'); - $obfuscationRegex = '(' . $obfuscationRegex . ')'; - if (preg_match($obfuscationRegex, $postField)) { + if ($obfuscationRegex != "" && preg_match('(' . $obfuscationRegex . ')', $postField)) { return [$postKey => '']; } else { return [$postKey => $postVal]; diff --git a/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php b/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php index 4d6a93c09c..2debc547a6 100644 --- a/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php +++ b/tests/Integrations/Guzzle/V7/GuzzleIntegrationTest.php @@ -18,7 +18,7 @@ public function testSendRequest() $this->getMockedClient()->sendRequest($request); }); $this->assertFlameGraph($traces, [ - SpanAssertion::build('Psr\Http\Client\ClientInterface.sendRequest', 'psr18', 'http', 'sendRequest') + SpanAssertion::build('Psr\Http\Client\ClientInterface.sendRequest', 'phpunit', 'http', 'sendRequest') ->withExactTags([ 'http.method' => 'PUT', 'http.url' => 'http://example.com', @@ -36,7 +36,7 @@ public function testSendRequest() 'network.destination.name' => 'example.com', TAG::SPAN_KIND => 'client', Tag::COMPONENT => 'guzzle', - '_dd.base_service' => 'psr18' + '_dd.base_service' => 'phpunit' ]), ]) ]); diff --git a/tests/Unit/ConfigurationTest.php b/tests/Unit/ConfigurationTest.php index 6fb10e4568..c96ceaacb3 100644 --- a/tests/Unit/ConfigurationTest.php +++ b/tests/Unit/ConfigurationTest.php @@ -279,10 +279,9 @@ public function testHttpHeadersCanSetOne() public function testHttpHeadersCanSetMultiple() { $this->putEnvAndReloadConfig([ - 'DD_TRACE_HEADER_TAGS=A-Header ,Any-Name , cOn7aining-!spe_cial?:ch/ars ', + 'DD_TRACE_HEADER_TAGS=A-Header ,Any-Name , cOn7aining-!spe_cial?:ch/ars , valueless:, Some-Header:with-colon-Key', ]); - // Same behavior as python tracer: - // https://github.com/DataDog/dd-trace-py/blob/f1298cb8100f146059f978b58c88641bd7424af8/ddtrace/http/headers.py - $this->assertSame(['a-header', 'any-name', 'con7aining-!spe_cial?:ch/ars'], array_keys(\dd_trace_env_config("DD_TRACE_HEADER_TAGS"))); + $this->assertSame(['a-header', 'any-name', 'con7aining-!spe_cial?', 'valueless', 'some-header'], array_keys(\dd_trace_env_config("DD_TRACE_HEADER_TAGS"))); + $this->assertEquals(['a-header' => '', 'any-name' => '', 'con7aining-!spe_cial?' => 'ch/ars', 'valueless' => '', 'some-header' => 'with-colon-Key'], \dd_trace_env_config("DD_TRACE_HEADER_TAGS")); } } diff --git a/tests/ext/dd_trace_serialize_header_to_meta.phpt b/tests/ext/dd_trace_serialize_header_to_meta.phpt new file mode 100644 index 0000000000..f8e548765a --- /dev/null +++ b/tests/ext/dd_trace_serialize_header_to_meta.phpt @@ -0,0 +1,26 @@ +--TEST-- +Headers values are mapped to expected tag key +--ENV-- +HTTP_CONTENT_TYPE=text/plain +HTTP_CUSTOM_HEADER=custom-header-value +HTTP_HEADER1=val +HTTP_HEADER2=v a l +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_HEADER_TAGS=Content-Type,Custom-Header:custom-HeaderKey,header1: t a g ,header2:tag +--GET-- +application_key=123 +--FILE-- + +--EXPECT-- +string(10) "text/plain" +string(19) "custom-header-value" +string(3) "val" +string(5) "v a l" diff --git a/tests/ext/limiter/001-limiter-disabled.phpt b/tests/ext/limiter/001-limiter-disabled.phpt index bfa5f0d2d6..652e677f98 100644 --- a/tests/ext/limiter/001-limiter-disabled.phpt +++ b/tests/ext/limiter/001-limiter-disabled.phpt @@ -3,6 +3,7 @@ rate limiter disabled --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=1 DD_TRACE_RATE_LIMIT=0 +DD_TRACE_SAMPLE_RATE=1 --FILE-- +--EXPECT-- +string(48) "https://localhost:9999/users?application_key=123" diff --git a/tests/ext/sandbox-regression/trampoline_install_fake_closure_hook.phpt b/tests/ext/sandbox-regression/trampoline_install_fake_closure_hook.phpt new file mode 100644 index 0000000000..08d96482e8 --- /dev/null +++ b/tests/ext/sandbox-regression/trampoline_install_fake_closure_hook.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test installing hook on trampoline fake closure +--SKIPIF-- + +--FILE-- +test(...); +DDTrace\install_hook($x, function($hook) { + echo "Hooked\n"; +}); + +$x(); +(new A)->test(); + +DDTrace\install_hook((new A)->shadow(...), function($hook) { + echo "Shadow hooked\n"; +}); +(new A)->shadow(); + +?> +--EXPECT-- +Hooked +Invoked test +Hooked +Invoked test +Hooked +Shadow hooked +Invoked shadow diff --git a/tests/ext/ust.phpt b/tests/ext/ust.phpt new file mode 100644 index 0000000000..bfcbb2c3ec --- /dev/null +++ b/tests/ext/ust.phpt @@ -0,0 +1,113 @@ +--TEST-- +Foo +--ENV-- +DD_SERVICE=version_test +DD_VERSION=5.2.0 +DD_TRACE_AUTO_FLUSH_ENABLED=0 +DD_TRACE_GENERATE_ROOT_SPAN=0 +--FILE-- +name = "s1"; +\DDTrace\close_span(); + +$s2 = \DDTrace\start_trace_span(); +$s2->name = "s2"; +$s2->service = "no dd_service"; +\DDTrace\close_span(); + +var_dump(dd_trace_serialize_closed_spans()); + +?> +--EXPECTF-- +array(2) { + [0]=> + array(%d) { + ["trace_id"]=> + string(%d) "%s" + ["span_id"]=> + string(%d) "%s" + ["start"]=> + int(%d) + ["duration"]=> + int(%d) + ["name"]=> + string(2) "s2" + ["resource"]=> + string(2) "s2" + ["service"]=> + string(13) "no dd_service" + ["type"]=> + string(3) "cli" + ["meta"]=> + array(%d) { + ["runtime-id"]=> + string(36) "%s" + ["_dd.p.dm"]=> + string(2) "-0" + ["_dd.p.tid"]=> + string(16) "%s" + } + ["metrics"]=> + array(%d) { + ["process_id"]=> + float(%f) + ["_dd.agent_psr"]=> + float(1) + ["_sampling_priority_v1"]=> + float(1) + ["php.compilation.total_time_ms"]=> + float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) + } + } + [1]=> + array(%d) { + ["trace_id"]=> + string(%d) "%s" + ["span_id"]=> + string(%d) "%s" + ["start"]=> + int(%d) + ["duration"]=> + int(%d) + ["name"]=> + string(2) "s1" + ["resource"]=> + string(2) "s1" + ["service"]=> + string(12) "version_test" + ["type"]=> + string(3) "cli" + ["meta"]=> + array(%d) { + ["runtime-id"]=> + string(36) "%s" + ["_dd.p.dm"]=> + string(2) "-0" + ["version"]=> + string(5) "5.2.0" + ["_dd.p.tid"]=> + string(16) "%s" + } + ["metrics"]=> + array(%d) { + ["process_id"]=> + float(%f) + ["_dd.agent_psr"]=> + float(1) + ["_sampling_priority_v1"]=> + float(1) + ["php.compilation.total_time_ms"]=> + float(%f) + ["php.memory.peak_usage_bytes"]=> + float(%f) + ["php.memory.peak_real_usage_bytes"]=> + float(%f) + } + } +} diff --git a/tests/snapshots/logs/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt b/tests/snapshots/logs/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt index d79ca1a1c9..a8caede28c 100644 --- a/tests/snapshots/logs/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt +++ b/tests/snapshots/logs/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt @@ -1 +1 @@ -{"message":"sampled listModels","status":"info","timestamp":"2024-07-18T07:58:56.301000+00:00","env":"test","version":"1.0","service":"openai","openai.request.method":"GET","openai.request.endpoint":"\/v1\/models","openai.organization.name":"org-1234","openai.user.api_key":"sk-...9d5d","dd.trace_id":"7579939180383610820","dd.span_id":"7579939180383610820"} \ No newline at end of file +{"message":"sampled listModels","status":"info","timestamp":"2024-07-18T07:58:56.301000+00:00","env":"test","service":"openai","openai.request.method":"GET","openai.request.endpoint":"\/v1\/models","openai.organization.name":"org-1234","openai.user.api_key":"sk-...9d5d","dd.trace_id":"7579939180383610820","dd.span_id":"7579939180383610820"} diff --git a/tests/snapshots/metrics/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt b/tests/snapshots/metrics/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt index 13f79afb8c..ea00470453 100644 --- a/tests/snapshots/metrics/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt +++ b/tests/snapshots/metrics/tests.integrations.open_ai.open_ai_test.test_open_ai_service.txt @@ -1,5 +1,5 @@ -openai.request.duration:205208|d|#env:test,service:openai,version:1.0,env:test,service:openai,version:1.0,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models -openai.ratelimit.requests:3000|g|#env:test,service:openai,version:1.0,env:test,service:openai,version:1.0,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models -openai.ratelimit.tokens:250000|g|#env:test,service:openai,version:1.0,env:test,service:openai,version:1.0,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models -openai.ratelimit.remaining.requests:2999|g|#env:test,service:openai,version:1.0,env:test,service:openai,version:1.0,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models -openai.ratelimit.remaining.tokens:249989|g|#env:test,service:openai,version:1.0,env:test,service:openai,version:1.0,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models +openai.request.duration:205208|d|#env:test,service:openai,env:test,service:openai,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models +openai.ratelimit.requests:3000|g|#env:test,service:openai,env:test,service:openai,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models +openai.ratelimit.tokens:250000|g|#env:test,service:openai,env:test,service:openai,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models +openai.ratelimit.remaining.requests:2999|g|#env:test,service:openai,env:test,service:openai,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models +openai.ratelimit.remaining.tokens:249989|g|#env:test,service:openai,env:test,service:openai,openai.organization.name:org-1234,openai.user.api_key:sk-...9d5d,openai.request.endpoint:/v1/models diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_cancel_fine_tune.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_cancel_fine_tune.json index b8d8cc6998..c51939620a 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_cancel_fine_tune.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_cancel_fine_tune.json @@ -45,14 +45,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion.json index 63bb9fc77b..73a8b8df71 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion.json @@ -42,14 +42,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream.json index 8fce4d8bbb..16e07a4296 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream.json @@ -38,14 +38,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json index 948c7b2782..dd561dfaa9 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_stream_with_error.json @@ -39,14 +39,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_functions.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_functions.json index ded6a74cb3..e4dda927bc 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_functions.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_functions.json @@ -41,14 +41,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_image_input.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_image_input.json index c91a34df88..af01f39a98 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_image_input.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_image_input.json @@ -43,14 +43,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_multiple_roles.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_multiple_roles.json index 3b616a32f3..32eefe633c 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_multiple_roles.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_chat_completion_with_multiple_roles.json @@ -44,14 +44,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion.json index 2597592cc7..37a2ddf02f 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion.json @@ -42,14 +42,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion_stream.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion_stream.json index 2cb0e09ae9..1980a7da44 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion_stream.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completion_stream.json @@ -38,14 +38,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json index e5f86487cd..91a25ee447 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_completions_with_multiple_error_messages.json @@ -32,14 +32,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding.json index 94ba4f0d53..7758a3f010 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding.json @@ -38,14 +38,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding_with_multiple_inputs.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding_with_multiple_inputs.json index 668232b82f..0e5a4ea7b8 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding_with_multiple_inputs.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_embedding_with_multiple_inputs.json @@ -39,14 +39,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_file.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_file.json index a42bbeeed1..d139d78c5a 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_file.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_file.json @@ -38,14 +38,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image.json index 86d1a298d0..1627add52f 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_edit.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_edit.json index 13dd799139..751b7e5f85 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_edit.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_edit.json @@ -38,14 +38,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_variation.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_variation.json index 9b852a66a9..02d774ef3c 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_variation.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_image_variation.json @@ -36,14 +36,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_moderation.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_moderation.json index 55689db1ad..cd1ada7adc 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_moderation.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_moderation.json @@ -55,14 +55,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_json.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_json.json index 484aae9ad0..16337b676e 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_json.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_json.json @@ -35,14 +35,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_text.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_text.json index cc8f0d75bf..9418de82ad 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_text.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_text.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_verbose_json.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_verbose_json.json index 2c682f2f5e..210faa0a9b 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_verbose_json.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_transcription_to_verbose_json.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_json.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_json.json index f7ebf8cae7..ab40075f97 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_json.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_json.json @@ -35,14 +35,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_text.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_text.json index 6e45609fab..532e43f7bd 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_text.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_text.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_verbose_json.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_verbose_json.json index 7188e4127a..38bfed3260 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_verbose_json.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_create_translation_to_verbose_json.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_file.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_file.json index e1c000068a..95683507b4 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_file.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_file.json @@ -33,14 +33,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "DELETE", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_model.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_model.json index ed3269246a..f1d96dc23c 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_model.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_delete_model.json @@ -33,14 +33,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "DELETE", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_download_file.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_download_file.json index 565b413a99..9042f57928 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_download_file.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_download_file.json @@ -29,14 +29,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_files.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_files.json index a9e8b9339a..78deb6466f 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_files.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_files.json @@ -32,14 +32,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events.json index b7db554516..007d8b9e05 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events.json @@ -33,14 +33,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events_stream.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events_stream.json index ddbb0eea45..328997f5cb 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events_stream.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_fine_tune_events_stream.json @@ -31,14 +31,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models.json index 39dccfd9c1..39a08b8714 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models.json @@ -32,14 +32,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json index c76b32e0a0..12f8f02c2e 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_error.json @@ -31,14 +31,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json index 891c75a521..261aba3bab 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_list_models_with_null_error_type.json @@ -31,14 +31,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_log_prompt_completion_sample_rate.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_log_prompt_completion_sample_rate.json index 0931fb64fd..e696dcf82b 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_log_prompt_completion_sample_rate.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_log_prompt_completion_sample_rate.json @@ -42,14 +42,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_logs_disabled.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_logs_disabled.json index b0837e96a2..5c639e7fa0 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_logs_disabled.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_logs_disabled.json @@ -32,14 +32,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_metrics_disabled.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_metrics_disabled.json index 488ab56da7..1f4d8b3226 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_metrics_disabled.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_metrics_disabled.json @@ -32,14 +32,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_open_ai_service.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_open_ai_service.json index d691df3984..66e9f2c5ac 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_open_ai_service.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_open_ai_service.json @@ -19,8 +19,7 @@ "openai.response.object": "list", "openai.user.api_key": "sk-...9d5d", "runtime-id": "0a784015-fb27-4c52-9df3-dedf7130fbcb", - "span.kind": "client", - "version": "1.0" + "span.kind": "client" }, "metrics": { "_dd.agent_psr": 1, @@ -32,21 +31,19 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai", "component": "psr18", "env": "test", "http.method": "GET", "http.status_code": "200", "http.url": "https://api.openai.com/v1/models?foo=bar", "network.destination.name": "api.openai.com", - "span.kind": "client", - "version": "1.0" + "span.kind": "client" } }]] diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_file.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_file.json index a193464263..e01b9c8e50 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_file.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_file.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_fine_tune.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_fine_tune.json index a6b5d56d56..61c1f8e863 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_fine_tune.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_fine_tune.json @@ -37,14 +37,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_model.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_model.json index 27742d0965..9ed5291b04 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_model.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_retrieve_model.json @@ -34,14 +34,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "GET", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_char_limit.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_char_limit.json index 869da8612e..c9b977009b 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_char_limit.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_char_limit.json @@ -42,14 +42,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_prompt_completion_sample_rate.json b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_prompt_completion_sample_rate.json index b0eace0670..cc3d9685d7 100644 --- a/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_prompt_completion_sample_rate.json +++ b/tests/snapshots/tests.integrations.open_ai.open_ai_test.test_span_prompt_completion_sample_rate.json @@ -40,14 +40,13 @@ }, { "name": "Psr\\Http\\Client\\ClientInterface.sendRequest", - "service": "psr18", + "service": "openai-test", "resource": "sendRequest", "trace_id": 0, "span_id": 2, "parent_id": 1, "type": "http", "meta": { - "_dd.base_service": "openai-test", "component": "psr18", "env": "test", "http.method": "POST", diff --git a/zend_abstract_interface/config/config_decode.c b/zend_abstract_interface/config/config_decode.c index 6daa3bb5f0..552360f657 100644 --- a/zend_abstract_interface/config/config_decode.c +++ b/zend_abstract_interface/config/config_decode.c @@ -105,7 +105,7 @@ static bool zai_config_decode_int(zai_str value, zval *decoded_value) { return true; } -static bool zai_config_decode_map(zai_str value, zval *decoded_value, bool persistent) { +static bool zai_config_decode_map(zai_str value, zval *decoded_value, bool persistent, bool lowercase, bool map_keyless) { zval tmp; ZVAL_ARR(&tmp, pemalloc(sizeof(HashTable), persistent)); zend_hash_init(Z_ARRVAL(tmp), 8, NULL, persistent ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR, persistent); @@ -113,41 +113,90 @@ static bool zai_config_decode_map(zai_str value, zval *decoded_value, bool persi char *data = (char *)value.ptr; if (data && *data) { // non-empty const char *key_start, *key_end, *value_start, *value_end; + do { - if (*data != ',' && *data != ' ' && *data != '\t' && *data != '\n') { - key_start = key_end = data; - while (*++data) { + while (*data == ',' || *data == ' ' || *data == '\t' || *data == '\n') { + data++; + } + + if (*data) { + key_start = data; + key_end = NULL; + bool has_colon = false; + + while (*data && *data != ',') { if (*data == ':') { - while (*++data && (*data == ' ' || *data == '\t' || *data == '\n')) - ; + has_colon = true; + data++; + + while (*data == ' ' || *data == '\t' || *data == '\n') data++; - value_start = value_end = data; - if (!*data || *data == ',') { - --value_end; // empty string instead of single char + if (*data == ',' || !*data) { + value_end = NULL; } else { - while (*++data && *data != ',') { + value_start = value_end = data; + do { if (*data != ' ' && *data != '\t' && *data != '\n') { value_end = data; } + data++; + } while (*data && *data != ','); + } + + if (key_end && key_start) { + size_t key_len = key_end - key_start + 1; + + zend_string *key = zend_string_init(key_start, key_len, persistent); + if (lowercase) { + zend_str_tolower(ZSTR_VAL(key), ZSTR_LEN(key)); + } + + zval val; + if (value_end) { + size_t value_len = value_end - value_start + 1; + ZVAL_NEW_STR(&val, zend_string_init(value_start, value_len, persistent)); + } else { + if (persistent) { + ZVAL_EMPTY_PSTRING(&val); + } else { + ZVAL_EMPTY_STRING(&val); + } } + zend_hash_update(Z_ARRVAL(tmp), key, &val); + zend_string_release(key); } - size_t key_len = key_end - key_start + 1; - size_t value_len = value_end - value_start + 1; - zval val; - ZVAL_NEW_STR(&val, zend_string_init(value_start, value_len, persistent)); - zend_hash_str_update(Z_ARRVAL(tmp), key_start, key_len, &val); break; } + + // Set key_end to the last valid non-whitespace character of the key if (*data != ' ' && *data != '\t' && *data != '\n') { key_end = data; } + data++; + } + + // Handle standalone keys (without a colon) if map_keyless is enabled + if (map_keyless && !has_colon && key_end) { + size_t key_len = key_end - key_start + 1; + zend_string *key = zend_string_init(key_start, key_len, persistent); + if (lowercase) { + zend_str_tolower(ZSTR_VAL(key), ZSTR_LEN(key)); + } + + zval val; + if (persistent) { + ZVAL_EMPTY_PSTRING(&val); + } else { + ZVAL_EMPTY_STRING(&val); + } + zend_hash_update(Z_ARRVAL(tmp), key, &val); + zend_string_release(key); } - } else { - ++data; } } while (*data); + // Check if the array has any elements; if not, cleanup if (zend_hash_num_elements(Z_ARRVAL(tmp)) == 0) { zend_hash_destroy(Z_ARRVAL(tmp)); pefree(Z_ARRVAL(tmp), persistent); @@ -232,7 +281,9 @@ bool zai_config_decode_value(zai_str value, zai_config_type type, zai_custom_par case ZAI_CONFIG_TYPE_INT: return zai_config_decode_int(value, decoded_value); case ZAI_CONFIG_TYPE_MAP: - return zai_config_decode_map(value, decoded_value, persistent); + return zai_config_decode_map(value, decoded_value, persistent, false, false); + case ZAI_CONFIG_TYPE_SET_OR_MAP_LOWERCASE: + return zai_config_decode_map(value, decoded_value, persistent, true, true); case ZAI_CONFIG_TYPE_SET: return zai_config_decode_set(value, decoded_value, persistent, false); case ZAI_CONFIG_TYPE_SET_LOWERCASE: diff --git a/zend_abstract_interface/config/config_decode.h b/zend_abstract_interface/config/config_decode.h index 754bb43a01..c811b37df2 100644 --- a/zend_abstract_interface/config/config_decode.h +++ b/zend_abstract_interface/config/config_decode.h @@ -13,6 +13,7 @@ typedef enum { ZAI_CONFIG_TYPE_MAP, ZAI_CONFIG_TYPE_SET, ZAI_CONFIG_TYPE_SET_LOWERCASE, + ZAI_CONFIG_TYPE_SET_OR_MAP_LOWERCASE, ZAI_CONFIG_TYPE_JSON, ZAI_CONFIG_TYPE_STRING, ZAI_CONFIG_TYPE_CUSTOM, diff --git a/zend_abstract_interface/uri_normalization/uri_normalization.c b/zend_abstract_interface/uri_normalization/uri_normalization.c index 40e0666db1..09e25e172f 100644 --- a/zend_abstract_interface/uri_normalization/uri_normalization.c +++ b/zend_abstract_interface/uri_normalization/uri_normalization.c @@ -157,7 +157,7 @@ zend_string *zai_filter_query_string(zai_str queryString, zend_array *whitelist, zend_hash_get_current_key(whitelist, &str, &numkey); if (zend_string_equals_literal(str, "*")) { zend_string *qs = zend_string_init(queryString.ptr, queryString.len, 0); - if (pattern) { + if (pattern && ZSTR_LEN(pattern)) { zend_string *replacement = zend_string_init(ZEND_STRL(""), 0); smart_str regex = {0};