diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java index a28f1fdd41f..503a7955e00 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java @@ -45,6 +45,7 @@ import jdk.jfr.events.InitialSecurityPropertyEvent; import jdk.jfr.events.MethodTimingEvent; import jdk.jfr.events.MethodTraceEvent; import jdk.jfr.internal.periodic.PeriodicEvents; +import jdk.jfr.internal.settings.MethodSetting; import jdk.jfr.internal.tracing.PlatformTracer; import jdk.jfr.tracing.MethodTracer; @@ -235,7 +236,7 @@ public final class JDKEvents { } private static void emitMethodTiming() { - if (MethodTimingEvent.enabled()) { + if (MethodSetting.isInitialized() && MethodTimingEvent.enabled()) { PlatformTracer.emitTiming(); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java index f3e1069ca21..0f2751b6ac7 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java @@ -42,6 +42,7 @@ import jdk.jfr.internal.tracing.PlatformTracer; @Name(Type.SETTINGS_PREFIX + "Filter") public final class MethodSetting extends FilterSetting { private final Modification modification; + private volatile static boolean initialized; public MethodSetting(PlatformEventType eventType, Modification modification, String defaultValue) { super(eventType, defaultValue); @@ -55,6 +56,20 @@ public final class MethodSetting extends FilterSetting { @Override protected void apply(PlatformEventType eventType, List filters) { + ensureInitialized(); PlatformTracer.setFilters(modification, filters); } + + // Expected to be called when holding external lock, so no extra + // synchronization is required here. + private static void ensureInitialized() { + if (!initialized) { + PlatformTracer.initialize(); + initialized = true; + } + } + + public static boolean isInitialized() { + return initialized; + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java index 5985b28b58e..2d40e496dc1 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java @@ -53,8 +53,6 @@ public final class PlatformTracer { private static List timingFilters = List.of(); private static TimedMethod OBJECT; - private static boolean initialized; - public static byte[] onMethodTrace(Module module, ClassLoader classLoader, String className, byte[] oldBytecode, long[] ids, String[] names, String[] signatures, int[] modifications) { @@ -159,7 +157,6 @@ public final class PlatformTracer { } public static void setFilters(Modification modification, List filters) { - ensureInitialized(); publishClasses(applyFilter(modification, filters)); } @@ -252,14 +249,6 @@ public final class PlatformTracer { timedClasses.clear(); } - // Expected to be called when holding external lock, so no extra - // synchronization is required here. - private static void ensureInitialized() { - if (!initialized) { - initialize(); - initialized = true; - } - } // This method has three purposes: // @@ -274,7 +263,7 @@ public final class PlatformTracer { // This method takes 1-10 milliseconds to run and is only executed once, // provided a user has specified a non-empty filter for the MethodTrace or // MethodTiming event. - private static void initialize() { + public static void initialize() { try { Logger.log(LogTag.JFR_METHODTRACE, LogLevel.DEBUG, "Method tracer initialization started."); Thread current = Thread.currentThread(); diff --git a/test/jdk/jdk/jfr/event/tracing/TestLazyPlatformTracer.java b/test/jdk/jdk/jfr/event/tracing/TestLazyPlatformTracer.java new file mode 100644 index 00000000000..d3e6146bc74 --- /dev/null +++ b/test/jdk/jdk/jfr/event/tracing/TestLazyPlatformTracer.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.event.tracing; + +import jdk.internal.misc.Unsafe; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +/** +* @test +* @summary Tests that PlatformTracer is not initialized if a method filter has not been set. +* @requires vm.flagless +* @requires vm.hasJFR +* @modules java.base/jdk.internal.misc jdk.jfr/jdk.jfr.internal.tracing +* @library /test/lib +* @run main/othervm -XX:StartFlightRecording jdk.jfr.event.tracing.TestLazyPlatformTracer +*/ +public class TestLazyPlatformTracer { + + public static void main(String... args) throws Exception { + // Stop recording so end chunk events are emitted + FlightRecorder.getFlightRecorder().getRecordings().getFirst().stop(); + if (!Unsafe.getUnsafe().shouldBeInitialized(jdk.jfr.internal.tracing.PlatformTracer.class)) { + throw new AssertionError("PlatformTracer should not have been initialized"); + } + } +}