From bad3083e254c88bb2984543e255aeaec1b3dcfe9 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 10:48:30 +0200 Subject: [PATCH 1/9] fix(ci): stop JAVA_TOOL_OPTIONS from silently killing integration tests head-truncated `java`/`jbang -version` pipelines died via SIGPIPE+pipefail once JAVA_TOOL_OPTIONS injected an extra output line, aborting the integration-test setup scripts with no log output. Also wire up loud failure reporting: PR runs now fail the check via post-pr-comment, and scheduled/main runs post to Slack. Co-Authored-By: Claude Sonnet 5 --- .gitlab/dd-trace-integration/.gitlab-ci.yml | 32 ++++++++++++++++++- .../install-prerequisites.sh | 10 +++--- .../dd-trace-integration/post-pr-comment.sh | 18 +++++++++-- .../run-integration-test.sh | 4 +-- utils/run-chaos-harness.sh | 4 +-- 5 files changed, 55 insertions(+), 13 deletions(-) diff --git a/.gitlab/dd-trace-integration/.gitlab-ci.yml b/.gitlab/dd-trace-integration/.gitlab-ci.yml index 22aa991b88..295f1317f5 100644 --- a/.gitlab/dd-trace-integration/.gitlab-ci.yml +++ b/.gitlab/dd-trace-integration/.gitlab-ci.yml @@ -305,4 +305,34 @@ post-pr-comment: timeout: 5m script: - .gitlab/dd-trace-integration/post-pr-comment.sh integration-test-results - allow_failure: true + +# Loud failure signal for scheduled/main runs, which have no PR to report back to. +notify-slack-dd-trace-integration: + stage: notify + tags: [ "arch:amd64" ] + image: registry.ddbuild.io/slack-notifier:v91289620-4ec922a-latest@sha256:06b24f392ccc383d371c72001520a254edef523bc0bfdc445f487106107b4202 + needs: + - job: prepare:start + artifacts: true + - job: get-versions + artifacts: true + - job: prepare-patched-agent + artifacts: false + - job: integration-test-x64-glibc + artifacts: false + - job: integration-test-x64-musl + artifacts: false + - job: integration-test-arm64-glibc + artifacts: false + - job: integration-test-arm64-musl + artifacts: false + rules: + - if: '$JDK_VERSION != null || $DEBUG_LEVEL != null || $HASH != null || $DOWNSTREAM != null' + when: never + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + - if: '$CI_PIPELINE_SOURCE == "schedule" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + when: on_failure + - when: never + script: + - .gitlab/dd-trace-integration/notify_channel.sh "${CURRENT_VERSION}" diff --git a/.gitlab/dd-trace-integration/install-prerequisites.sh b/.gitlab/dd-trace-integration/install-prerequisites.sh index b54ca4b3c5..f89300466a 100755 --- a/.gitlab/dd-trace-integration/install-prerequisites.sh +++ b/.gitlab/dd-trace-integration/install-prerequisites.sh @@ -54,14 +54,14 @@ if ! command -v jbang &> /dev/null; then # Verify installation if command -v jbang &> /dev/null; then - JBANG_VERSION=$(jbang version 2>&1 | head -1) + JBANG_VERSION=$(jbang version 2>&1 | head -1 || true) log_info "jbang installed successfully: ${JBANG_VERSION}" else log_warn "jbang installation completed but not found in PATH" log_warn "Please ensure ~/.jbang/bin is in your PATH" fi else - JBANG_VERSION=$(jbang version 2>&1 | head -1) + JBANG_VERSION=$(jbang version 2>&1 | head -1 || true) log_info "jbang already installed: ${JBANG_VERSION}" fi @@ -124,7 +124,7 @@ else if [ -x "$JBANG_JDK_DIR/bin/java" ]; then JDK25_INSTALLED=true log_info "JDK 25 installed manually from Adoptium" - "$JBANG_JDK_DIR/bin/java" -version 2>&1 | head -1 + "$JBANG_JDK_DIR/bin/java" -version 2>&1 | head -1 || true else log_warn "JDK 25 extraction failed" fi @@ -164,7 +164,7 @@ fi if [ -z "${JAVA_HOME:-}" ]; then if command -v java &> /dev/null; then log_info "Java found in PATH" - java -version 2>&1 | head -3 + java -version 2>&1 | head -3 || true else echo "ERROR: Java not found. Please set JAVA_HOME or ensure java is in PATH" exit 1 @@ -176,7 +176,7 @@ else fi log_info "Java found at JAVA_HOME: ${JAVA_HOME}" - "${JAVA_HOME}/bin/java" -version 2>&1 | head -3 + "${JAVA_HOME}/bin/java" -version 2>&1 | head -3 || true fi # ======================================== diff --git a/.gitlab/dd-trace-integration/post-pr-comment.sh b/.gitlab/dd-trace-integration/post-pr-comment.sh index a0d8349150..bdfd4eb908 100755 --- a/.gitlab/dd-trace-integration/post-pr-comment.sh +++ b/.gitlab/dd-trace-integration/post-pr-comment.sh @@ -147,9 +147,12 @@ elif [ "${TOTAL_PASS}" -gt 0 ]; then STATUS_EMOJI=":white_check_mark:" STATUS_TEXT="PASSED" else - OVERALL_STATUS="neutral" - STATUS_EMOJI=":grey_question:" - STATUS_TEXT="NO RESULTS" + # No results at all usually means the test matrix never ran (setup/prerequisite + # failure) rather than a clean run — treat it as a failure so it isn't silently + # swallowed. + OVERALL_STATUS="failure" + STATUS_EMOJI=":rotating_light:" + STATUS_TEXT="COULD NOT RUN" fi log_info "Results: ${TOTAL_PASS} passed, ${TOTAL_FAIL} failed out of ${TOTAL} configurations" @@ -162,6 +165,15 @@ if [ "${OVERALL_STATUS}" = "success" ]; then COMMENT_BODY=":white_check_mark: **All ${TOTAL} integration tests passed** :bar_chart: [Dashboard](${DASHBOARD_URL}) · :construction_worker: [Pipeline](${CI_PIPELINE_URL:-}) · :package: \`${DDPROF_SHA:0:8}\`" +elif [ "${TOTAL}" -eq 0 ]; then + # No results produced at all - the test matrix itself never ran (e.g. a setup + # or prerequisite failure), not a clean pass/fail outcome. + COMMENT_BODY=":rotating_light: **Integration tests could not run — no results were produced** + +The test matrix in \`${RESULTS_DIR}\` is empty. This usually means a setup step +(prerequisite installation, JDK provisioning, etc.) failed before any test could run. + +:construction_worker: [Pipeline](${CI_PIPELINE_URL:-}) · :package: \`${DDPROF_SHA:0:8}\`" else # Some failures or unknowns - show full matrix COMMENT_BODY="${STATUS_EMOJI} **${TOTAL_PASS}** passed, **${TOTAL_FAIL}** failed out of **${TOTAL}** configurations diff --git a/.gitlab/dd-trace-integration/run-integration-test.sh b/.gitlab/dd-trace-integration/run-integration-test.sh index 555fd0dd6a..a36e352d53 100755 --- a/.gitlab/dd-trace-integration/run-integration-test.sh +++ b/.gitlab/dd-trace-integration/run-integration-test.sh @@ -195,7 +195,7 @@ mkdir -p "${RESULTS_DIR}" log_info "Results directory: ${RESULTS_DIR}" log_info "Java version:" -"${JAVA_HOME}/bin/java" -version 2>&1 | head -3 +"${JAVA_HOME}/bin/java" -version 2>&1 | head -3 || true # ======================================== # Install Prerequisites @@ -220,7 +220,7 @@ if ! command -v jbang &> /dev/null; then exit 1 fi -log_info "jbang version: $(jbang version 2>&1 | head -1)" +log_info "jbang version: $(jbang version 2>&1 | head -1 || true)" # ======================================== # Artifact Collection on Exit diff --git a/utils/run-chaos-harness.sh b/utils/run-chaos-harness.sh index ba8f695208..3ac1fae016 100755 --- a/utils/run-chaos-harness.sh +++ b/utils/run-chaos-harness.sh @@ -47,7 +47,7 @@ echo "Chaos run: runtime=${RUNTIME}s config=${CONFIG} allocator=${ALLOCATOR}" # doesn't match what's already active; otherwise just use `java` as found. if [ -n "${CHAOS_JDK:-}" ]; then JDK_MAJOR="${CHAOS_JDK%%.*}" - ACTIVE_MAJOR=$(java -version 2>&1 | head -1 | grep -oE '"[0-9]+' | tr -d '"') + ACTIVE_MAJOR=$(java -version 2>&1 | grep -m1 'version "' | grep -oE '"[0-9]+' | tr -d '"') if [ "${ACTIVE_MAJOR}" != "${JDK_MAJOR}" ]; then JDK_ARCH=$(uname -m | sed 's/x86_64/x64/') JDK_INSTALL_DIR="${CHAOS_JDK_DIR:-${WORK_DIR}/jdk-${CHAOS_JDK}}" @@ -73,7 +73,7 @@ if ! command -v java >/dev/null 2>&1; then echo "FAIL:no java on PATH (set CHAOS_JDK to have one downloaded)" >&2 exit 1 fi -echo "Using: $(java -version 2>&1 | head -1)" +echo "Using: $(java -version 2>&1 | grep -m1 'version "')" # --- ddprof jar -------------------------------------------------------------- # Prefer a local build artifact. If absent and CURRENT_VERSION is set (CI From 67eeec6798c6498e25c416f5c06e60d8e0e22600 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 13:00:55 +0200 Subject: [PATCH 2/9] fix: skipped JFR validation must fail, not silently pass A skipped JFR conformance check (e.g. JDK 25 unavailable) was reported as VALIDATION_SKIPPED with exit 0, letting integration tests appear to pass even though no conformance check actually ran. Co-Authored-By: Claude Sonnet 5 --- test-validation/validate-jfr-conformance.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test-validation/validate-jfr-conformance.sh b/test-validation/validate-jfr-conformance.sh index a9f11538c2..b3c6950112 100755 --- a/test-validation/validate-jfr-conformance.sh +++ b/test-validation/validate-jfr-conformance.sh @@ -305,11 +305,13 @@ log_info "" # Check if JFR validation should be skipped (JDK 25 unavailable) if [ -f /tmp/skip-jfr-validation ]; then SKIP_REASON=$(cat /tmp/skip-jfr-validation 2>/dev/null || echo "prerequisite unavailable") - log_warn "Skipping JFR validation: ${SKIP_REASON}" + log_error "Cannot run JFR validation: ${SKIP_REASON}" + # A skipped validation is not a pass — no conformance check actually ran, so + # this must not be reported as success further up the chain. if [ -n "${OUTPUT_FILE}" ]; then - echo "VALIDATION_SKIPPED: ${SKIP_REASON}" > "${OUTPUT_FILE}" + echo "VALIDATION_FAILED: Skipped - ${SKIP_REASON}" > "${OUTPUT_FILE}" fi - exit 0 + exit 1 fi # jfr-shell (jafar) requires Java 25 (class file version 69.0) From deadb9f0810b659822c046ae99dac422ab1f062e Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 13:01:22 +0200 Subject: [PATCH 3/9] fix: route jbang Maven resolution through internal proxy CI runners can't reach Maven Central directly, so jbang fails to resolve io.btrace:jafar-shell. Route it through MAVEN_REPOSITORY_PROXY, the same internal proxy Gradle already uses. Co-Authored-By: Claude Sonnet 5 --- .../dd-trace-integration/install-prerequisites.sh | 14 ++++++++++---- test-validation/validate-jfr-conformance.sh | 9 ++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.gitlab/dd-trace-integration/install-prerequisites.sh b/.gitlab/dd-trace-integration/install-prerequisites.sh index f89300466a..56f8c6c071 100755 --- a/.gitlab/dd-trace-integration/install-prerequisites.sh +++ b/.gitlab/dd-trace-integration/install-prerequisites.sh @@ -143,12 +143,18 @@ fi # ======================================== # Pre-warm jfr-shell backend # ======================================== -# jafar-shell resolves its backend plugin (io.btrace:jfr-shell-jafar) from Maven at -# runtime. If that artifact is unavailable (network restriction, version not yet -# published), every validation run fails. Detect this early so we can skip gracefully. +# jafar-shell itself (io.btrace:jafar-shell) is resolved by jbang from Maven Central, +# which CI runners can't reach directly. Route jbang through the same internal Maven +# proxy Gradle already uses (MAVEN_REPOSITORY_PROXY) so resolution doesn't depend on +# public internet egress. Detect remaining resolution failures early so we can skip +# gracefully instead of failing every validation run. if [ ! -f /tmp/skip-jfr-validation ] && command -v jbang &> /dev/null; then log_info "Pre-warming jfr-shell backend..." - PREWARM_OUT=$(jbang --java 25 jfr-shell@btraceio script /dev/null 2>&1 || true) + if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then + PREWARM_OUT=$(jbang --java 25 --repos="central=${MAVEN_REPOSITORY_PROXY}" jfr-shell@btraceio script /dev/null 2>&1 || true) + else + PREWARM_OUT=$(jbang --java 25 jfr-shell@btraceio script /dev/null 2>&1 || true) + fi if echo "$PREWARM_OUT" | grep -q "No backends found\|No JFR backends available\|Failed to resolve artifact.*jfr-shell-jafar"; then log_warn "jfr-shell backend unavailable (io.btrace:jfr-shell-jafar not resolvable from Maven)" log_warn "JFR validation will be skipped" diff --git a/test-validation/validate-jfr-conformance.sh b/test-validation/validate-jfr-conformance.sh index b3c6950112..8b4c90c1f6 100755 --- a/test-validation/validate-jfr-conformance.sh +++ b/test-validation/validate-jfr-conformance.sh @@ -320,8 +320,15 @@ unset JAVA_VERSION # Prevent jbang from picking up test JDK version log_info "Using Java 25 for jbang (required by jfr-shell)" +# jbang resolves jafar-shell from Maven Central, which CI runners can't reach directly. +# Route it through the same internal Maven proxy Gradle uses (MAVEN_REPOSITORY_PROXY). +JBANG_REPOS_OPT="" +if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then + JBANG_REPOS_OPT="--repos=\"central=${MAVEN_REPOSITORY_PROXY}\"" +fi + # Pass calculated thresholds directly (single source of truth - no duplicate logic in jfrs) -VALIDATION_CMD="jbang --java 25 jfr-shell@btraceio script \"${VALIDATION_SCRIPT}\" \"${JFR_FILE}\" \"${PROFILE}\" \"${MIN_EXECUTION_SAMPLES}\" \"${MIN_ALLOCATION_SAMPLES}\" \"${MIN_THREAD_COUNT}\" \"${EXPECTED_CPU_EVENT}\" \"${EXPECTED_ALLOC_EVENT}\" \"${CHECK_ENDPOINT}\" \"${UNEXPECTED_JDK_EXEC}\" \"${UNEXPECTED_JDK_ALLOC}\" \"${UNEXPECTED_DD_EXEC}\" \"${UNEXPECTED_DD_ALLOC}\" \"${UNEXPECTED_ENDPOINT}\"" +VALIDATION_CMD="jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio script \"${VALIDATION_SCRIPT}\" \"${JFR_FILE}\" \"${PROFILE}\" \"${MIN_EXECUTION_SAMPLES}\" \"${MIN_ALLOCATION_SAMPLES}\" \"${MIN_THREAD_COUNT}\" \"${EXPECTED_CPU_EVENT}\" \"${EXPECTED_ALLOC_EVENT}\" \"${CHECK_ENDPOINT}\" \"${UNEXPECTED_JDK_EXEC}\" \"${UNEXPECTED_JDK_ALLOC}\" \"${UNEXPECTED_DD_EXEC}\" \"${UNEXPECTED_DD_ALLOC}\" \"${UNEXPECTED_ENDPOINT}\"" if [ -n "${OUTPUT_FILE}" ]; then # Set up trap to write failure marker if script is killed/crashes From c498439956b44e4ee6b79a6dd7f2dff103219eaa Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 13:54:38 +0200 Subject: [PATCH 4/9] fix: post-pr-comment never posted or failed the pipeline The job's needs never included prepare:start, so it never received DDPROF_COMMIT_BRANCH via dotenv and exited 0 before checking any test results. Also replace the pr-commenter/benchmarking-platform clone (broken: wrong repo path, needs public GitHub egress) with the existing dd-octo-sts based upsert-github-pr-comment.sh used elsewhere in this repo, and make the failure exit unconditional on posting. Co-Authored-By: Claude Sonnet 5 --- .gitlab/dd-trace-integration/.gitlab-ci.yml | 5 ++ .../dd-trace-integration/post-pr-comment.sh | 80 +++---------------- 2 files changed, 17 insertions(+), 68 deletions(-) diff --git a/.gitlab/dd-trace-integration/.gitlab-ci.yml b/.gitlab/dd-trace-integration/.gitlab-ci.yml index babb7e7223..6ec9ec366c 100644 --- a/.gitlab/dd-trace-integration/.gitlab-ci.yml +++ b/.gitlab/dd-trace-integration/.gitlab-ci.yml @@ -287,7 +287,12 @@ post-pr-comment: stage: integration-test tags: [ "arch:arm64" ] image: registry.ddbuild.io/images/dd-octo-sts-ci-base:2025.06-1 + id_tokens: + DDOCTOSTS_ID_TOKEN: + aud: dd-octo-sts needs: + - job: prepare:start + artifacts: true - job: prepare-patched-agent artifacts: true - job: integration-test-x64-glibc diff --git a/.gitlab/dd-trace-integration/post-pr-comment.sh b/.gitlab/dd-trace-integration/post-pr-comment.sh index bdfd4eb908..4dd812022f 100755 --- a/.gitlab/dd-trace-integration/post-pr-comment.sh +++ b/.gitlab/dd-trace-integration/post-pr-comment.sh @@ -13,10 +13,12 @@ # Requires: # - DDPROF_COMMIT_BRANCH: Branch name to find PR # - CI_PIPELINE_URL: Link to pipeline -# - pr-commenter tool (available in CI images) +# - dd-octo-sts CLI (for GitHub token exchange, via upsert-github-pr-comment.sh) set -euo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # Colors for logging GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -28,55 +30,11 @@ log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; } log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } RESULTS_DIR="${1:-integration-test-results}" -REPO="DataDog/java-profiler" # Dashboard URL (GitHub Pages) DASHBOARD_URL="https://datadog.github.io/java-profiler/integration/" -# Check required tools - try to get pr-commenter from benchmarking-platform if not available -PR_COMMENTER_AVAILABLE=false -if command -v pr-commenter >/dev/null 2>&1; then - PR_COMMENTER_AVAILABLE=true -elif [ -n "${CI_JOB_TOKEN:-}" ]; then - # In CI, clone benchmarking-platform to get pr-commenter - log_info "pr-commenter not found, cloning benchmarking-platform..." - PLATFORM_DIR=$(mktemp -d) - trap "rm -rf ${PLATFORM_DIR}" EXIT - git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ddbuild.io/DataDog/".insteadOf "https://github.com/DataDog/" - if git clone --depth 1 --branch dd-trace-go https://github.com/DataDog/benchmarking-platform "${PLATFORM_DIR}" 2>/dev/null; then - if [ -x "${PLATFORM_DIR}/tools/pr-commenter" ]; then - export PATH="${PLATFORM_DIR}/tools:${PATH}" - PR_COMMENTER_AVAILABLE=true - log_info "pr-commenter available from benchmarking-platform" - elif [ -f "${PLATFORM_DIR}/tools/pr-commenter.py" ]; then - # Try Python version - alias pr-commenter="python3 ${PLATFORM_DIR}/tools/pr-commenter.py" - PR_COMMENTER_AVAILABLE=true - log_info "pr-commenter.py available from benchmarking-platform" - else - log_warn "pr-commenter not found in benchmarking-platform" - ls -la "${PLATFORM_DIR}/tools/" 2>/dev/null || log_warn "No tools directory" - fi - else - log_warn "Failed to clone benchmarking-platform" - fi -else - log_warn "pr-commenter not found and not in CI - will print comment instead" -fi - -# Check required environment -if [ -z "${DDPROF_COMMIT_BRANCH:-}" ]; then - log_warn "DDPROF_COMMIT_BRANCH not set - skipping comment" - exit 0 -fi - -# Skip for main/master branches (no PR) -if [ "${DDPROF_COMMIT_BRANCH}" = "main" ] || [ "${DDPROF_COMMIT_BRANCH}" = "master" ]; then - log_info "Skipping PR comment for ${DDPROF_COMMIT_BRANCH} branch" - exit 0 -fi - -log_info "Posting comment for branch: ${DDPROF_COMMIT_BRANCH}" +log_info "Collecting results for branch: ${DDPROF_COMMIT_BRANCH:-}" # Collect test results log_info "Collecting test results from ${RESULTS_DIR}..." @@ -217,28 +175,14 @@ $(echo -e "${FAILURES}")" - :package: Commit: \`${DDPROF_SHA}\`" fi -# Post comment using pr-commenter -if [ "${PR_COMMENTER_AVAILABLE}" = "true" ]; then - log_info "Posting comment via pr-commenter..." - - if echo "${COMMENT_BODY}" | pr-commenter \ - --for-repo="${REPO}" \ - --for-pr="${DDPROF_COMMIT_BRANCH}" \ - --header="Integration Tests" \ - --on-duplicate=replace; then - log_info "Successfully posted comment" - else - log_error "Failed to post comment via pr-commenter" - log_info "Comment that would be posted:" - echo "${COMMENT_BODY}" - exit 1 - fi -else - log_info "Comment that would be posted to PR:" - echo "" - echo "${COMMENT_BODY}" - echo "" -fi +# Post comment via dd-octo-sts (upsert-github-pr-comment.sh handles missing +# branch/PR/token gracefully, so a comment-posting problem never masks the +# actual test outcome below). +BODY_FILE=$(mktemp) +trap 'rm -f "${BODY_FILE}"' EXIT +echo "${COMMENT_BODY}" > "${BODY_FILE}" +"${HERE}/../scripts/upsert-github-pr-comment.sh" \ + "dd-trace-integration-results" "${DDPROF_COMMIT_BRANCH:-}" "${BODY_FILE}" # Exit with failure if tests failed (makes pipeline fail) if [ "${OVERALL_STATUS}" = "failure" ]; then From 2b2dedacf4f1a6a14dcc9bb28e9a5132bbcf9286 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 13:54:48 +0200 Subject: [PATCH 5/9] fix: stop gating JFR validation on jfr-shell backend resolution The pre-warm check skipped all JFR validation whenever the optional jfr-shell-jafar/jfr-shell-jdk backend plugins failed to resolve. That backend system uses its own embedded resolver, separate from jbang's --repos, so it can't be routed through the Maven proxy and can fail even when jafar-shell itself resolves fine. It doesn't matter: the validation script only uses jafar-shell's bundled parser (open/show), which never touches the backend system. Verified locally that validation passes end-to-end with the backend plugins entirely unresolvable. Only skip now if jfr-shell itself fails to start. Co-Authored-By: Claude Sonnet 5 --- .../install-prerequisites.sh | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.gitlab/dd-trace-integration/install-prerequisites.sh b/.gitlab/dd-trace-integration/install-prerequisites.sh index 56f8c6c071..de0fac333a 100755 --- a/.gitlab/dd-trace-integration/install-prerequisites.sh +++ b/.gitlab/dd-trace-integration/install-prerequisites.sh @@ -141,26 +141,33 @@ if [ "$JDK25_INSTALLED" = "false" ]; then fi # ======================================== -# Pre-warm jfr-shell backend +# Pre-warm jfr-shell # ======================================== # jafar-shell itself (io.btrace:jafar-shell) is resolved by jbang from Maven Central, # which CI runners can't reach directly. Route jbang through the same internal Maven # proxy Gradle already uses (MAVEN_REPOSITORY_PROXY) so resolution doesn't depend on -# public internet egress. Detect remaining resolution failures early so we can skip -# gracefully instead of failing every validation run. +# public internet egress. +# +# jafar-shell also has an optional pluggable "backend" system (io.btrace:jfr-shell-jafar +# / jfr-shell-jdk) resolved separately at runtime via its own embedded resolver, which +# does not go through jbang's --repos and so cannot be routed through the proxy. That's +# fine: the "open"+"show" flow used by validate-jfr-conformance.sh reads JFR files via +# jafar-shell's own bundled parser and never touches the backend system, so a missing +# backend does not affect real validation and must not be treated as fatal here. if [ ! -f /tmp/skip-jfr-validation ] && command -v jbang &> /dev/null; then - log_info "Pre-warming jfr-shell backend..." + log_info "Pre-warming jfr-shell..." if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then - PREWARM_OUT=$(jbang --java 25 --repos="central=${MAVEN_REPOSITORY_PROXY}" jfr-shell@btraceio script /dev/null 2>&1 || true) + JBANG_REPOS_OPT="--repos=central=${MAVEN_REPOSITORY_PROXY}" else - PREWARM_OUT=$(jbang --java 25 jfr-shell@btraceio script /dev/null 2>&1 || true) + JBANG_REPOS_OPT="" fi - if echo "$PREWARM_OUT" | grep -q "No backends found\|No JFR backends available\|Failed to resolve artifact.*jfr-shell-jafar"; then - log_warn "jfr-shell backend unavailable (io.btrace:jfr-shell-jafar not resolvable from Maven)" - log_warn "JFR validation will be skipped" - echo "jfr-shell backend unavailable (io.btrace:jfr-shell-jafar not resolvable from Maven)" > /tmp/skip-jfr-validation + if PREWARM_OUT=$(jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio script /dev/null 2>&1); then + log_info "jfr-shell ready" else - log_info "jfr-shell backend ready" + log_warn "jfr-shell failed to start (io.btrace:jafar-shell not resolvable from Maven)" + log_warn "JFR validation will be skipped" + echo "jfr-shell failed to start (io.btrace:jafar-shell not resolvable from Maven)" > /tmp/skip-jfr-validation + echo "$PREWARM_OUT" | tail -20 fi fi From 6f88e7b6d09f93b2c397fae42b056a5022e496ac Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 15:50:15 +0200 Subject: [PATCH 6/9] fix: guard JFR validation queries against unregistered event types jfr-shell hard-fails a query for an event type absent from the recording's metadata instead of returning zero, cascading into undefined-variable errors. Precompute type presence via `jfr summary` and skip guarded queries accordingly. Also fix the datadog.EndpointEvent -> datadog.Endpoint typo and silence `uptime: command not found` when it's unavailable. --- .../run-integration-test.sh | 2 +- test-validation/validate-jfr-conformance.sh | 47 +++- test-validation/validate-jfr.jfrs | 200 ++++++++++++++---- 3 files changed, 201 insertions(+), 48 deletions(-) diff --git a/.gitlab/dd-trace-integration/run-integration-test.sh b/.gitlab/dd-trace-integration/run-integration-test.sh index a36e352d53..48b7d72308 100755 --- a/.gitlab/dd-trace-integration/run-integration-test.sh +++ b/.gitlab/dd-trace-integration/run-integration-test.sh @@ -103,7 +103,7 @@ collect_system_metrics() { local cpu_count=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "1") local cpu_quota=$(cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 2>/dev/null || echo "-1") local cpu_period=$(cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 2>/dev/null || echo "-1") - local load_avg=$(uptime | awk -F'load average:' '{print $2}' | xargs) + local load_avg=$(command -v uptime >/dev/null 2>&1 && uptime | awk -F'load average:' '{print $2}' | xargs || echo "unavailable") local container=$(test -f /.dockerenv && echo "true" || echo "false") # Parse throttling stats diff --git a/test-validation/validate-jfr-conformance.sh b/test-validation/validate-jfr-conformance.sh index 8b4c90c1f6..303ce8628f 100755 --- a/test-validation/validate-jfr-conformance.sh +++ b/test-validation/validate-jfr-conformance.sh @@ -286,7 +286,50 @@ log_info " jdk.ExecutionSample: ${UNEXPECTED_JDK_EXEC}" log_info " jdk.ObjectAllocationSample: ${UNEXPECTED_JDK_ALLOC}" log_info " datadog.ExecutionSample: ${UNEXPECTED_DD_EXEC}" log_info " datadog.ObjectSample: ${UNEXPECTED_DD_ALLOC}" -log_info " datadog.EndpointEvent: ${UNEXPECTED_ENDPOINT}" +log_info " datadog.Endpoint: ${UNEXPECTED_ENDPOINT}" +log_info "" + +# ======================================== +# Detect which event types are actually registered in the recording +# ======================================== +# jfr-shell's query engine hard-fails with "Event type not found" instead of +# returning a zero count when a type isn't in the recording's metadata at all +# (e.g. JDK built-in CPU/allocation events are entirely absent from +# ddprof-only recordings, or an event predates the JDK that produced the +# file). Precompute presence with the JDK's own `jfr summary`, which lists +# every registered event type unconditionally, so the .jfrs script can skip +# queries for types that were never registered instead of crashing. +EVENT_SUMMARY="" +if ! EVENT_SUMMARY=$(jbang jdk exec -j 25 jfr summary "${JFR_FILE}" 2>/dev/null); then + log_warn "Could not run 'jfr summary' on ${JFR_FILE}; assuming all event types are present" + EVENT_SUMMARY="" +fi + +has_event_type() { + local type="$1" + if [ -z "${EVENT_SUMMARY}" ]; then + echo "true" + elif echo "${EVENT_SUMMARY}" | grep -qE "^[[:space:]]*${type//./\\.}[[:space:]]"; then + echo "true" + else + echo "false" + fi +} + +HAS_JDK_EXEC=$(has_event_type "jdk.ExecutionSample") +HAS_DD_EXEC=$(has_event_type "datadog.ExecutionSample") +HAS_JDK_ALLOC=$(has_event_type "jdk.ObjectAllocationSample") +HAS_DD_ALLOC=$(has_event_type "datadog.ObjectSample") +HAS_JDK_THREAD_ALLOC=$(has_event_type "jdk.ThreadAllocationStatistics") +HAS_DD_ENDPOINT=$(has_event_type "datadog.Endpoint") + +log_info "Event types registered in recording:" +log_info " jdk.ExecutionSample: ${HAS_JDK_EXEC}" +log_info " datadog.ExecutionSample: ${HAS_DD_EXEC}" +log_info " jdk.ObjectAllocationSample: ${HAS_JDK_ALLOC}" +log_info " datadog.ObjectSample: ${HAS_DD_ALLOC}" +log_info " jdk.ThreadAllocationStatistics: ${HAS_JDK_THREAD_ALLOC}" +log_info " datadog.Endpoint: ${HAS_DD_ENDPOINT}" log_info "" # Prepare validation command @@ -328,7 +371,7 @@ if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then fi # Pass calculated thresholds directly (single source of truth - no duplicate logic in jfrs) -VALIDATION_CMD="jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio script \"${VALIDATION_SCRIPT}\" \"${JFR_FILE}\" \"${PROFILE}\" \"${MIN_EXECUTION_SAMPLES}\" \"${MIN_ALLOCATION_SAMPLES}\" \"${MIN_THREAD_COUNT}\" \"${EXPECTED_CPU_EVENT}\" \"${EXPECTED_ALLOC_EVENT}\" \"${CHECK_ENDPOINT}\" \"${UNEXPECTED_JDK_EXEC}\" \"${UNEXPECTED_JDK_ALLOC}\" \"${UNEXPECTED_DD_EXEC}\" \"${UNEXPECTED_DD_ALLOC}\" \"${UNEXPECTED_ENDPOINT}\"" +VALIDATION_CMD="jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio script \"${VALIDATION_SCRIPT}\" \"${JFR_FILE}\" \"${PROFILE}\" \"${MIN_EXECUTION_SAMPLES}\" \"${MIN_ALLOCATION_SAMPLES}\" \"${MIN_THREAD_COUNT}\" \"${EXPECTED_CPU_EVENT}\" \"${EXPECTED_ALLOC_EVENT}\" \"${CHECK_ENDPOINT}\" \"${UNEXPECTED_JDK_EXEC}\" \"${UNEXPECTED_JDK_ALLOC}\" \"${UNEXPECTED_DD_EXEC}\" \"${UNEXPECTED_DD_ALLOC}\" \"${UNEXPECTED_ENDPOINT}\" \"${HAS_JDK_EXEC}\" \"${HAS_DD_EXEC}\" \"${HAS_JDK_ALLOC}\" \"${HAS_DD_ALLOC}\" \"${HAS_JDK_THREAD_ALLOC}\" \"${HAS_DD_ENDPOINT}\"" if [ -n "${OUTPUT_FILE}" ]; then # Set up trap to write failure marker if script is killed/crashes diff --git a/test-validation/validate-jfr.jfrs b/test-validation/validate-jfr.jfrs index 087bcdc4b3..d4e5b9058e 100755 --- a/test-validation/validate-jfr.jfrs +++ b/test-validation/validate-jfr.jfrs @@ -15,12 +15,26 @@ # $10 - Unexpected jdk.ObjectAllocationSample: true|false (optional, default false) # $11 - Unexpected datadog.ExecutionSample: true|false (optional, default false) # $12 - Unexpected datadog.ObjectSample: true|false (optional, default false) -# $13 - Unexpected datadog.EndpointEvent: true|false (optional, default false) +# $13 - Unexpected datadog.Endpoint: true|false (optional, default false) +# $14 - jdk.ExecutionSample registered in recording: true|false (optional, default true) +# $15 - datadog.ExecutionSample registered in recording: true|false (optional, default true) +# $16 - jdk.ObjectAllocationSample registered in recording: true|false (optional, default true) +# $17 - datadog.ObjectSample registered in recording: true|false (optional, default true) +# $18 - jdk.ThreadAllocationStatistics registered in recording: true|false (optional, default true) +# $19 - datadog.Endpoint registered in recording: true|false (optional, default true) # # Thresholds are calculated by the calling bash script (validate-jfr-conformance.sh) # based on platform, JVM type, and libc variant. This eliminates duplicate # threshold logic and provides a single source of truth. # +# The $14-$19 "registered in recording" flags are also calculated by the calling +# script (via `jfr summary`), because jfr-shell's query engine hard-fails with +# "Event type not found" instead of returning a zero count when a type isn't in +# the recording's metadata at all (e.g. JDK built-in events are entirely absent +# from ddprof-only recordings, or an event predates the JDK that produced the +# file). Every query against one of these six types is guarded by its flag so a +# missing type is treated as zero events instead of aborting the script. +# # The script prints SUCCESS or VALIDATION_FAILED at the end. # The calling script should check for "VALIDATION_FAILED" in output. @@ -37,6 +51,12 @@ set unexpected_jdk_alloc = "$10" set unexpected_dd_exec = "$11" set unexpected_dd_alloc = "$12" set unexpected_endpoint = "$13" +set has_jdk_exec = "$14" +set has_dd_exec = "$15" +set has_jdk_alloc = "$16" +set has_dd_alloc = "$17" +set has_jdk_thread_alloc = "$18" +set has_dd_endpoint = "$19" # Default min values if not provided (legacy compatibility) if "${min_execution_samples}" == "" @@ -85,6 +105,33 @@ if "${unexpected_endpoint}" == "" set unexpected_endpoint = "false" endif +# Default type-presence flags to true (legacy compatibility: assume present, +# matching the pre-existing unguarded-query behavior when the caller doesn't +# pass these) +if "${has_jdk_exec}" == "" + set has_jdk_exec = "true" +endif + +if "${has_dd_exec}" == "" + set has_dd_exec = "true" +endif + +if "${has_jdk_alloc}" == "" + set has_jdk_alloc = "true" +endif + +if "${has_dd_alloc}" == "" + set has_dd_alloc = "true" +endif + +if "${has_jdk_thread_alloc}" == "" + set has_jdk_thread_alloc = "true" +endif + +if "${has_dd_endpoint}" == "" + set has_dd_endpoint = "true" +endif + echo "=== JFR Validation ===" echo "Recording: $1" echo "Scenario: ${scenario}" @@ -114,9 +161,19 @@ set validation_failed = "false" # ======================================== echo "[1/8] Validating ExecutionSample events..." -# Check both jdk.ExecutionSample and datadog.ExecutionSample -set jdk_exec_count = events/jdk.ExecutionSample | count() -set dd_exec_count = events/datadog.ExecutionSample | count() +# Check both jdk.ExecutionSample and datadog.ExecutionSample (skip whichever +# isn't registered in this recording's metadata) +if "${has_jdk_exec}" == "true" + set jdk_exec_count = events/jdk.ExecutionSample | count() +else + set jdk_exec_count = 0 +endif + +if "${has_dd_exec}" == "true" + set dd_exec_count = events/datadog.ExecutionSample | count() +else + set dd_exec_count = 0 +endif echo " Found: ${jdk_exec_count.count} jdk.ExecutionSample events" echo " Found: ${dd_exec_count.count} datadog.ExecutionSample events" @@ -194,7 +251,11 @@ echo "[2/8] Validating stack traces..." # Use the event type we detected in step 1 # Validation: all present ExecutionSample events should have stack traces if "${exec_event_type}" == "jdk" - set samples_with_stack = events/jdk.ExecutionSample[exists(stackTrace)] | count() + if "${has_jdk_exec}" == "true" + set samples_with_stack = events/jdk.ExecutionSample[exists(stackTrace)] | count() + else + set samples_with_stack = 0 + endif echo " Samples with stack traces: ${samples_with_stack.count} of ${jdk_exec_count.count}" if ${samples_with_stack.count} < ${jdk_exec_count.count} @@ -205,7 +266,11 @@ if "${exec_event_type}" == "jdk" echo " ✓ All ExecutionSample events have stack traces (${samples_with_stack.count})" endif else - set samples_with_stack = events/datadog.ExecutionSample[exists(stackTrace)] | count() + if "${has_dd_exec}" == "true" + set samples_with_stack = events/datadog.ExecutionSample[exists(stackTrace)] | count() + else + set samples_with_stack = 0 + endif echo " Samples with stack traces: ${samples_with_stack.count} of ${dd_exec_count.count}" if ${samples_with_stack.count} < ${dd_exec_count.count} @@ -225,9 +290,17 @@ echo "[3/8] Validating thread diversity..." # Use the event type we detected in step 1 # Note: datadog.ExecutionSample uses eventThread, jdk.ExecutionSample uses sampledThread if "${exec_event_type}" == "jdk" - set unique_threads = events/jdk.ExecutionSample | groupBy(sampledThread/javaName) | count() + if "${has_jdk_exec}" == "true" + set unique_threads = events/jdk.ExecutionSample | groupBy(sampledThread/javaName) | count() + else + set unique_threads = 0 + endif else - set unique_threads = events/datadog.ExecutionSample | groupBy(eventThread/javaName) | count() + if "${has_dd_exec}" == "true" + set unique_threads = events/datadog.ExecutionSample | groupBy(eventThread/javaName) | count() + else + set unique_threads = 0 + endif endif echo " Unique threads sampled: ${unique_threads.count}" @@ -243,9 +316,17 @@ endif # Show top threads by sample count echo " Top threads by samples:" if "${exec_event_type}" == "jdk" - show events/jdk.ExecutionSample | groupBy(sampledThread/javaName) | top(5, by=count) + if "${has_jdk_exec}" == "true" + show events/jdk.ExecutionSample | groupBy(sampledThread/javaName) | top(5, by=count) + else + echo " (jdk.ExecutionSample not present in recording)" + endif else - show events/datadog.ExecutionSample | groupBy(eventThread/javaName) | top(5, by=count) + if "${has_dd_exec}" == "true" + show events/datadog.ExecutionSample | groupBy(eventThread/javaName) | top(5, by=count) + else + echo " (datadog.ExecutionSample not present in recording)" + endif endif # ======================================== @@ -254,9 +335,19 @@ endif echo "" echo "[4/8] Validating ObjectAllocationSample events..." -# Check both jdk.ObjectAllocationSample and datadog.ObjectSample -set jdk_alloc_count = events/jdk.ObjectAllocationSample | count() -set dd_alloc_count = events/datadog.ObjectSample | count() +# Check both jdk.ObjectAllocationSample and datadog.ObjectSample (skip +# whichever isn't registered in this recording's metadata) +if "${has_jdk_alloc}" == "true" + set jdk_alloc_count = events/jdk.ObjectAllocationSample | count() +else + set jdk_alloc_count = 0 +endif + +if "${has_dd_alloc}" == "true" + set dd_alloc_count = events/datadog.ObjectSample | count() +else + set dd_alloc_count = 0 +endif echo " Found: ${jdk_alloc_count.count} jdk.ObjectAllocationSample events" echo " Found: ${dd_alloc_count.count} datadog.ObjectSample events" @@ -351,7 +442,11 @@ endif echo "" echo "[5/8] Validating ThreadAllocationStatistics..." -set thread_alloc = events/jdk.ThreadAllocationStatistics | count() +if "${has_jdk_thread_alloc}" == "true" + set thread_alloc = events/jdk.ThreadAllocationStatistics | count() +else + set thread_alloc = 0 +endif echo " Found: ${thread_alloc.count} ThreadAllocationStatistics events" @@ -369,13 +464,18 @@ echo "" echo "[6/8] Validating endpoint events..." if "${check_endpoint}" == "true" - set endpoint_count = events/datadog.EndpointEvent | count() - echo " Found: ${endpoint_count.count} datadog.EndpointEvent events" + if "${has_dd_endpoint}" == "true" + set endpoint_count = events/datadog.Endpoint | count() + echo " Found: ${endpoint_count.count} datadog.Endpoint events" - if ${endpoint_count.count} > 0 - echo " ✓ Endpoint events present (tracer integration working)" + if ${endpoint_count.count} > 0 + echo " ✓ Endpoint events present (tracer integration working)" + else + echo " WARNING: No endpoint events found" + echo " This may indicate tracer is not capturing any requests" + endif else - echo " WARNING: No endpoint events found" + echo " WARNING: datadog.Endpoint event type not present in recording" echo " This may indicate tracer is not capturing any requests" endif else @@ -392,51 +492,61 @@ set has_unexpected = "false" # Check for unexpected jdk.ExecutionSample if "${unexpected_jdk_exec}" == "true" - set check_jdk_exec = events/jdk.ExecutionSample | count() - if ${check_jdk_exec.count} > 0 - echo " ERROR: Found unexpected jdk.ExecutionSample events (${check_jdk_exec.count})" - set validation_failed = "true" - set has_unexpected = "true" + if "${has_jdk_exec}" == "true" + set check_jdk_exec = events/jdk.ExecutionSample | count() + if ${check_jdk_exec.count} > 0 + echo " ERROR: Found unexpected jdk.ExecutionSample events (${check_jdk_exec.count})" + set validation_failed = "true" + set has_unexpected = "true" + endif endif endif # Check for unexpected jdk.ObjectAllocationSample if "${unexpected_jdk_alloc}" == "true" - set check_jdk_alloc = events/jdk.ObjectAllocationSample | count() - if ${check_jdk_alloc.count} > 0 - echo " ERROR: Found unexpected jdk.ObjectAllocationSample events (${check_jdk_alloc.count})" - set validation_failed = "true" - set has_unexpected = "true" + if "${has_jdk_alloc}" == "true" + set check_jdk_alloc = events/jdk.ObjectAllocationSample | count() + if ${check_jdk_alloc.count} > 0 + echo " ERROR: Found unexpected jdk.ObjectAllocationSample events (${check_jdk_alloc.count})" + set validation_failed = "true" + set has_unexpected = "true" + endif endif endif # Check for unexpected datadog.ExecutionSample if "${unexpected_dd_exec}" == "true" - set check_dd_exec = events/datadog.ExecutionSample | count() - if ${check_dd_exec.count} > 0 - echo " ERROR: Found unexpected datadog.ExecutionSample events (${check_dd_exec.count})" - set validation_failed = "true" - set has_unexpected = "true" + if "${has_dd_exec}" == "true" + set check_dd_exec = events/datadog.ExecutionSample | count() + if ${check_dd_exec.count} > 0 + echo " ERROR: Found unexpected datadog.ExecutionSample events (${check_dd_exec.count})" + set validation_failed = "true" + set has_unexpected = "true" + endif endif endif # Check for unexpected datadog.ObjectSample if "${unexpected_dd_alloc}" == "true" - set check_dd_alloc = events/datadog.ObjectSample | count() - if ${check_dd_alloc.count} > 0 - echo " ERROR: Found unexpected datadog.ObjectSample events (${check_dd_alloc.count})" - set validation_failed = "true" - set has_unexpected = "true" + if "${has_dd_alloc}" == "true" + set check_dd_alloc = events/datadog.ObjectSample | count() + if ${check_dd_alloc.count} > 0 + echo " ERROR: Found unexpected datadog.ObjectSample events (${check_dd_alloc.count})" + set validation_failed = "true" + set has_unexpected = "true" + endif endif endif -# Check for unexpected datadog.EndpointEvent +# Check for unexpected datadog.Endpoint if "${unexpected_endpoint}" == "true" - set check_endpoint_evt = events/datadog.EndpointEvent | count() - if ${check_endpoint_evt.count} > 0 - echo " ERROR: Found unexpected datadog.EndpointEvent events (${check_endpoint_evt.count})" - set validation_failed = "true" - set has_unexpected = "true" + if "${has_dd_endpoint}" == "true" + set check_endpoint_evt = events/datadog.Endpoint | count() + if ${check_endpoint_evt.count} > 0 + echo " ERROR: Found unexpected datadog.Endpoint events (${check_endpoint_evt.count})" + set validation_failed = "true" + set has_unexpected = "true" + endif endif endif From e991442b50cd13871031bc687ac088ff0ef35196 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 17:00:44 +0200 Subject: [PATCH 7/9] fix: use jfr-shell metadata instead of stock jfr summary for event-type presence detection Stock jfr summary misparses ddprof's multi-chunk JFR output and silently reports datadog.* event types as unregistered even when real events are present, causing false VALIDATION_FAILED results in CI. --- test-validation/validate-jfr-conformance.sh | 49 ++++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/test-validation/validate-jfr-conformance.sh b/test-validation/validate-jfr-conformance.sh index 303ce8628f..741732e427 100755 --- a/test-validation/validate-jfr-conformance.sh +++ b/test-validation/validate-jfr-conformance.sh @@ -296,20 +296,38 @@ log_info "" # returning a zero count when a type isn't in the recording's metadata at all # (e.g. JDK built-in CPU/allocation events are entirely absent from # ddprof-only recordings, or an event predates the JDK that produced the -# file). Precompute presence with the JDK's own `jfr summary`, which lists -# every registered event type unconditionally, so the .jfrs script can skip -# queries for types that were never registered instead of crashing. -EVENT_SUMMARY="" -if ! EVENT_SUMMARY=$(jbang jdk exec -j 25 jfr summary "${JFR_FILE}" 2>/dev/null); then - log_warn "Could not run 'jfr summary' on ${JFR_FILE}; assuming all event types are present" - EVENT_SUMMARY="" +# file). Precompute presence with jfr-shell's own `metadata` command (the +# jafar backend), which lists every registered event type unconditionally, so +# the .jfrs script can skip queries for types that were never registered +# instead of crashing. This must use the same jafar backend as the actual +# validation query below - the stock JDK `jfr summary`/`jfr print` tools +# misparse ddprof's multi-chunk recordings and silently report datadog.* +# event types as absent even when they are present with real events. + +# jfr-shell (jafar) requires Java 25 (class file version 69.0) +# Always use --java 25 to let jbang download the correct JDK +unset JAVA_VERSION # Prevent jbang from picking up test JDK version + +log_info "Using Java 25 for jbang (required by jfr-shell)" + +# jbang resolves jafar-shell from Maven Central, which CI runners can't reach directly. +# Route it through the same internal Maven proxy Gradle uses (MAVEN_REPOSITORY_PROXY). +JBANG_REPOS_OPT="" +if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then + JBANG_REPOS_OPT="--repos=\"central=${MAVEN_REPOSITORY_PROXY}\"" +fi + +EVENT_METADATA="" +if ! EVENT_METADATA=$(eval "jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio metadata \"${JFR_FILE}\"" 2>/dev/null); then + log_warn "Could not run 'jfr-shell metadata' on ${JFR_FILE}; assuming all event types are present" + EVENT_METADATA="" fi has_event_type() { local type="$1" - if [ -z "${EVENT_SUMMARY}" ]; then + if [ -z "${EVENT_METADATA}" ]; then echo "true" - elif echo "${EVENT_SUMMARY}" | grep -qE "^[[:space:]]*${type//./\\.}[[:space:]]"; then + elif echo "${EVENT_METADATA}" | grep -qE -- "- ${type//./\\.}\$"; then echo "true" else echo "false" @@ -357,19 +375,6 @@ if [ -f /tmp/skip-jfr-validation ]; then exit 1 fi -# jfr-shell (jafar) requires Java 25 (class file version 69.0) -# Always use --java 25 to let jbang download the correct JDK -unset JAVA_VERSION # Prevent jbang from picking up test JDK version - -log_info "Using Java 25 for jbang (required by jfr-shell)" - -# jbang resolves jafar-shell from Maven Central, which CI runners can't reach directly. -# Route it through the same internal Maven proxy Gradle uses (MAVEN_REPOSITORY_PROXY). -JBANG_REPOS_OPT="" -if [ -n "${MAVEN_REPOSITORY_PROXY:-}" ]; then - JBANG_REPOS_OPT="--repos=\"central=${MAVEN_REPOSITORY_PROXY}\"" -fi - # Pass calculated thresholds directly (single source of truth - no duplicate logic in jfrs) VALIDATION_CMD="jbang --java 25 ${JBANG_REPOS_OPT} jfr-shell@btraceio script \"${VALIDATION_SCRIPT}\" \"${JFR_FILE}\" \"${PROFILE}\" \"${MIN_EXECUTION_SAMPLES}\" \"${MIN_ALLOCATION_SAMPLES}\" \"${MIN_THREAD_COUNT}\" \"${EXPECTED_CPU_EVENT}\" \"${EXPECTED_ALLOC_EVENT}\" \"${CHECK_ENDPOINT}\" \"${UNEXPECTED_JDK_EXEC}\" \"${UNEXPECTED_JDK_ALLOC}\" \"${UNEXPECTED_DD_EXEC}\" \"${UNEXPECTED_DD_ALLOC}\" \"${UNEXPECTED_ENDPOINT}\" \"${HAS_JDK_EXEC}\" \"${HAS_DD_EXEC}\" \"${HAS_JDK_ALLOC}\" \"${HAS_DD_ALLOC}\" \"${HAS_JDK_THREAD_ALLOC}\" \"${HAS_DD_ENDPOINT}\"" From 32a622939804ab5e934ab0e81a062a2055f33320 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 5 Aug 2026 17:05:43 +0200 Subject: [PATCH 8/9] fix: correct invalid top(by=sum(...)) syntax in JFR allocation-type query groupBy(...) | top(5, by=sum(weight)) isn't valid JfrPath grammar (by= expects a plain field, not a nested aggregate call) and threw a parse error on every run. Sort within groupBy via sortBy=value instead. --- test-validation/validate-jfr.jfrs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test-validation/validate-jfr.jfrs b/test-validation/validate-jfr.jfrs index d4e5b9058e..fdd48f1aa0 100755 --- a/test-validation/validate-jfr.jfrs +++ b/test-validation/validate-jfr.jfrs @@ -373,7 +373,7 @@ if "${expected_alloc}" == "jdk" # Show top allocation sites if we have any if ${jdk_alloc_count.count} > 0 echo " Top allocation types:" - show events/jdk.ObjectAllocationSample | groupBy(objectClass/name) | top(5, by=sum(weight)) + show events/jdk.ObjectAllocationSample | groupBy(objectClass/name, sortBy=value) | top(5) endif elif "${expected_alloc}" == "datadog" @@ -396,7 +396,7 @@ elif "${expected_alloc}" == "datadog" # Show top allocation sites if we have any if ${dd_alloc_count.count} > 0 echo " Top allocation types:" - show events/datadog.ObjectSample | groupBy(objectClass/name) | top(5, by=sum(weight)) + show events/datadog.ObjectSample | groupBy(objectClass/name, sortBy=value) | top(5) endif else @@ -415,7 +415,7 @@ else # Show top allocation sites if we have any if ${jdk_alloc_count.count} > 0 echo " Top allocation types:" - show events/jdk.ObjectAllocationSample | groupBy(objectClass/name) | top(5, by=sum(weight)) + show events/jdk.ObjectAllocationSample | groupBy(objectClass/name, sortBy=value) | top(5) endif else echo " Total: ${dd_alloc_count.count} allocation events (using datadog.ObjectSample)" @@ -431,7 +431,7 @@ else # Show top allocation sites if we have any if ${dd_alloc_count.count} > 0 echo " Top allocation types:" - show events/datadog.ObjectSample | groupBy(objectClass/name) | top(5, by=sum(weight)) + show events/datadog.ObjectSample | groupBy(objectClass/name, sortBy=value) | top(5) endif endif endif From da00902c07d9e903ed7cef23c2a8c2b9a1b479c6 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Thu, 6 Aug 2026 10:26:25 +0200 Subject: [PATCH 9/9] fix: don't let PR-comment transport failures fail the integration-test pipeline set -e was aborting post-pr-comment.sh before the real test-outcome exit code was reached whenever the GitHub API call failed, masking passing test runs. Co-Authored-By: Claude Sonnet 5 --- .gitlab/dd-trace-integration/post-pr-comment.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab/dd-trace-integration/post-pr-comment.sh b/.gitlab/dd-trace-integration/post-pr-comment.sh index 4dd812022f..2f76b18138 100755 --- a/.gitlab/dd-trace-integration/post-pr-comment.sh +++ b/.gitlab/dd-trace-integration/post-pr-comment.sh @@ -181,8 +181,10 @@ fi BODY_FILE=$(mktemp) trap 'rm -f "${BODY_FILE}"' EXIT echo "${COMMENT_BODY}" > "${BODY_FILE}" -"${HERE}/../scripts/upsert-github-pr-comment.sh" \ - "dd-trace-integration-results" "${DDPROF_COMMIT_BRANCH:-}" "${BODY_FILE}" +if ! "${HERE}/../scripts/upsert-github-pr-comment.sh" \ + "dd-trace-integration-results" "${DDPROF_COMMIT_BRANCH:-}" "${BODY_FILE}"; then + log_error "Failed to post PR comment (transport failure) — continuing; only the test outcome below determines pipeline status" +fi # Exit with failure if tests failed (makes pipeline fail) if [ "${OVERALL_STATUS}" = "failure" ]; then