Skip to content

chore: add FFM coverage map benchmarks - #1070

Merged
kyakdan merged 4 commits into
mainfrom
ffm-coverage-map-benchmark
Nov 9, 2026
Merged

kyakdan merged 4 commits into
mainfrom
ffm-coverage-map-benchmark

Conversation

@kyakdan

@kyakdan kyakdan commented Oct 14, 2026

Copy link
Copy Markdown
Member

Refs #1069

sun.misc.Unsafe memory access is deprecated for removal (JEP 471/498) and warns on JDK 24+. CoverageMap.recordCoverage runs on every instrumented edge, so this PR adds FFM-backed coverage maps to CoverageInstrumentationBenchmark to measure what replacing Unsafe would cost:

  • FfmCoverageMap uses MemorySegment directly (Java 22 source).
  • FfmMethodHandleCoverageMap uses the same API via method handles. It compiles with Java 8 source level, so it could ship in the same jar as the Unsafe-backed map and only be loaded on JDK 22+.

Both use the same update rule, instrumentation strategy and map size as the existing Unsafe benchmarks. Since FFM requires JDK 22+, the FFM benchmarks are only built with --java_runtime_version=remotejdk_25, the only JDK >= 22 that rules_java provides. CI runs on JDK 8 and 21, so it doesn't run them.

Results

JDK 25.0.2, macOS arm64, JMH defaults (5 forks, 5 × 10 s warmup and measurement):

Benchmark ops/s vs. Unsafe_NeverZero2
uninstrumented 9.03M ± 0.58M
staticMethod_Unsafe_NeverZero2 (what Jazzer uses today) 3.73M ± 0.04M baseline
staticMethod_Ffm_NeverZero 2.93M ± 0.01M −21.5%
staticMethod_FfmMethodHandle_NeverZero 2.87M ± 0.04M −23.2%
staticMethod_DirectByteBuffer2_NeverZero 2.46M ± 0.63M −34%

FFM costs about 21% throughput on this benchmark, which only measures the instrumented target, not libFuzzer's loop. The method-handle variant is within about 2% of direct FFM, so a single release could use Unsafe where it's still allowed and fall back to FFM otherwise.

Running

bazel run --java_runtime_version=remotejdk_25 \
  //src/jmh/java/com/code_intelligence/jazzer/instrumentor:CoverageInstrumentationBenchmark

sun.misc.Unsafe memory access is deprecated for removal (JEP 471/498)
and warns on JDK 24+. Add coverage maps backed by the Foreign Function
& Memory API to CoverageInstrumentationBenchmark to compare them with
the Unsafe-backed map Jazzer uses today:

- FfmCoverageMap uses MemorySegment directly (Java 22 source).
- FfmMethodHandleCoverageMap accesses the same API via method handles,
  so it compiles with Java 8 source level and could ship in the same
  jar as the Unsafe-backed map.

Since FFM requires JDK 22+, the FFM benchmarks are only built with
--java_runtime_version=remotejdk_25.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc8e7077-31cc-47a3-acb9-050440f42ea5

@oetr oetr 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!

GitHub retired the macos-14 runner image on 2026-11-02. Use Xcode 16.4, the default on macos-15.
The JVM reconstitutes retransformed classes with their methods in an order that varies between runs. Since coverage and hook IDs are assigned in method order, fuzzing with a fixed seed wasn't reproducible, e.g. ReactorFuzzTest timed out on macOS CI with JDK 8. Sort the methods first.
On macOS, libraries built with -fsanitize=fuzzer-no-link load the UBSan runtime, which then turns the JVM's own SIGSEGV/SIGBUS into crashes, e.g. in NativeValueProfileFuzzer.
@kyakdan
kyakdan merged commit bdebf95 into main Nov 9, 2026
0 of 9 checks passed
@kyakdan
kyakdan deleted the ffm-coverage-map-benchmark branch November 9, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants