From f133367e164f802e4a7b1251228fd636b4745f41 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 17 Apr 2026 16:07:09 +0200 Subject: [PATCH 1/3] build(jmh-fork): exclude native C sources from license plugin --- jmh-fork/jmh-core/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/jmh-fork/jmh-core/pom.xml b/jmh-fork/jmh-core/pom.xml index 847f927..96f3f90 100644 --- a/jmh-fork/jmh-core/pom.xml +++ b/jmh-fork/jmh-core/pom.xml @@ -119,6 +119,7 @@ questions. src/main/java/io/codspeed/** src/test/java/io/codspeed/** + native-*/** From 47b11fee49a7c05897d084398fbc1ee1e03b9286 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 17 Apr 2026 16:26:55 +0200 Subject: [PATCH 2/3] feat(jmh-fork): emit benchmark markers around measurement iterations, skip warmup forks --- .../org/openjdk/jmh/runner/BaseRunner.java | 21 +++++++++++++++++++ .../java/org/openjdk/jmh/runner/Runner.java | 21 ++++++++++++++----- .../org/openjdk/jmh/runner/RunnerTest.java | 6 +++--- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java b/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java index 03adf5b..c1092a8 100644 --- a/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java +++ b/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java @@ -50,6 +50,17 @@ */ abstract class BaseRunner { + /** + * System property the parent Runner sets on forked JVMs whose iterations are warmup-only + * (results discarded). The forked process reads it to suppress CodSpeed marker emission so the + * runner only sees real measurement regions. + */ + static final String CODSPEED_WARMUP_FORK_PROP = "io.codspeed.warmupFork"; + + static boolean isWarmupFork() { + return "true".equals(System.getProperty(CODSPEED_WARMUP_FORK_PROP)); + } + private long projectedTotalTime; private long projectedRunningTime; private long actualRunningTime; @@ -291,7 +302,17 @@ protected void runBenchmark(BenchmarkParams benchParams, BenchmarkHandler handle boolean isFirstIteration = (benchParams.getWarmup().getCount() == 0) && (i == 1); boolean isLastIteration = (i == mp.getCount()); + + long startTs = InstrumentHooks.currentTimestamp(); IterationResult ir = handler.runIteration(benchParams, mp, isFirstIteration, isLastIteration); + long endTs = InstrumentHooks.currentTimestamp(); + + if (!isWarmupFork()) { + InstrumentHooks hooks = InstrumentHooks.getInstance(); + int pid = (int) ProcessHandle.current().pid(); + hooks.addMarker(pid, InstrumentHooks.MARKER_TYPE_BENCHMARK_START, startTs); + hooks.addMarker(pid, InstrumentHooks.MARKER_TYPE_BENCHMARK_END, endTs); + } out.iterationResult(benchParams, mp, i, ir); allMeasurement += ir.getMetadata().getAllOps(); diff --git a/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java b/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java index a4339cf..abd926f 100644 --- a/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java +++ b/jmh-fork/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java @@ -681,7 +681,7 @@ private Multimap runSeparate(ActionPlan action for (int i = 0; i < totalForks; i++) { boolean warmupFork = (i < warmupForkCount); - List forkedString = getForkedMainCommand(params, profilers, server.getHost(), server.getPort()); + List forkedString = getForkedMainCommand(params, profilers, server.getHost(), server.getPort(), warmupFork); etaBeforeBenchmark(); @@ -844,11 +844,18 @@ private List doFork(BinaryLinkServer reader, List comma } /** - * @param host host VM host - * @param port host VM port - * @return + * Build the command line used to launch a forked benchmark JVM. + * + * @param benchmark benchmark parameters for the current run + * @param profilers external profilers contributing extra JVM args + * @param host host VM host + * @param port host VM port + * @param warmupFork true for warmup-only forks whose results are discarded; causes + * {@code -D}{@value BaseRunner#CODSPEED_WARMUP_FORK_PROP}{@code =true} + * to be added so the forked JVM suppresses CodSpeed marker emission + * @return the full argv, starting with the JVM executable */ - List getForkedMainCommand(BenchmarkParams benchmark, List profilers, String host, int port) { + List getForkedMainCommand(BenchmarkParams benchmark, List profilers, String host, int port, boolean warmupFork) { // Poll profilers for options List javaInvokeOptions = new ArrayList<>(); List javaOptions = new ArrayList<>(); @@ -876,6 +883,10 @@ List getForkedMainCommand(BenchmarkParams benchmark, ListemptyList(), System.getProperty("java.version"), System.getProperty("java.vm.name"), System.getProperty("java.vm.version"), Version.getPlainVersion(), TimeValue.days(1)); - List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT); + List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT, false); // expecting 1 compile command file List files = CompilerHints.getCompileCommandFiles(command); @@ -100,7 +100,7 @@ public void testOptsWithCompileCommandFileResultInMergedCompileCommandFile() thr Utils.getCurrentJvm(), Collections.singletonList(CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints), System.getProperty("java.version"), System.getProperty("java.vm.name"), System.getProperty("java.vm.version"), Version.getPlainVersion(), TimeValue.days(1)); - List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT); + List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT, false); // expecting 1 compile command file List files = CompilerHints.getCompileCommandFiles(command); @@ -137,7 +137,7 @@ public void testOptsWith2CompileCommandFilesResultInMergedCompileCommandFile() t Arrays.asList(CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints1, CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints2), System.getProperty("java.version"), System.getProperty("java.vm.name"), System.getProperty("java.vm.version"), Version.getPlainVersion(), TimeValue.days(1)); - List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT); + List command = blade.getForkedMainCommand(bp, Collections.emptyList(), DUMMY_HOST, DUMMY_PORT, false); // expecting 1 compile command file List files = CompilerHints.getCompileCommandFiles(command); From ff83f3b5487062805ee0588147552ef6e5388940 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 17 Apr 2026 16:38:49 +0200 Subject: [PATCH 3/3] fix(jmh-fork): make InstrumentHooks.currentTimestamp safe when native lib is unavailable --- .../src/main/c/instrument_hooks_jni.c | 2 +- .../java/io/codspeed/instrument_hooks/InstrumentHooks.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/jmh-fork/jmh-core/native-instrument-hooks/src/main/c/instrument_hooks_jni.c b/jmh-fork/jmh-core/native-instrument-hooks/src/main/c/instrument_hooks_jni.c index ffb5015..11a6499 100644 --- a/jmh-fork/jmh-core/native-instrument-hooks/src/main/c/instrument_hooks_jni.c +++ b/jmh-fork/jmh-core/native-instrument-hooks/src/main/c/instrument_hooks_jni.c @@ -16,7 +16,7 @@ static void throw_runtime_exception(JNIEnv *env, const char *msg) { // --- Static methods --- JNIEXPORT jlong JNICALL -Java_io_codspeed_instrument_1hooks_InstrumentHooks_currentTimestamp( +Java_io_codspeed_instrument_1hooks_InstrumentHooks_nativeCurrentTimestamp( JNIEnv *env, jclass cls) { (void)env; (void)cls; diff --git a/jmh-fork/jmh-core/src/main/java/io/codspeed/instrument_hooks/InstrumentHooks.java b/jmh-fork/jmh-core/src/main/java/io/codspeed/instrument_hooks/InstrumentHooks.java index 26ce843..50ef867 100644 --- a/jmh-fork/jmh-core/src/main/java/io/codspeed/instrument_hooks/InstrumentHooks.java +++ b/jmh-fork/jmh-core/src/main/java/io/codspeed/instrument_hooks/InstrumentHooks.java @@ -63,7 +63,11 @@ public static InstrumentHooks getInstance() { public static final int FEATURE_DISABLE_CALLGRIND_MARKERS = 0; // Static methods (no instance needed) - public static native long currentTimestamp(); + public static long currentTimestamp() { + return nativeAvailable ? nativeCurrentTimestamp() : 0L; + } + + private static native long nativeCurrentTimestamp(); public static native void setFeature(int feature, boolean enabled);