Skip to content

Return empty method lines instead of throwing when class bytecode resource is missing - #12380

Open
dougqh wants to merge 1 commit into
masterfrom
dougqh/fix-ci-vis-null-classstream-npe
Open

Return empty method lines instead of throwing when class bytecode resource is missing#12380
dougqh wants to merge 1 commit into
masterfrom
dougqh/fix-ci-vis-null-classstream-npe

Conversation

@dougqh

@dougqh dougqh commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Guards ByteCodeLinesResolver.ClassMethodLines.parse against a null bytecode stream: when Utils.getClassStream(clazz) can't locate a class's .class resource (documented @Nullable), it now returns an empty ClassMethodLines and logs at debug, instead of letting ClassReader throw an IOException that gets wrapped, logged at ERROR, and swallowed.

Also migrates ByteCodeLinesResolverTest from Groovy/Spock to JUnit 5/Java (project convention for new/touched unit tests) and adds a regression test (NullResourceClassLoader) exercising the null-stream path directly.

Motivation

Investigated a recurring, non-crashing Datadog error-tracking issue for CI Visibility (TestNG) test runs: Could not determine method borders for {} logged at ERROR, observed across tracer versions from 1.48.1 through 1.65.1, recurring every few months whenever a test suite exercises a class whose bytecode resource isn't resolvable this way (e.g. a Mockito-generated proxy class). No functional impact (the exception was already caught and empty lines returned), but it's noisy in production logs/error tracking and keeps re-triggering an alert.

Additional Notes

  • Fix mirrors the existing handling for the identical condition in the sibling method Utils.getFileName, which already checks for a null stream and logs at debug.
  • Verified with ./gradlew :dd-java-agent:agent-ci-visibility:test (full module) and spotlessCheck.

Contributor Checklist

  • Format the title according to the contribution guidelines
  • Assign the type: and (comp: or inst:) labels in addition to any other useful labels
  • Avoid using close, fix, or any linking keywords when referencing an issue
  • Update the CODEOWNERS file on source file addition, migration, or deletion — not needed, files stay under the existing agent-ci-visibility ownership
  • Update public documentation with any new configuration flags or behaviors — not applicable, no config/behavior surface change
  • Once approved, use merge queue to merge the PR

Jira ticket: none (found via Datadog error tracking, not a filed ticket)

…ource is missing

Utils.getClassStream() is documented @nullable and can return null for
classes whose .class resource can't be located on the classpath (e.g.
Mockito-generated proxy classes). ByteCodeLinesResolver.ClassMethodLines.parse
passed that possibly-null stream straight into ClassReader, which threw an
IOException that got wrapped and logged at ERROR, then swallowed. This has
been recurring in error tracking for CI Visibility test runs across many
tracer versions. Short-circuit on a null stream and log at debug instead,
matching the existing handling in Utils.getFileName for the same condition.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh dougqh added tag: ai generated Largely based on code generated by an AI or LLM comp: ci visibility Continuous Integration Visibility type: bug fix Bug fix labels Sep 2, 2026
@dougqh
dougqh marked this pull request as ready for review September 2, 2026 10:56
@dougqh
dougqh requested a review from a team as a code owner September 2, 2026 10:56
@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - sbt-scalatest

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent -25.91 55.43 $\color{green}{\blacktriangledown}$ -81.34 55.43 $\color{green}{\blacktriangledown}$ -81.34 49/199
agentEvpProxy -26.17 n/a n/a n/a n/a -

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@datadog-prod-us1-5 datadog-prod-us1-5 Bot 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.

Datadog Autotest: PASS

More details

The null guard returns empty line data when the class resource is absent. The Java test migration keeps the prior cases and adds the missing-resource case.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Datadog Autotest · Commit c34874c · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

@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: c34874c328

ℹ️ 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 on lines +69 to +71
if (classStream == null) {
log.debug("Could not get input stream for class {}", clazz.getName());
return classMethodLines;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid caching a resource lookup failure

When a custom class loader temporarily returns null but can provide the resource later, returning this non-null empty value from ClassMethodLines.parse causes methodLinesCache.computeIfAbsent to cache the failed lookup. Subsequent calls that hit the entry therefore never retry and continue returning missing method borders, unlike the existing exception path explicitly marked “do not cache failure”; handle the null case without storing a negative result while still suppressing the error-level log.

Useful? React with 👍 / 👎.

Comment on lines +89 to +92
ByteCodeLinesResolver linesResolver = new ByteCodeLinesResolver();
Lines methodLines = linesResolver.getMethodLines(unresolvableMethod);

assertFalse(methodLines.isValid());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the regression test distinguish the new behavior

This assertion also passes against the previous implementation: ClassReader throws for the null stream, but getMethodLines catches that exception and already returns Lines.EMPTY. Consequently, removing the new guard and restoring the production ERROR log would leave this regression test green; call ClassMethodLines.parse directly and assert it does not throw, or capture the logger and verify the error-level event is absent.

Useful? React with 👍 / 👎.

@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 62.75% (+3.72%)

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

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - nebula-release-plugin

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 38.44 37.15 $\color{red}{\blacktriangle}$ +1.29 37.15 $\color{red}{\blacktriangle}$ +1.29 25/101
agentless 37.04 35.70 $\color{red}{\blacktriangle}$ +1.34 35.70 $\color{red}{\blacktriangle}$ +1.34 25/102
agentlessCodeCoverage 45.60 44.48 $\color{red}{\blacktriangle}$ +1.12 44.48 $\color{red}{\blacktriangle}$ +1.12 25/101
agentlessLineCoverage 56.23 55.43 $\color{red}{\blacktriangle}$ +0.80 55.43 $\color{red}{\blacktriangle}$ +0.80 25/104

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - netflix-zuul

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 87.58 87.80 $\color{green}{\blacktriangledown}$ -0.22 87.80 $\color{green}{\blacktriangledown}$ -0.22 25/107
agentless 81.09 81.05 $\color{red}{\blacktriangle}$ +0.04 81.05 $\color{red}{\blacktriangle}$ +0.04 25/107
agentlessCodeCoverage 94.88 95.12 $\color{green}{\blacktriangledown}$ -0.24 95.12 $\color{green}{\blacktriangledown}$ -0.24 25/106
agentlessLineCoverage 111.83 111.62 $\color{red}{\blacktriangle}$ +0.21 111.62 $\color{red}{\blacktriangle}$ +0.21 24/105

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - reactive-streams-jvm

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 22.25 22.09 $\color{red}{\blacktriangle}$ +0.16 21.65 $\color{red}{\blacktriangle}$ +0.60 27/108
agentless 20.43 19.20 $\color{red}{\blacktriangle}$ +1.23 18.82 $\color{red}{\blacktriangle}$ +1.61 26/101
agentlessCodeCoverage 20.85 20.39 $\color{red}{\blacktriangle}$ +0.46 19.99 $\color{red}{\blacktriangle}$ +0.86 25/99
agentlessLineCoverage 27.95 26.98 $\color{red}{\blacktriangle}$ +0.97 26.98 $\color{red}{\blacktriangle}$ +0.97 8/72

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - heliboard

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 10.45 10.13 $\color{red}{\blacktriangle}$ +0.32 9.92 $\color{red}{\blacktriangle}$ +0.53 23/96

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@dd-octo-sts

dd-octo-sts Bot commented Sep 2, 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.01 s 13.96 s [-0.3%; +1.1%] (no difference)
startup:insecure-bank:tracing:Agent 12.98 s 12.98 s [-0.8%; +0.9%] (no difference)
startup:petclinic:appsec:Agent 17.15 s 16.90 s [+0.6%; +2.4%] (maybe worse)
startup:petclinic:iast:Agent 17.02 s 17.08 s [-1.3%; +0.6%] (no difference)
startup:petclinic:profiling:Agent 16.83 s 16.90 s [-1.7%; +0.9%] (no difference)
startup:petclinic:sca:Agent 16.98 s 16.71 s [+0.6%; +2.5%] (maybe worse)
startup:petclinic:tracing:Agent 16.14 s 16.10 s [-1.0%; +1.4%] (no difference)

Commit: c34874c3 · 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.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - sonar-kotlin

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 12.32 13.13 $\color{green}{\blacktriangledown}$ -0.81 12.87 $\color{green}{\blacktriangledown}$ -0.55 25/97
agentless 12.53 12.12 $\color{red}{\blacktriangle}$ +0.41 11.88 $\color{red}{\blacktriangle}$ +0.65 25/96
agentlessCodeCoverage 14.64 15.41 $\color{green}{\blacktriangledown}$ -0.77 15.11 $\color{green}{\blacktriangledown}$ -0.47 25/96
agentlessLineCoverage 17.79 17.73 $\color{red}{\blacktriangle}$ +0.06 17.03 $\color{red}{\blacktriangle}$ +0.76 25/96

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - jolokia

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 92.73 95.12 $\color{green}{\blacktriangledown}$ -2.39 93.23 $\color{green}{\blacktriangledown}$ -0.50 26/107
agentless 88.41 91.39 $\color{green}{\blacktriangledown}$ -2.98 89.58 $\color{green}{\blacktriangledown}$ -1.17 25/105
agentlessCodeCoverage 96.84 99.00 $\color{green}{\blacktriangledown}$ -2.16 99.00 $\color{green}{\blacktriangledown}$ -2.16 25/104
agentlessLineCoverage 98.90 101.00 $\color{green}{\blacktriangledown}$ -2.10 99.00 $\color{green}{\blacktriangledown}$ -0.10 26/104

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - okhttp

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 19.90 19.59 $\color{red}{\blacktriangle}$ +0.31 19.59 $\color{red}{\blacktriangle}$ +0.31 25/105
agentless 19.85 19.20 $\color{red}{\blacktriangle}$ +0.65 19.20 $\color{red}{\blacktriangle}$ +0.65 25/100
agentlessCodeCoverage 25.22 22.54 $\color{red}{\blacktriangle}$ +2.68 22.54 $\color{red}{\blacktriangle}$ +2.68 26/102
agentlessLineCoverage 40.04 38.67 $\color{red}{\blacktriangle}$ +1.37 38.67 $\color{red}{\blacktriangle}$ +1.37 26/102

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - spring_boot

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 15.96 16.36 $\color{green}{\blacktriangledown}$ -0.40 16.36 $\color{green}{\blacktriangledown}$ -0.40 24/100
agentless 9.21 9.92 $\color{green}{\blacktriangledown}$ -0.71 9.92 $\color{green}{\blacktriangledown}$ -0.71 24/99
agentlessCodeCoverage 13.03 13.40 $\color{green}{\blacktriangledown}$ -0.37 13.40 $\color{green}{\blacktriangledown}$ -0.37 24/99
agentlessLineCoverage 21.62 22.09 $\color{green}{\blacktriangledown}$ -0.47 22.09 $\color{green}{\blacktriangledown}$ -0.47 23/97

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Test Environment - sonar-java

Job Status: 🟢 success

Scenario This PR (%) 7d median Δ 7d 30d median Δ 30d runs (7d/30d)
agent 12.64 16.36 $\color{green}{\blacktriangledown}$ -3.72 11.88 $\color{red}{\blacktriangle}$ +0.76 26/104
agentless 46.79 12.12 $\color{red}{\blacktriangle}$ +34.67 11.42 $\color{red}{\blacktriangle}$ +35.37 26/103
agentlessCodeCoverage 125.33 74.82 $\color{red}{\blacktriangle}$ +50.51 77.88 $\color{red}{\blacktriangle}$ +47.45 26/103
agentlessLineCoverage 118.86 116.18 $\color{red}{\blacktriangle}$ +2.68 118.53 $\color{red}{\blacktriangle}$ +0.33 25/103

Baseline: median of @test.tracer_overhead on main (gitlab) over the last 7/30 days, per OSS project & scenario. Δ = this PR − baseline median; red ▲ = more overhead, green ▽ = less overhead than baseline.

Comment on lines +7 to +14
/**
* A {@link ClassLoader} that defines classes from an in-memory map and returns {@code null} from
* resource lookups (rather than throwing), used to exercise {@code ByteCodeLinesResolver} when a
* class's bytecode resource cannot be located.
*
* <p>Kept in Java rather than the Java 8 test suite's Groovy counterpart on purpose: see {@link
* MisbehavingClassLoader} for why.
*/

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.

nit:

Suggested change
/**
* A {@link ClassLoader} that defines classes from an in-memory map and returns {@code null} from
* resource lookups (rather than throwing), used to exercise {@code ByteCodeLinesResolver} when a
* class's bytecode resource cannot be located.
*
* <p>Kept in Java rather than the Java 8 test suite's Groovy counterpart on purpose: see {@link
* MisbehavingClassLoader} for why.
*/
/**
* A {@link ClassLoader} that defines classes from an in-memory map and returns {@code null} from
* resource lookups (rather than throwing), used to exercise {@code ByteCodeLinesResolver} when a
* class's bytecode resource cannot be located.
*/

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

Labels

comp: ci visibility Continuous Integration Visibility tag: ai generated Largely based on code generated by an AI or LLM type: bug fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants