Skip to content

Support instrumentations that make structural changes and tolerate already-loaded target classes - #12610

Open
mcculls wants to merge 14 commits into
masterfrom
mcculls/preserve-structure-of-loaded-types
Open

mcculls wants to merge 14 commits into
masterfrom
mcculls/preserve-structure-of-loaded-types

Conversation

@mcculls

@mcculls mcculls commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Introduces Instrumenter.WithStructuralChange, a marker interface for instrumentations whose type advice adds fields, methods, or interfaces to a target type. Each such instrumentation declares a structuralChangeMarker() interface that gets woven into the type when the structural change is applied.
  • Adds MatchRecorder.PreserveLoadedStructure, which narrows a match so an already-loaded class is only retransformed if it already declares the structural-change marker — avoiding VerifyError/redefinition failures from applying structural bytecode changes to classes the JVM has already loaded, while still allowing retransformation of classes that were already structurally changed pre-load.
  • Adds MatchRecorder.CopyMatch to reuse an already-computed match under another id instead of re-running the type matcher.
  • Migrates existing structural-change instrumentations (IAST Taintable visitors, TaskUnwrappingInstrumentation, Vert.x RequestImplInstrumentation) onto the new WithStructuralChange pattern, removing the now-redundant TaintableRedefinitionStrategyListener, MrReturnAdvice, and LoadedTaintableClass.
  • Standardizes how new interfaces are added via advice across TaintableVisitor, UnwrappingVisitor, and RequestImplInstrumentation, extracting a shared CollectionUtils.arrayContains helper.
  • Fixes a TaintedMap bug where the last element wasn't deduplicated
  • Removes the ENABLE_ADVICE_TRANSFORMER test workaround, no longer needed now that structural-change retransformation is handled properly.
  • Adds PreserveLoadedStructureRetransformationForkedTest covering: structural changes surviving retransformation, and an already-loaded (not structurally changed) class correctly not being retransformed.

Test plan

  • New forked test: PreserveLoadedStructureRetransformationForkedTest
  • Existing IAST, Vert.x, and Kafka instrumentation tests updated/passing
  • CollectionUtilsTest covers the new arrayContains utility

🤖 Generated with Claude Code

@mcculls mcculls added tag: ai generated Largely based on code generated by an AI or LLM comp: tooling Build & Tooling type: feature Enhancements and improvements labels Sep 23, 2026
@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

🎯 Code Coverage (details)
• Patch Coverage: 4.48%
• Overall Coverage: 59.26% (-0.04%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: e0fe52c | Docs | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.83 s 14.72 s [-0.2%; +1.6%] (no difference)
startup:insecure-bank:tracing:Agent 13.58 s 13.70 s [-1.6%; -0.1%] (maybe better)
startup:petclinic:appsec:Agent 17.65 s 17.52 s [+0.0%; +1.4%] (maybe worse)
startup:petclinic:iast:Agent 17.50 s 17.06 s [-1.7%; +6.9%] (no difference)
startup:petclinic:profiling:Agent 17.40 s 16.93 s [-1.9%; +7.4%] (no difference)
startup:petclinic:sca:Agent 17.62 s 17.33 s [+0.6%; +2.8%] (maybe worse)
startup:petclinic:tracing:Agent 16.61 s 16.27 s [-2.2%; +6.3%] (no difference)

Commit: e0fe52c0 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@pr-commenter

pr-commenter Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Kafka / producer-benchmark

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
git_branch master mcculls/preserve-structure-of-loaded-types
git_commit_date 1790160832 1790268112
git_commit_sha 3723256 e0fe52c
See matching parameters
Baseline Candidate
ci_job_date 1790269383 1790269383
ci_job_id 2077541474 2077541474
ci_pipeline_id 139899754 139899754
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
jdkVersion 11.0.31 11.0.31
jmhVersion 1.36 1.36
jvm /usr/lib/jvm/java-11-openjdk-amd64/bin/java /usr/lib/jvm/java-11-openjdk-amd64/bin/java
jvmArgs -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=15002 -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=15002 -Dhttp.nonProxyHosts=localhost *.localhost
kernel_version Linux runner-zfyrx7zua-project-304-concurrent-0-7k5yyj0n 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux Linux runner-zfyrx7zua-project-304-concurrent-0-7k5yyj0n 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
vmName OpenJDK 64-Bit Server VM OpenJDK 64-Bit Server VM
vmVersion 11.0.31+11-post-1ubuntu1-22.04.2-Ubuntu 11.0.31+11-post-1ubuntu1-22.04.2-Ubuntu

Summary

Found 0 performance improvements and 0 performance regressions! Performance is the same for 3 metrics, 0 unstable metrics.

See unchanged results
scenario Δ mean throughput
scenario:not-instrumented/KafkaProduceBenchmark.benchProduce unsure
[-54923.340op/s; -10707.901op/s] or [-3.007%; -0.586%]
scenario:only-tracing-dsm-disabled-benchmarks/KafkaProduceBenchmark.benchProduce same
scenario:only-tracing-dsm-enabled-benchmarks/KafkaProduceBenchmark.benchProduce same

@pr-commenter

pr-commenter Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Kafka / consumer-benchmark

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
git_branch master mcculls/preserve-structure-of-loaded-types
git_commit_date 1790160832 1790268112
git_commit_sha 3723256 e0fe52c
See matching parameters
Baseline Candidate
ci_job_date 1790269442 1790269442
ci_job_id 2077541477 2077541477
ci_pipeline_id 139899754 139899754
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
jdkVersion 11.0.31 11.0.31
jmhVersion 1.36 1.36
jvm /usr/lib/jvm/java-11-openjdk-amd64/bin/java /usr/lib/jvm/java-11-openjdk-amd64/bin/java
jvmArgs -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=15002 -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=15002 -Dhttp.nonProxyHosts=localhost *.localhost
kernel_version Linux runner-zfyrx7zua-project-304-concurrent-0-fif2tehu 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux Linux runner-zfyrx7zua-project-304-concurrent-0-fif2tehu 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
vmName OpenJDK 64-Bit Server VM OpenJDK 64-Bit Server VM
vmVersion 11.0.31+11-post-1ubuntu1-22.04.2-Ubuntu 11.0.31+11-post-1ubuntu1-22.04.2-Ubuntu

Summary

Found 1 performance improvements and 0 performance regressions! Performance is the same for 2 metrics, 0 unstable metrics.

scenario Δ mean throughput
scenario:only-tracing-dsm-disabled-benchmarks/KafkaConsumerBenchmark.benchConsume better
[+5226.738op/s; +17535.485op/s] or [+1.739%; +5.834%]
See unchanged results
scenario Δ mean throughput
scenario:not-instrumented/KafkaConsumerBenchmark.benchConsume same
scenario:only-tracing-dsm-enabled-benchmarks/KafkaConsumerBenchmark.benchConsume same

@mcculls
mcculls marked this pull request as ready for review September 23, 2026 14:26
@mcculls
mcculls requested review from a team as code owners September 23, 2026 14:26
@mcculls
mcculls requested review from amarziali, claponcet, dromanol and ygree and removed request for a team September 23, 2026 14:26

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 058842ae68

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread internal-api/src/main/java/datadog/trace/util/CollectionUtils.java

@datadog-prod-us1-3 datadog-prod-us1-3 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Autotest was unable to complete this review. View session

Please try again by commenting @autotest review.

@amarziali amarziali left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks clean to me. Thanks for the improvement and for having updated the docs

if (ENABLE_ADVICE_TRANSFORMER) {
transformer.applyAdvice(new TaintableVisitor(instrumentedType()));
}
transformer.applyAdvice(new TaintableVisitor(instrumentedType()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simplification of not needing the if.
I wonder if we could do the same with TaintableVisitor, too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, could you elaborate on the second point? The ENABLE_ADVICE_TRANSFORMER flag was a workaround for testing because the old approach interacted badly with the tests. The new approach doesn't need such a flag.

But TaintableVisitor is still required because that's how the Taintable interface gets applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just saying that TaintableVisitor seems to be a constant throughout the IAST code, so maybe, we could figure out a way to add it implicitly rather than explicitly. But it isn't in enough places that I feel strongly about it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I see - yes there is scope for further cleanup/consolidation there

int structuralTransformationId = allocateRuntimeTransformationId();
matchers.add(new MatchRecorder.CopyMatch(transformationId, structuralTransformationId));
addStructuralNarrowing(structuralChange, structuralTransformationId);
structuralChange.typeAdvice(this);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an instrumentation implements both WithStructuralChange and HasMethodAdvice and gets split into two transformations here, the split-out structural-change transformation never receives the shared helperTransformer/contextRequestRewriter — they're added to advice earlier in buildTypeAdvice, but finishAdviceStack(transformationId) clears advice before structuralChange.typeAdvice(this) runs here.

None of the three instrumentations migrated in this PR trip this (their typeAdvice() only applies a raw AsmVisitorWrapper), so it's not a regression from this change specifically, but it's a latent gap in the new split path: any future dual-interface instrumentation whose type-level advice relies on injected helper classes or context-store rewriting would silently lose that support on the structural-change transformation, likely surfacing as a hard-to-trace NoClassDefFoundError/NoSuchFieldError at instrumentation time. Might be worth re-adding helperTransformer/contextRequestRewriter to advice before this call, or documenting that typeAdvice() on the structural-change path can't rely on them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type advice cannot rely on helpers or context rewriting because it acts at a different level - those concepts are specific to method advice.

I'll add a small note to make this clear.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documented in e0fe52c

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was Claude's analysis. I wasn't sure myself, so I passed it along.

@rkaneriya rkaneriya left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No DSM-specific objection.

@ygree ygree left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I was missing the motivation part, which I’d summarize as follows:

Make structural instrumentation safe, regardless of the class-loading order. Apply additions on initial load. Preserve them during retransformation. Skip them for classes loaded without them. This prevents JVM retransformation failures while keeping method advice active and avoiding the global disabling of IAST structural instrumentation.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: tooling Build & Tooling tag: ai generated Largely based on code generated by an AI or LLM type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants