Nikita.tkachenko/runtime code coverage by nikita-tkachenko-datadog · Pull Request #10951 · DataDog/dd-trace-java

added 3 commits

March 23, 2026 14:47
Implement Coverage Binary Protocol v1 (CoverageBinaryEncoder) using
two bit vectors per record for executable and covered lines. Switch
from LCOV text format to the new "ddcov" binary format for coverage
uploads. Add className to ClassProbeMapping, key coverage data by
CoverageKey (sourceFile + className), and include language/env tags
in upload metadata.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@nikita-tkachenko-datadog

`new int[probeCount][]` creates an array of null references.
When the collector iterates `probeToLines[p]`, it NPEs on classes
that have probes but no executable lines (no debug info, interfaces).
Initialize each entry to `new int[0]` instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Skip classes without a SourceFile attribute in the collector to
  prevent NPE in the binary encoder (null is not a valid string
  in the coverage binary protocol)
- Log instrumentation failures at debug level so they are visible
  when troubleshooting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ClassProbeMappingBuilder previously ran JaCoCo's Analyzer N+1 times
per class (once per probe + once for executable lines). For a class
with 200 probes, that meant 201 full ASM parses of the same bytecode.

The new implementation parses the class once using ClassProbesAdapter,
builds a simplified instruction graph (ProbeNode with predecessor
links), and walks predecessor chains to determine which lines each
probe covers. ~200x faster for large classes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… scan

On cache miss, try ClassLoader.getResourceAsStream() first (O(1) per
class) before scanning all classpath jars/directories. Uses the system
classloader (application classpath) first, then context classloader.
CRC64 is verified after reading to ensure bytes match what was
instrumented. Falls back to full classpath scan for any classes the
classloader can't resolve.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@nikita-tkachenko-datadog

Replace custom ScheduledExecutorService with a dedicated
AgentTaskScheduler(CODE_COVERAGE) instance, following the same
pattern as Profiler, Debugger, Remote Config, and Tracer Flare.

This gives us proper daemon thread in the dd-trace-java thread group,
null context classloader, uncaught exception handler, and shutdown
hook handling for free.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>