diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 1cd197fb9920..760a00651ed0 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -268,6 +268,24 @@ "workingSets": "SVM", }, + "com.oracle.svm.core.jdk17": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": ["com.oracle.svm.core"], + "requiresConcealed" : { + "java.base" : [ + "jdk.internal.loader", + "jdk.internal.misc", + "jdk.internal.platform", + "sun.invoke.util", + ], + }, + "javaCompliance": "17+", + "checkstyle": "com.oracle.svm.core", + "workingSets": "SVM", + }, + + "com.oracle.svm.core.genscavenge": { "subDir": "src", "sourceDirs": [ @@ -1055,6 +1073,7 @@ "com.oracle.svm.core.jdk14", "com.oracle.svm.core.jdk15", "com.oracle.svm.core.jdk16", + "com.oracle.svm.core.jdk17", "com.oracle.svm.core.graal.amd64", "com.oracle.svm.core.graal.aarch64", "com.oracle.svm.core.posix", diff --git a/substratevm/src/com.oracle.svm.core.jdk15/src/com/oracle/svm/core/jdk15/HiddenClassSupportImpl.java b/substratevm/src/com.oracle.svm.core.jdk15/src/com/oracle/svm/core/jdk15/HiddenClassSupportImpl.java new file mode 100644 index 000000000000..ca57e42f9229 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.jdk15/src/com/oracle/svm/core/jdk15/HiddenClassSupportImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.svm.core.jdk15; + +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; + +import com.oracle.svm.core.jdk.HiddenClassSupport; + + +final class HiddenClassSupportImpl extends HiddenClassSupport { + @Override + public boolean isHidden(Class clazz) { + return clazz.isHidden(); + } +} + +@AutomaticFeature +final class HiddenClassFeature implements Feature { + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return JavaVersionUtil.JAVA_SPEC >= 15; + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(HiddenClassSupport.class, new HiddenClassSupportImpl()); + } +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_dcmd_DCmdStart.java b/substratevm/src/com.oracle.svm.core.jdk17/src/com/oracle/svm/core/jdk17/Target_jdk_internal_platform_CgroupMetrics_JDK17OrLater.java similarity index 66% rename from substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_dcmd_DCmdStart.java rename to substratevm/src/com.oracle.svm.core.jdk17/src/com/oracle/svm/core/jdk17/Target_jdk_internal_platform_CgroupMetrics_JDK17OrLater.java index a166c4fba475..ec0d393668eb 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_dcmd_DCmdStart.java +++ b/substratevm/src/com.oracle.svm.core.jdk17/src/com/oracle/svm/core/jdk17/Target_jdk_internal_platform_CgroupMetrics_JDK17OrLater.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -22,18 +22,19 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.jfr; +package com.oracle.svm.core.jdk17; -import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.jdk.JDK17OrLater; +import static com.oracle.svm.core.Containers.Options.UseContainerSupport; -@TargetClass(className = "jdk.jfr.internal.dcmd.DCmdStart", onlyWith = JfrEnabled.class) -public final class Target_jdk_jfr_internal_dcmd_DCmdStart { - @Alias - public Target_jdk_jfr_internal_dcmd_DCmdStart() { - } +import jdk.internal.platform.CgroupMetrics; - @Alias - public native String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) - throws Exception; -} +@TargetClass(value = jdk.internal.platform.CgroupMetrics.class, onlyWith = JDK17OrLater.class) +public final class Target_jdk_internal_platform_CgroupMetrics_JDK17OrLater { + @Substitute + public static boolean isUseContainerSupport() { + return UseContainerSupport.getValue(); + } +} \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/HiddenClassSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/HiddenClassSupport.java new file mode 100644 index 000000000000..436ceeeccbe6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/HiddenClassSupport.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.svm.core.jdk; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.util.VMError; + +/** + * Abstracts the information about hidden classes, which are not available in Java 11 and Java 8. + * This class provides all information about hidden classes without exposing any JDK types and + * methods that are not yet present in the old JDKs. + */ +public class HiddenClassSupport { + @Fold + public static HiddenClassSupport singleton() { + return ImageSingletons.lookup(HiddenClassSupport.class); + } + + @Fold + public static boolean isAvailable() { + return ImageSingletons.contains(HiddenClassSupport.class); + } + + /** Same as {@code Class.isHidden()}. */ + public boolean isHidden(Class clazz) { + throw VMError.shouldNotReachHere(); + } +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrChunkWriter.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrChunkWriter.java index 1cec47f10a56..2ed1c338dab1 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrChunkWriter.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrChunkWriter.java @@ -404,4 +404,8 @@ private void changeEpoch() { JfrTraceIdEpoch.getInstance().changeEpoch(); } } + + public long getChunkStartNanos() { + return chunkStartNanos; + } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java index 47452933910c..8aaaa82ab157 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrEnabled.java @@ -41,6 +41,13 @@ public boolean getAsBoolean() { } public static boolean get() { - return VMInspection.isEnabled() && JavaVersionUtil.JAVA_SPEC == 11 && (OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN); + return VMInspection.isEnabled() && jvmVersionSupported() && osSupported(); + } + + private static boolean jvmVersionSupported() { + return JavaVersionUtil.JAVA_SPEC == 11 || JavaVersionUtil.JAVA_SPEC == 16 || JavaVersionUtil.JAVA_SPEC == 17; + } + private static boolean osSupported() { + return OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN; } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java index 1a719a2b9db6..cefc1ce39c0d 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java @@ -149,8 +149,8 @@ public void duringSetup(DuringSetupAccess c) { public void beforeAnalysis(Feature.BeforeAnalysisAccess access) { RuntimeSupport runtime = RuntimeSupport.getRuntimeSupport(); JfrManager manager = JfrManager.get(); - runtime.addStartupHook(manager::setup); - runtime.addShutdownHook(manager::teardown); + runtime.addStartupHook(manager.startupHook()); + runtime.addShutdownHook(manager.shutdownHook()); } @Override @@ -180,7 +180,8 @@ public void duringAnalysis(DuringAnalysisAccess access) { // Use canonical name for package private AbstractJDKEvent if (c.getCanonicalName().equals("jdk.jfr.Event") || c.getCanonicalName().equals("jdk.internal.event.Event") - || c.getCanonicalName().equals("jdk.jfr.events.AbstractJDKEvent")) { + || c.getCanonicalName().equals("jdk.jfr.events.AbstractJDKEvent") + || c.getCanonicalName().equals("jdk.jfr.events.AbstractBufferStatisticsEvent")) { continue; } try { diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrManager.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrManager.java index 9770546ea225..8aafee0f3efa 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrManager.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrManager.java @@ -24,10 +24,24 @@ */ package com.oracle.svm.jfr; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; import java.time.Duration; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import jdk.jfr.Recording; +import jdk.jfr.internal.OldObjectSample; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.SecuritySupport; +import jdk.jfr.internal.Utils; +import jdk.jfr.internal.jfc.JFC; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -64,20 +78,24 @@ static JfrManager get() { return ImageSingletons.lookup(JfrManager.class); } - void setup() { - parseFlightRecorderLogging(SubstrateOptions.FlightRecorderLogging.getValue()); - if (SubstrateOptions.FlightRecorder.getValue()) { - periodicEventSetup(); - initRecording(); - } + Runnable startupHook() { + return () -> { + parseFlightRecorderLogging(SubstrateOptions.FlightRecorderLogging.getValue()); + if (SubstrateOptions.FlightRecorder.getValue()) { + periodicEventSetup(); + initRecording(); + } + }; } - void teardown() { - if (SubstrateOptions.FlightRecorder.getValue()) { - // Everything should already have been torn down by JVM.destroyJFR(), which is called in - // a shutdown hook. - assert !SubstrateJVM.isInitialized(); - } + Runnable shutdownHook() { + return () -> { + if (SubstrateOptions.FlightRecorder.getValue()) { + // Everything should already have been torn down by JVM.destroyJFR(), which is called in + // a shutdown hook. + assert !SubstrateJVM.isInitialized(); + } + }; } private static void parseFlightRecorderLogging(String option) { @@ -107,15 +125,175 @@ private static void initRecording() { Boolean dumpOnExit = parseBoolean(args, JfrStartArgument.DumpOnExit); Boolean pathToGcRoots = parseBoolean(args, JfrStartArgument.PathToGCRoots); - Target_jdk_jfr_internal_dcmd_DCmdStart dcmdStart = new Target_jdk_jfr_internal_dcmd_DCmdStart(); try { - String msg = dcmdStart.execute(name, settings, delay, duration, disk, path, maxAge, maxSize, - dumpOnExit, pathToGcRoots); - Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, msg); + if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + + ", settings=" + Arrays.asList(settings) + + ", delay=" + delay + + ", duration=" + duration + + ", disk=" + disk + + ", filename=" + path + + ", maxage=" + maxAge + + ", maxsize=" + maxSize + + ", dumponexit =" + dumpOnExit + + ", path-to-gc-roots=" + pathToGcRoots); + } + if (name != null) { + try { + Integer.parseInt(name); + throw new Exception("Name of recording can't be numeric"); + } catch (NumberFormatException nfe) { + // ok, can't be mixed up with name + } + } + + if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) { + throw new Exception("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename."); + } + if (settings.length == 1 && settings[0].length() == 0) { + throw new Exception("No settings specified. Use settings=none to start without any settings"); + } + Map s = new HashMap<>(); + for (String configName : settings) { + try { + s.putAll(JFC.createKnown(configName).getSettings()); + } catch (FileNotFoundException e) { + throw new Exception("Could not find settings file'" + configName + "'", e); + } catch (IOException | ParseException e) { + throw new Exception("Could not parse settings file '" + settings[0] + "'", e); + } + } + + OldObjectSample.updateSettingPathToGcRoots(s, pathToGcRoots); + + if (duration != null) { + if (duration < 1000L * 1000L * 1000L) { + // to avoid typo, duration below 1s makes no sense + throw new Exception("Could not start recording, duration must be at least 1 second."); + } + } + + if (delay != null) { + if (delay < 1000L * 1000L * 1000) { + // to avoid typo, delay shorter than 1s makes no sense. + throw new Exception("Could not start recording, delay must be at least 1 second."); + } + } + + + Recording recording = new Recording(); + if (name != null) { + recording.setName(name); + } + + if (disk != null) { + recording.setToDisk(disk.booleanValue()); + } + recording.setSettings(s); + SecuritySupport.SafePath safePath = null; + + if (path != null) { + try { + if (dumpOnExit == null) { + // default to dumponexit=true if user specified filename + dumpOnExit = Boolean.TRUE; + } + Path p = Paths.get(path); + if (Files.isDirectory(p) && Boolean.TRUE.equals(dumpOnExit)) { + // Decide destination filename at dump time + // Purposely avoid generating filename in Recording#setDestination due to + // security concerns + PrivateAccess.getInstance().getPlatformRecording(recording).setDumpOnExitDirectory(new SecuritySupport.SafePath(p)); + } else { + safePath = resolvePath(recording, path); + recording.setDestination(safePath.toPath()); + } + } catch (IOException | InvalidPathException e) { + recording.close(); + throw new Exception("Could not start recording, not able to write to file: " + path, e); + } + } + + if (maxAge != null) { + recording.setMaxAge(Duration.ofNanos(maxAge)); + } + + if (maxSize != null) { + recording.setMaxSize(maxSize); + } + + if (duration != null) { + recording.setDuration(Duration.ofNanos(duration)); + } + + if (dumpOnExit != null) { + recording.setDumpOnExit(dumpOnExit); + } + + StringBuilder msg = new StringBuilder(); + + if (delay != null) { + Duration dDelay = Duration.ofNanos(delay); + recording.scheduleStart(dDelay); + + msg.append("Recording " + recording.getId() + " scheduled to start in "); + msg.append(Utils.formatTimespan(dDelay, " ")); + msg.append("."); + } else { + recording.start(); + msg.append("Started recording " + recording.getId() + "."); + } + + if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) { + msg.append(" No limit specified, using maxsize=250MB as default."); + recording.setMaxSize(250 * 1024L * 1024L); + } + + if (safePath != null && duration != null) { + msg.append(" The result will be written to:"); + msg.append(System.getProperty("line.separator")); + msg.append(getPath(safePath)); + msg.append(System.getProperty("line.separator")); + } + Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, msg.toString()); } catch (Throwable e) { VMError.shouldNotReachHere(e); } } + private static SecuritySupport.SafePath resolvePath(Recording recording, String filename) throws InvalidPathException { + if (filename == null) { + return makeGenerated(recording, Paths.get(".")); + } + Path path = Paths.get(filename); + if (Files.isDirectory(path)) { + return makeGenerated(recording, path); + } + return new SecuritySupport.SafePath(path.toAbsolutePath().normalize()); + } + + private static SecuritySupport.SafePath makeGenerated(Recording recording, Path directory) { + return new SecuritySupport.SafePath(directory.toAbsolutePath().resolve(Utils.makeFilename(recording)).normalize()); + } + + private static String getPath(SecuritySupport.SafePath path) { + if (path == null) { + return "N/A"; + } + try { + return getPath(SecuritySupport.getAbsolutePath(path).toPath()); + } catch (IOException ioe) { + return getPath(path.toPath()); + } + } + + private static String getPath(Path path) { + try { + return path.toAbsolutePath().toString(); + } catch (SecurityException e) { + // fall back on filename + return path.toString(); + } + } private static Map parseStartFlightRecording() { Map optionsMap = new HashMap<>(); diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrTypeRepository.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrTypeRepository.java index caaf00360c5b..8e5951be296d 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrTypeRepository.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrTypeRepository.java @@ -33,7 +33,9 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.annotate.Uninterruptible; + import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.jdk.HiddenClassSupport; import com.oracle.svm.jfr.traceid.JfrTraceId; /** @@ -120,6 +122,9 @@ private static void writeClass(JfrChunkWriter writer, TypeInfo typeInfo, Class eventClass) { + try { + Field f = eventClass.getDeclaredField("eventHandler"); + f.setAccessible(true); + return f.get(null); + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + throw new InternalError("Could not access event handler"); + } + } + public static boolean isInitialized() { return get().initialized; } @@ -246,6 +260,11 @@ public void endRecording() { // and that no further JFR events will be triggered. } + /** See {@link JVM#isRecording}. This is not thread safe */ + public boolean unsafeIsRecording() { + return recording; + } + /** See {@link JVM#getClassId}. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getClassId(Class clazz) { @@ -385,6 +404,16 @@ public boolean shouldRotateDisk() { } } + /** See {@link JVM#getChunkStartNanos}. */ + public long getChunkStartNanos() { + JfrChunkWriter chunkWriter = unlockedChunkWriter.lock(); + try { + return chunkWriter.getChunkStartNanos(); + } finally { + chunkWriter.unlock(); + } + } + /** See {@link JVM#log}. */ public void log(int tagSetId, int level, String message) { jfrLogging.log(tagSetId, level, message); diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_JVM.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_JVM.java index 2a569f9b2fd2..414b0a602069 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_JVM.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_JVM.java @@ -34,14 +34,19 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK14OrEarlier; +import com.oracle.svm.core.jdk.JDK14OrLater; import com.oracle.svm.core.jdk.JDK15OrLater; +import com.oracle.svm.core.jdk.JDK16OrEarlier; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.jfr.traceid.JfrTraceId; import jdk.jfr.Event; import jdk.jfr.internal.EventWriter; import jdk.jfr.internal.JVM; import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.handlers.EventHandler; // Checkstyle: allow synchronization. @SuppressWarnings({"static-method", "unused"}) @@ -51,11 +56,14 @@ public final class Target_jdk_jfr_internal_JVM { @Alias static Object FILE_DELTA_CHANGE; // Checkstyle: resume - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // - private volatile boolean recording; @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // private volatile boolean nativeOK; + @Alias + @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) + @TargetElement(onlyWith = JDK11OrEarlier.class) + private volatile boolean recording; + /** See {@link JVM#registerNatives}. */ @Substitute private static void registerNatives() { @@ -85,6 +93,13 @@ public void endRecording() { SubstrateJVM.get().endRecording(); } + /** See {@link JVM#isRecording}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) + public boolean isRecording() { + return SubstrateJVM.get().unsafeIsRecording(); + } + /** See {@link JVM#getAllEventClasses}. */ @Substitute public List> getAllEventClasses() { @@ -100,13 +115,14 @@ public long getUnloadedEventClassCount() { /** See {@link JVM#getClassId}. Intrinsified on HotSpot. */ @Substitute public static long getClassId(Class clazz) { - return getClassIdNonIntrinsic(clazz); + return SubstrateJVM.get().getClassId(clazz); } /** See {@link JVM#getClassIdNonIntrinsic}. */ @Substitute + @TargetElement(onlyWith = JDK16OrEarlier.class) public static long getClassIdNonIntrinsic(Class clazz) { - return SubstrateJVM.get().getClassId(clazz); + return getClassId(clazz); } /** See {@link JVM#getPid}. */ @@ -271,6 +287,29 @@ public double getTimeConversionFactor() { return 1; } + /** See {@link JVM#getChunkStartNanos}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) + public long getChunkStartNanos() { + return SubstrateJVM.get().getChunkStartNanos(); + } + + /** See {@link JVM#setHandler}. */ + @Substitute + @TargetElement(onlyWith = JDK15OrLater.class) + public boolean setHandler(Class eventClass, EventHandler handler) { + // eventHandler fields should all be set at compile time so this method + // should never be reached at runtime + throw VMError.shouldNotReachHere("eventHandler does not exist for: " + eventClass); + } + + /** See {@link JVM#getHandler}. */ + @Substitute + @TargetElement(onlyWith = JDK15OrLater.class) + public Object getHandler(Class eventClass) { + return SubstrateJVM.getHandler(eventClass); + } + /** See {@link JVM#getTypeId}. */ @Substitute public long getTypeId(Class clazz) { @@ -339,4 +378,39 @@ public void emitOldObjectSamples(long cutoff, boolean emitAll, boolean skipBFS) public boolean shouldRotateDisk() { return SubstrateJVM.get().shouldRotateDisk(); } + + /** See {@link JVM#flush}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) // + public void flush() { + // Temporarily do nothing. This is used for JFR streaming. + } + + /** See {@link JVM#include}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) // + public void include(Thread thread) { + // Temporarily do nothing. This is used for JFR streaming. + } + + /** See {@link JVM#exclude}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) // + public void exclude(Thread thread) { + // Temporarily do nothing. This is used for JFR streaming. + } + + /** See {@link JVM#isExcluded}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) // + public boolean isExcluded(Thread thread) { + // Temporarily do nothing. This is used for JFR streaming. + return false; + } + /** See {@link JVM#markChunkFinal}. */ + @Substitute + @TargetElement(onlyWith = JDK14OrLater.class) // + public void markChunkFinal() { + // Temporarily do nothing. This is used for JFR streaming. + } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_StringPool.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_StringPool.java index 12c55317d3be..755de3729218 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_StringPool.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/Target_jdk_jfr_internal_StringPool.java @@ -24,16 +24,11 @@ */ package com.oracle.svm.jfr; -import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import jdk.internal.misc.Unsafe; - @TargetClass(value = jdk.jfr.internal.StringPool.class, onlyWith = JfrEnabled.class) final class Target_jdk_jfr_internal_StringPool { - @Alias private static Unsafe unsafe; - @Substitute public static long addString(@SuppressWarnings("unused") String s) { // This disables String caching and forces the EventWriter to write strings by value. diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogConfiguration.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogConfiguration.java index ec002034ff2f..836ef94d2494 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogConfiguration.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogConfiguration.java @@ -30,10 +30,12 @@ import java.util.Map; import java.util.Set; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.util.VMError; import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogTag; @@ -109,6 +111,29 @@ private static Map> createLogTagSets() { result.put(LogTag.JFR_EVENT, EnumSet.of(JfrLogTag.JFR, JfrLogTag.EVENT)); result.put(LogTag.JFR_SETTING, EnumSet.of(JfrLogTag.JFR, JfrLogTag.SETTING)); result.put(LogTag.JFR_DCMD, EnumSet.of(JfrLogTag.JFR, JfrLogTag.DCMD)); + + // JDK16 support + if (JavaVersionUtil.JAVA_SPEC >= 16) { + try { + LogTag jfrSystemStreaming = Enum.valueOf(LogTag.class, "JFR_SYSTEM_STREAMING"); + LogTag jfrSystemThrottle = Enum.valueOf(LogTag.class, "JFR_SYSTEM_THROTTLE"); + result.put(jfrSystemStreaming, EnumSet.of(JfrLogTag.JFR, JfrLogTag.SYSTEM, JfrLogTag.STREAMING)); + result.put(jfrSystemThrottle, EnumSet.of(JfrLogTag.JFR, JfrLogTag.SYSTEM, JfrLogTag.THROTTLE)); + } catch (IllegalArgumentException | NullPointerException e) { + throw VMError.shouldNotReachHere("Should be defined", e); + } + } + + // JDK17 support + if (JavaVersionUtil.JAVA_SPEC >= 17) { + try { + LogTag jfrStart = Enum.valueOf(LogTag.class, "JFR_START"); + result.put(jfrStart, EnumSet.of(JfrLogTag.JFR, JfrLogTag.START)); + } catch (IllegalArgumentException | NullPointerException e) { + throw VMError.shouldNotReachHere("Should be defined", e); + } + } + return result; } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogTag.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogTag.java index 82387bd61f4f..6b99c4e51559 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogTag.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogTag.java @@ -38,5 +38,8 @@ enum JfrLogTag { BYTECODE, PARSER, METADATA, - DCMD + STREAMING, + THROTTLE, + DCMD, + START } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogging.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogging.java index aaff25455329..3c7e553fc9e3 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogging.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/logging/JfrLogging.java @@ -25,6 +25,8 @@ */ package com.oracle.svm.jfr.logging; +import java.util.Set; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -97,13 +99,16 @@ private static String[] createLogTagSets() { String[] result = new String[getMaxLogTagSetId(values) + 1]; for (LogTag logTagSet : values) { StringBuilder builder = new StringBuilder(); - for (JfrLogTag logTag : JfrLogConfiguration.LOG_TAG_SETS.get(logTagSet)) { - if (builder.length() > 0) { - builder.append(","); + Set set = JfrLogConfiguration.LOG_TAG_SETS.get(logTagSet); + if (set != null) { + for (JfrLogTag logTag : set) { + if (builder.length() > 0) { + builder.append(","); + } + builder.append(logTag.toString().toLowerCase()); } - builder.append(logTag.toString().toLowerCase()); + result[getId(logTagSet)] = builder.toString(); } - result[getId(logTagSet)] = builder.toString(); } return result; } diff --git a/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestClassEvent.java b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestClassEvent.java index cf31c27980c2..0f6d9cfaccbc 100644 --- a/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestClassEvent.java +++ b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestClassEvent.java @@ -33,11 +33,12 @@ import jdk.jfr.consumer.RecordingFile; import org.junit.Test; + public class TestClassEvent { @Test public void test() throws Exception { JFR jfr = new LocalJFR(); - Recording recording = jfr.startRecording("TestSingleEvent"); + Recording recording = jfr.startRecording("TestClassEvent"); ClassEvent event = new ClassEvent(); event.clazz = TestClassEvent.class;