From 8fc3daeaf90389444fc2e26962ac9588d1c47b49 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Mon, 21 Feb 2022 09:37:39 -0500 Subject: [PATCH 1/8] Implementation of ExecuteVMOperationEvent --- .../src/com/oracle/svm/core/jfr/JfrEvent.java | 3 +- .../src/com/oracle/svm/core/jfr/JfrType.java | 3 +- .../svm/core/jfr/JfrTypeRepository.java | 15 ++++ .../oracle/svm/core/jfr/JfrVMOperations.java | 68 ++++++++++++++++++ .../jfr/events/ExecuteVMOperationEvent.java | 69 +++++++++++++++++++ .../oracle/svm/core/thread/VMOperation.java | 7 +- .../com/oracle/svm/hosted/jfr/JfrFeature.java | 19 ++++- .../com/oracle/svm/test/jfr/TestGCEvents.java | 3 +- .../svm/test/jfr/utils/JfrFileParser.java | 2 + .../VMOperationConstantPoolParser.java | 44 ++++++++++++ 10 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/VMOperationConstantPoolParser.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java index 28e10826864a..70d047dee930 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java @@ -52,7 +52,8 @@ public enum JfrEvent { GCPhasePauseLevel3Event("jdk.GCPhasePauseLevel3"), GCPhasePauseLevel4Event("jdk.GCPhasePauseLevel4"), SafepointBegin("jdk.SafepointBegin"), - SafepointEnd("jdk.SafepointEnd"); + SafepointEnd("jdk.SafepointEnd"), + ExecuteVMOperation("jdk.ExecuteVMOperation"); private final long id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrType.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrType.java index 7709c613591e..b6294998bd17 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrType.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrType.java @@ -41,7 +41,8 @@ public enum JfrType { Package("jdk.types.Package"), FrameType("jdk.types.FrameType"), GCCause("jdk.types.GCCause"), - GCName("jdk.types.GCName"); + GCName("jdk.types.GCName"), + VMOperation("jdk.types.VMOperationType"); private final long id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java index cc7c93c296f6..0b5aa0a2b458 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java @@ -65,6 +65,7 @@ public int write(JfrChunkWriter writer) { count += writeClassLoaders(writer, typeInfo); count += writeGCCauses(writer); count += writeGCNames(writer); + count += writeVMOperations(writer); return count; } @@ -225,6 +226,20 @@ private static int writeGCNames(JfrChunkWriter writer) { return NON_EMPTY; } + private static int writeVMOperations(JfrChunkWriter writer) { + Class[] vmOperations = JfrVMOperations.singleton().getVMOperations(); + + assert vmOperations.length > 0; + writer.writeCompressedLong(JfrType.VMOperation.getId()); + writer.writeCompressedLong(vmOperations.length); + for (int id = 0; id < vmOperations.length; id++) { + writer.writeCompressedLong(id + 1); // id starts with 1 + writer.writeString(vmOperations[id].getCanonicalName()); + } + + return NON_EMPTY; + } + private static void writeClassLoader(JfrChunkWriter writer, ClassLoader cl, long id) { JfrSymbolRepository symbolRepo = SubstrateJVM.getSymbolRepository(); writer.writeCompressedLong(id); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java new file mode 100644 index 000000000000..5f52c05c02b3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. 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.jfr; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.annotate.Uninterruptible; + +import java.util.Collection; + +public class JfrVMOperations { + private Class[] vmOperations; + + @Platforms(Platform.HOSTED_ONLY.class) + public JfrVMOperations() { + vmOperations = new Class[0]; + } + + @Fold + public static JfrVMOperations singleton() { + return ImageSingletons.lookup(JfrVMOperations.class); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void addVMOperations(Collection> vmOps) { + vmOperations = vmOps.toArray(new Class[vmOps.size()]); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int getVMOperationId(Class clazz) { + for (int id = 0; id < vmOperations.length; id++) { + if (vmOperations[id] == clazz) { + return id + 1; // id starts with 1 + } + } + return 0; + } + + public Class[] getVMOperations() { + return vmOperations; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java new file mode 100644 index 000000000000..49673564606b --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. 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.jfr.events; + +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.jfr.HasJfrSupport; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.JfrVMOperations; +import com.oracle.svm.core.jfr.SubstrateJVM; +import com.oracle.svm.core.thread.Safepoint; +import com.oracle.svm.core.thread.VMOperation; + +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.StackValue; + +public class ExecuteVMOperationEvent { + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emit(VMOperation vmOperation, IsolateThread requestingThread, long startTicks) { + if (!HasJfrSupport.get()) { + return; + } + + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.ExecuteVMOperation)) { + int id = JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass()); + assert id != 0; + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + JfrNativeEventWriter.beginEventWrite(data, false); + JfrNativeEventWriter.putLong(data, JfrEvent.ExecuteVMOperation.getId()); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass())); + JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); + JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); + JfrNativeEventWriter.putThread(data, requestingThread); + JfrNativeEventWriter.putLong(data, VMOperation.isInProgressAtSafepoint() ? Safepoint.Master.singleton().getSafepointId().rawValue() : 0); + JfrNativeEventWriter.endEventWrite(data, false); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java index ceb148392a1c..dcef8d6c4b2f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java @@ -31,6 +31,8 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.events.ExecuteVMOperationEvent; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.thread.VMOperationControl.OpInProgress; import com.oracle.svm.core.util.VMError; @@ -83,8 +85,10 @@ protected final void execute(NativeVMOperationData data) { VMOperation prevOperation = control.getInProgress().getOperation(); IsolateThread prevQueuingThread = control.getInProgress().getQueuingThread(); IsolateThread prevExecutingThread = control.getInProgress().getExecutingThread(); + IsolateThread requestingThread = getQueuingThread(data); - control.setInProgress(this, getQueuingThread(data), CurrentIsolate.getCurrentThread(), true); + control.setInProgress(this, requestingThread, CurrentIsolate.getCurrentThread(), true); + long startTicks = JfrTicks.elapsedTicks(); try { trace.string("[Executing operation ").string(name); operate(data); @@ -93,6 +97,7 @@ protected final void execute(NativeVMOperationData data) { trace.string("[VMOperation.execute caught: ").string(t.getClass().getName()).string("]").newline(); throw VMError.shouldNotReachHere(t); } finally { + ExecuteVMOperationEvent.emit(this, requestingThread, startTicks); control.setInProgress(prevOperation, prevQueuingThread, prevExecutingThread, false); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java index de6b3d03139b..6164305c3a43 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java @@ -25,11 +25,12 @@ package com.oracle.svm.hosted.jfr; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import com.oracle.svm.core.jfr.JfrGCNames; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.hosted.Feature; @@ -47,12 +48,14 @@ import com.oracle.svm.core.jfr.JfrFrameTypeSerializer; import com.oracle.svm.core.jfr.JfrGlobalMemory; import com.oracle.svm.core.jfr.JfrManager; +import com.oracle.svm.core.jfr.JfrGCNames; import com.oracle.svm.core.jfr.JfrNativeEventWriter; import com.oracle.svm.core.jfr.JfrNativeEventWriterData; import com.oracle.svm.core.jfr.JfrRecorderThread; import com.oracle.svm.core.jfr.JfrSerializerSupport; import com.oracle.svm.core.jfr.JfrThreadLocal; import com.oracle.svm.core.jfr.JfrThreadStateSerializer; +import com.oracle.svm.core.jfr.JfrVMOperations; import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.traceid.JfrTraceId; import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch; @@ -60,10 +63,12 @@ import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.thread.ThreadListenerFeature; import com.oracle.svm.core.thread.ThreadListenerSupport; +import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import com.sun.management.HotSpotDiagnosticMXBean; @@ -179,6 +184,7 @@ public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(JfrTraceIdMap.class, new JfrTraceIdMap()); ImageSingletons.add(JfrTraceIdEpoch.class, new JfrTraceIdEpoch()); ImageSingletons.add(JfrGCNames.class, new JfrGCNames()); + ImageSingletons.add(JfrVMOperations.class, new JfrVMOperations()); JfrSerializerSupport.get().register(new JfrFrameTypeSerializer()); JfrSerializerSupport.get().register(new JfrThreadStateSerializer()); @@ -228,12 +234,23 @@ public void beforeCompilation(BeforeCompilationAccess a) { // Scan all classes and build sets of packages, modules and class-loaders. Count all items. Collection types = ((FeatureImpl.CompilationAccessImpl) a).getTypes(); + Collection> vmOperations = new ArrayList<>(); for (SharedType type : types) { DynamicHub hub = type.getHub(); Class clazz = hub.getHostedJavaClass(); // Off-set by one for error-catcher JfrTraceId.assign(clazz, hub.getTypeID() + 1); + + // Capture concrete VMOperation types for ExecuteVMOperationEvent + HostedType ht = (HostedType) type; + if (VMOperation.class.isAssignableFrom(clazz) && + !clazz.isInterface() && + !Modifier.isAbstract(clazz.getModifiers()) && + ht.getWrapped().isReachable()) { + vmOperations.add(clazz); + } } + ImageSingletons.lookup(JfrVMOperations.class).addVMOperations(vmOperations); } private static void eventSubtypeReachable(DuringAnalysisAccess a, Class c) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestGCEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestGCEvents.java index 5116cf690819..2557c29d79c8 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestGCEvents.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestGCEvents.java @@ -35,7 +35,8 @@ public String[] getTestedEvents() { "jdk.GarbageCollection", "jdk.GCPhasePause", "jdk.GCPhasePauseLevel1", - "jdk.GCPhasePauseLevel2" + "jdk.GCPhasePauseLevel2", + "jdk.ExecuteVMOperation" }; } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/JfrFileParser.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/JfrFileParser.java index 335287a74e54..dfc57198ab76 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/JfrFileParser.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/JfrFileParser.java @@ -50,6 +50,7 @@ import com.oracle.svm.test.jfr.utils.poolparsers.ThreadConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.ThreadGroupConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.ThreadStateConstantPoolParser; +import com.oracle.svm.test.jfr.utils.poolparsers.VMOperationConstantPoolParser; import jdk.jfr.Recording; import org.junit.Assert; @@ -76,6 +77,7 @@ public class JfrFileParser { supportedConstantPools.put(JfrType.GCName.getId(), new GCNameConstantPoolParser()); supportedConstantPools.put(JfrType.GCCause.getId(), new GCCauseConstantPoolParser()); + supportedConstantPools.put(JfrType.VMOperation.getId(), new VMOperationConstantPoolParser()); } public static HashMap getSupportedConstantPools() { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/VMOperationConstantPoolParser.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/VMOperationConstantPoolParser.java new file mode 100644 index 000000000000..490a3b17ba8e --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/VMOperationConstantPoolParser.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. 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.test.jfr.utils.poolparsers; + +import com.oracle.svm.test.jfr.utils.RecordingInput; +import org.junit.Assert; + +import java.io.IOException; + +public class VMOperationConstantPoolParser extends ConstantPoolParser { + + @Override + public void parse(RecordingInput input) throws IOException { + int numberOfVMOperationTypes = input.readInt(); + for (int i = 0; i < numberOfVMOperationTypes; i++) { + addFoundId(input.readInt()); // Id. + Assert.assertFalse("VMOperation type is empty!", input.readUTF().isEmpty()); + } + } +} From e499738198550881758ea4aaaf8e5aee01902d6e Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 3 Mar 2022 08:49:52 -0500 Subject: [PATCH 2/8] Christian's comments --- .../oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java index 49673564606b..c62bd545914c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java @@ -59,10 +59,10 @@ public static void emit(VMOperation vmOperation, IsolateThread requestingThread, JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); JfrNativeEventWriter.putEventThread(data); JfrNativeEventWriter.putLong(data, JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass())); - JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); - JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); + JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); // At a safepoint + JfrNativeEventWriter.putBoolean(data, true); // Blocking JfrNativeEventWriter.putThread(data, requestingThread); - JfrNativeEventWriter.putLong(data, VMOperation.isInProgressAtSafepoint() ? Safepoint.Master.singleton().getSafepointId().rawValue() : 0); + JfrNativeEventWriter.putLong(data, vmOperation.getCausesSafepoint() ? Safepoint.Master.singleton().getSafepointId().rawValue() : 0); JfrNativeEventWriter.endEventWrite(data, false); } } From 15a353423dcbf948a4fb635f3e0fd3519ebf6549 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 3 Mar 2022 10:04:23 -0500 Subject: [PATCH 3/8] Fix style --- .../com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java index c62bd545914c..98629ac4287c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java @@ -59,7 +59,7 @@ public static void emit(VMOperation vmOperation, IsolateThread requestingThread, JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); JfrNativeEventWriter.putEventThread(data); JfrNativeEventWriter.putLong(data, JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass())); - JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); // At a safepoint + JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); JfrNativeEventWriter.putBoolean(data, true); // Blocking JfrNativeEventWriter.putThread(data, requestingThread); JfrNativeEventWriter.putLong(data, vmOperation.getCausesSafepoint() ? Safepoint.Master.singleton().getSafepointId().rawValue() : 0); From 2ed48728296bad33e2e844f654dcb7581156cca3 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 7 Mar 2022 12:25:49 +0100 Subject: [PATCH 4/8] Ensure that the VM operation names live in the image heap. --- .../oracle/svm/core/genscavenge/GCImpl.java | 18 ++- .../core/genscavenge/MemoryWalkerImpl.java | 87 ------------ .../svm/core/genscavenge/PathExhibitor.java | 3 +- .../src/com/oracle/svm/core/MemoryWalker.java | 15 +- .../src/com/oracle/svm/core/VMInspection.java | 67 ++++++--- .../code/AbstractRuntimeCodeInstaller.java | 48 +++++-- .../oracle/svm/core/code/CodeInfoTable.java | 28 +++- .../oracle/svm/core/deopt/Deoptimizer.java | 42 +++++- .../VMOperationInfo.java} | 50 +++---- .../svm/core/heap/VMOperationInfos.java | 132 ++++++++++++++++++ .../oracle/svm/core/jfr/JfrChunkWriter.java | 5 +- .../svm/core/jfr/JfrTypeRepository.java | 14 +- .../com/oracle/svm/core/jfr/SubstrateJVM.java | 45 ++++-- .../jfr/events/ExecuteVMOperationEvent.java | 15 +- .../oracle/svm/core/thread/Continuation.java | 15 +- .../svm/core/thread/JavaVMOperation.java | 33 +---- .../svm/core/thread/NativeVMOperation.java | 5 +- .../svm/core/thread/PlatformThreads.java | 63 ++++++--- .../oracle/svm/core/thread/VMOperation.java | 27 ++-- .../svm/core/thread/VMOperationControl.java | 25 +++- .../com/oracle/svm/core/thread/VMThreads.java | 41 ++++-- .../com/oracle/svm/hosted/jfr/JfrFeature.java | 19 +-- 22 files changed, 479 insertions(+), 318 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/{jfr/JfrVMOperations.java => heap/VMOperationInfo.java} (55%) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index b130027d2cfc..f1adef48d8c7 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -29,6 +29,7 @@ import java.lang.ref.Reference; +import com.oracle.svm.core.heap.VMOperationInfos; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; @@ -1235,7 +1236,7 @@ private CollectionInProgressError() { private static class CollectionVMOperation extends NativeVMOperation { CollectionVMOperation() { - super("Garbage collection", SystemEffect.SAFEPOINT); + super(VMOperationInfos.get(CollectionVMOperation.class, "Garbage collection", SystemEffect.SAFEPOINT)); } @Override @@ -1376,7 +1377,9 @@ private void printGCSummary() { log.string(prefix).string("MaximumHeapSize: ").unsigned(getPolicy().getMaximumHeapSize()).newline(); log.string(prefix).string("AlignedChunkSize: ").unsigned(HeapParameters.getAlignedHeapChunkSize()).newline(); - JavaVMOperation.enqueueBlockingSafepoint("PrintGCSummaryShutdownHook", ThreadLocalAllocation::disableAndFlushForAllThreads); + FlushTLABsOperation vmOp = new FlushTLABsOperation(); + vmOp.enqueue(); + HeapImpl heap = HeapImpl.getHeapImpl(); Space edenSpace = heap.getYoungGeneration().getEden(); UnsignedWord youngChunkBytes = edenSpace.getChunkBytes(); @@ -1405,4 +1408,15 @@ private void printGCSummary() { log.string(prefix).string("TotalNanos: ").signed(totalNanos).newline(); log.string(prefix).string("GCLoadPercent: ").signed(roundedGCLoad).newline(); } + + private static class FlushTLABsOperation extends JavaVMOperation { + protected FlushTLABsOperation() { + super(VMOperationInfos.get(FlushTLABsOperation.class, "Flush TLABs", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + ThreadLocalAllocation.disableAndFlushForAllThreads(); + } + } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java deleted file mode 100644 index f4f19a39ea19..000000000000 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2016, 2017, 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.genscavenge; - -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.hosted.Feature; - -import com.oracle.svm.core.MemoryWalker; -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.annotate.RestrictHeapAccess; -import com.oracle.svm.core.code.CodeInfoTable; -import com.oracle.svm.core.thread.JavaVMOperation; - -/** Walker to visit different memory regions with a {@link MemoryWalker}. */ -final class MemoryWalkerImpl extends MemoryWalker { - @Platforms(Platform.HOSTED_ONLY.class) - MemoryWalkerImpl() { - } - - @Override - public boolean visitMemory(MemoryWalker.Visitor visitor) { - MemoryWalkerVMOperation op = new MemoryWalkerVMOperation(visitor); - op.enqueue(); - return op.getResult(); - } - - static final class MemoryWalkerVMOperation extends JavaVMOperation { - private final MemoryWalker.Visitor visitor; - private boolean result = false; - - MemoryWalkerVMOperation(MemoryWalker.Visitor memoryVisitor) { - super("MemoryWalkerImpl.visitMemory", SystemEffect.SAFEPOINT); - this.visitor = memoryVisitor; - } - - @Override - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Allocation would change the memory being visited.") - public void operate() { - ThreadLocalAllocation.disableAndFlushForAllThreads(); - result = HeapImpl.getHeapImpl().walkMemory(visitor) && - CodeInfoTable.getImageCodeCache().walkImageCode(visitor) && - CodeInfoTable.getRuntimeCodeCache().walkRuntimeMethods(visitor); - } - - boolean getResult() { - return result; - } - } -} - -@AutomaticFeature -class MemoryWalkerFeature implements Feature { - @Override - public boolean isInConfiguration(IsInConfigurationAccess access) { - return SubstrateOptions.UseSerialGC.getValue(); - } - - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(MemoryWalker.class, new MemoryWalkerImpl()); - } -} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java index 4b95f0050f7f..35e5f7b4791f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java @@ -27,6 +27,7 @@ import java.util.ArrayList; +import com.oracle.svm.core.heap.VMOperationInfos; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; @@ -529,7 +530,7 @@ private static final class FindPathToObjectOperation extends JavaVMOperation { private final PathEdge result; FindPathToObjectOperation(PathExhibitor exhibitor, Object object, PathEdge result) { - super("FindPathToObjectOperation", SystemEffect.SAFEPOINT); + super(VMOperationInfos.get(FindPathToObjectOperation.class, "Find path to object", SystemEffect.SAFEPOINT)); this.exhibitor = exhibitor; this.object = object; this.result = result; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MemoryWalker.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MemoryWalker.java index aeb8f404c208..70180606f8dc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MemoryWalker.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MemoryWalker.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.core; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -33,19 +32,7 @@ import com.oracle.svm.core.heap.ObjectVisitor; /** A walker over different kinds of allocated memory. */ -public abstract class MemoryWalker { - - /** Get the implementation of the MemoryWalker. */ - public static MemoryWalker getMemoryWalker() { - return ImageSingletons.lookup(MemoryWalker.class); - } - - /** - * Walk memory applying the visitor. Returns true if all visits returned true, else false when - * any visit returns false. - */ - public abstract boolean visitMemory(Visitor visitor); - +public final class MemoryWalker { public interface ImageHeapRegionVisitor { /** Visit a region from the native image heap. */ @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while visiting memory.") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java index 2c85bcd9c5ef..6ddff48d2f3f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java @@ -42,6 +42,7 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.deopt.DeoptimizationSupport; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.RuntimeSupport; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.stack.JavaStackWalker; @@ -103,7 +104,17 @@ static void install() { @Override public void handle(Signal arg0) { - JavaVMOperation.enqueueBlockingSafepoint("DumpAllStacks", () -> { + DumpAllStacksOperation vmOp = new DumpAllStacksOperation(); + vmOp.enqueue(); + } + + private static class DumpAllStacksOperation extends JavaVMOperation { + DumpAllStacksOperation() { + super(VMOperationInfos.get(DumpAllStacksOperation.class, "Dump all stacks", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { Log log = Log.log(); log.string("Full thread dump:").newline().newline(); for (IsolateThread vmThread = VMThreads.firstThread(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) { @@ -119,29 +130,29 @@ public void handle(Signal arg0) { } } log.flush(); - }); - } + } - private static void dumpStack(Log log, IsolateThread vmThread) { - Thread javaThread = PlatformThreads.fromVMThread(vmThread); - if (javaThread != null) { - log.character('"').string(javaThread.getName()).character('"'); - log.string(" #").signed(javaThread.getId()); - if (javaThread.isDaemon()) { - log.string(" daemon"); + private static void dumpStack(Log log, IsolateThread vmThread) { + Thread javaThread = PlatformThreads.fromVMThread(vmThread); + if (javaThread != null) { + log.character('"').string(javaThread.getName()).character('"'); + log.string(" #").signed(javaThread.getId()); + if (javaThread.isDaemon()) { + log.string(" daemon"); + } + } else { + log.string("(no Java thread)"); } - } else { - log.string("(no Java thread)"); - } - log.string(" tid=0x").zhex(vmThread.rawValue()); - if (javaThread != null) { - log.string(" state=").string(javaThread.getState().name()); - } - log.newline(); + log.string(" tid=0x").zhex(vmThread.rawValue()); + if (javaThread != null) { + log.string(" state=").string(javaThread.getState().name()); + } + log.newline(); - log.indent(true); - JavaStackWalker.walkThread(vmThread, StackFramePrintVisitor.SINGLETON, log); - log.indent(false); + log.indent(true); + JavaStackWalker.walkThread(vmThread, StackFramePrintVisitor.SINGLETON, log); + log.indent(false); + } } } @@ -172,10 +183,20 @@ static void install() { @Override public void handle(Signal arg0) { - JavaVMOperation.enqueueBlockingSafepoint("DumpRuntimeCompilation", () -> { + DumpRuntimeCompiledMethodsOperation vmOp = new DumpRuntimeCompiledMethodsOperation(); + vmOp.enqueue(); + } + + private static class DumpRuntimeCompiledMethodsOperation extends JavaVMOperation { + DumpRuntimeCompiledMethodsOperation() { + super(VMOperationInfos.get(DumpRuntimeCompiledMethodsOperation.class, "Dump runtime compiled methods", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { Log log = Log.log(); SubstrateDiagnostics.dumpRuntimeCompilation(log); log.flush(); - }); + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/AbstractRuntimeCodeInstaller.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/AbstractRuntimeCodeInstaller.java index 366442b66bf5..a62a4ba55dd8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/AbstractRuntimeCodeInstaller.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/AbstractRuntimeCodeInstaller.java @@ -31,6 +31,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.deopt.SubstrateInstalledCode; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.thread.JavaVMOperation; @@ -62,21 +63,10 @@ protected static void doInstallPrepared(SharedMethod method, CodeInfo codeInfo, } protected static void doInstallPreparedAndTethered(SharedMethod method, CodeInfo codeInfo, SubstrateInstalledCode installedCode) { - Throwable[] errorBox = {null}; - JavaVMOperation.enqueueBlockingSafepoint("Install code", () -> { - try { - assert !installedCode.isValid() && !installedCode.isAlive(); - CodePointer codeStart = CodeInfoAccess.getCodeStart(codeInfo); - installedCode.setAddress(codeStart.rawValue(), method); - - CodeInfoTable.getRuntimeCodeCache().addMethod(codeInfo); - platformHelper().performCodeSynchronization(codeInfo); - } catch (Throwable e) { - errorBox[0] = e; - } - }); - if (errorBox[0] != null) { - throw rethrow(errorBox[0]); + InstallCodeOperation vmOp = new InstallCodeOperation(method, codeInfo, installedCode); + vmOp.enqueue(); + if (vmOp.error != null) { + throw rethrow(vmOp.error); } } @@ -89,6 +79,34 @@ protected static RuntimeCodeInstallerPlatformHelper platformHelper() { return ImageSingletons.lookup(RuntimeCodeInstallerPlatformHelper.class); } + private static class InstallCodeOperation extends JavaVMOperation { + private final SharedMethod method; + private final CodeInfo codeInfo; + private final SubstrateInstalledCode installedCode; + private Throwable error; + + InstallCodeOperation(SharedMethod method, CodeInfo codeInfo, SubstrateInstalledCode installedCode) { + super(VMOperationInfos.get(InstallCodeOperation.class, "Install code", SystemEffect.SAFEPOINT)); + this.method = method; + this.codeInfo = codeInfo; + this.installedCode = installedCode; + } + + @Override + protected void operate() { + try { + assert !installedCode.isValid() && !installedCode.isAlive(); + CodePointer codeStart = CodeInfoAccess.getCodeStart(codeInfo); + installedCode.setAddress(codeStart.rawValue(), method); + + CodeInfoTable.getRuntimeCodeCache().addMethod(codeInfo); + platformHelper().performCodeSynchronization(codeInfo); + } catch (Throwable e) { + error = e; + } + } + } + /** Methods which are platform specific. */ public interface RuntimeCodeInstallerPlatformHelper { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 0dd70333fd63..4b0e3fcf20e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -28,6 +28,8 @@ import java.util.List; import com.oracle.svm.core.heap.ReferenceMapIndex; +import com.oracle.svm.core.heap.VMOperationInfos; +import com.oracle.svm.core.thread.VMOperation.SystemEffect; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; @@ -183,13 +185,8 @@ private static SubstrateInstalledCode getInstalledCode0(CodeInfo info) { } public static void invalidateInstalledCode(SubstrateInstalledCode installedCode) { - /* Captures "installedCode" for the VMOperation. */ - JavaVMOperation.enqueueBlockingSafepoint("CodeInfoTable.invalidateInstalledCode", () -> { - counters().invalidateInstalledCodeCount.inc(); - if (installedCode.isAlive()) { // could be invalid (non-entrant), but executing - invalidateInstalledCodeAtSafepoint(WordFactory.pointer(installedCode.getAddress())); - } - }); + InvalidateInstalledCodeOperation vmOp = new InvalidateInstalledCodeOperation(installedCode); + vmOp.enqueue(); } /** @@ -251,6 +248,23 @@ public static void tearDown() { private static CodeInfoTableCounters counters() { return ImageSingletons.lookup(CodeInfoTableCounters.class); } + + private static class InvalidateInstalledCodeOperation extends JavaVMOperation { + private final SubstrateInstalledCode installedCode; + + InvalidateInstalledCodeOperation(SubstrateInstalledCode installedCode) { + super(VMOperationInfos.get(InvalidateInstalledCodeOperation.class, "Invalidate code", SystemEffect.SAFEPOINT)); + this.installedCode = installedCode; + } + + @Override + protected void operate() { + counters().invalidateInstalledCodeCount.inc(); + if (installedCode.isAlive()) { // could be invalid (non-entrant), but executing + invalidateInstalledCodeAtSafepoint(WordFactory.pointer(installedCode.getAddress())); + } + } + } } final class CodeInfoTableCounters { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index dcfbec9aaf8f..414bddf00a8c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -68,6 +68,7 @@ import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ReferenceAccess; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.log.Log; @@ -263,9 +264,19 @@ private static void installDeoptimizedFrame(Pointer sourceSp, DeoptimizedFrame d */ @NeverInline("deoptimize must have a separate stack frame") public static void deoptimizeAll() { - JavaVMOperation.enqueueBlockingSafepoint("Deoptimizer.deoptimizeInRange", () -> { - deoptimizeInRange((CodePointer) WordFactory.zero(), (CodePointer) WordFactory.zero(), true); - }); + DeoptimizeAllOperation vmOp = new DeoptimizeAllOperation(); + vmOp.enqueue(); + } + + private static class DeoptimizeAllOperation extends JavaVMOperation { + DeoptimizeAllOperation() { + super(VMOperationInfos.get(DeoptimizeAllOperation.class, "Deoptimize all", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + deoptimizeInRange(WordFactory.zero(), WordFactory.zero(), true); + } } /** @@ -344,7 +355,28 @@ public static void deoptimizeFrame(Pointer sourceSp, boolean ignoreNonDeoptimiza */ IsolateThread targetThread = CurrentIsolate.getCurrentThread(); - JavaVMOperation.enqueueBlockingSafepoint("DeoptimizeFrame", () -> Deoptimizer.deoptimizeFrameOperation(sourceSp, ignoreNonDeoptimizable, speculation, targetThread)); + DeoptimizeFrameOperation vmOp = new DeoptimizeFrameOperation(sourceSp, ignoreNonDeoptimizable, speculation, targetThread); + vmOp.enqueue(); + } + + private static class DeoptimizeFrameOperation extends JavaVMOperation { + private final Pointer sourceSp; + private final boolean ignoreNonDeoptimizable; + private final SpeculationReason speculation; + private final IsolateThread targetThread; + + DeoptimizeFrameOperation(Pointer sourceSp, boolean ignoreNonDeoptimizable, SpeculationReason speculation, IsolateThread targetThread) { + super(VMOperationInfos.get(DeoptimizeFrameOperation.class, "Deoptimize frame", SystemEffect.SAFEPOINT)); + this.sourceSp = sourceSp; + this.ignoreNonDeoptimizable = ignoreNonDeoptimizable; + this.speculation = speculation; + this.targetThread = targetThread; + } + + @Override + protected void operate() { + Deoptimizer.deoptimizeFrameOperation(sourceSp, ignoreNonDeoptimizable, speculation, targetThread); + } } private static void deoptimizeFrameOperation(Pointer sourceSp, boolean ignoreNonDeoptimizable, SpeculationReason speculation, IsolateThread targetThread) { @@ -628,7 +660,7 @@ private static final class DeoptSourceFrameOperation extends JavaVMOperation { private DeoptimizedFrame result; DeoptSourceFrameOperation(Deoptimizer receiver, CodePointer pc, boolean ignoreNonDeoptimizable) { - super("DeoptSourceFrameOperation", SystemEffect.SAFEPOINT); + super(VMOperationInfos.get(DeoptSourceFrameOperation.class, "Deoptimize source frame", SystemEffect.SAFEPOINT)); this.receiver = receiver; this.pc = pc; this.ignoreNonDeoptimizable = ignoreNonDeoptimizable; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java similarity index 55% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java index 5f52c05c02b3..192274c3f1d9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrVMOperations.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2022, 2022, Red Hat Inc. 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 @@ -23,46 +22,39 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.jfr; - -import org.graalvm.compiler.api.replacements.Fold; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; +package com.oracle.svm.core.heap; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.thread.VMOperation.SystemEffect; -import java.util.Collection; - -public class JfrVMOperations { - private Class[] vmOperations; +public final class VMOperationInfo { + private final int id; + private final String name; + private final SystemEffect systemEffect; - @Platforms(Platform.HOSTED_ONLY.class) - public JfrVMOperations() { - vmOperations = new Class[0]; + VMOperationInfo(int id, String name, SystemEffect systemEffect) { + this.id = id; + this.name = name; + this.systemEffect = systemEffect; } - @Fold - public static JfrVMOperations singleton() { - return ImageSingletons.lookup(JfrVMOperations.class); + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int getId() { + return id; } - @Platforms(Platform.HOSTED_ONLY.class) - public void addVMOperations(Collection> vmOps) { - vmOperations = vmOps.toArray(new Class[vmOps.size()]); + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public String getName() { + return name; } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public int getVMOperationId(Class clazz) { - for (int id = 0; id < vmOperations.length; id++) { - if (vmOperations[id] == clazz) { - return id + 1; // id starts with 1 - } - } - return 0; + public boolean getCausesSafepoint() { + return SystemEffect.getCausesSafepoint(systemEffect); } - public Class[] getVMOperations() { - return vmOperations; + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public boolean isBlocking() { + return true; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java new file mode 100644 index 000000000000..0c20e9f7901d --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022, 2022, 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.heap; + +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Objects; + +import com.oracle.svm.core.thread.VMOperation; +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.thread.VMOperation.SystemEffect; + +/** + * This class is used to ensure that all VM operation names live in the image heap. It also keeps + * track of all VM operation names that are present in the image. + * + * The VM operation names are user facing as they are for example used in JFR events. + */ +public final class VMOperationInfos { + @Platforms(Platform.HOSTED_ONLY.class) private static final HashMap HostedMap = new HashMap<>(); + @UnknownObjectField(types = VMOperationInfos[].class) static String[] Names = new String[0]; + + @Fold + public static VMOperationInfos singleton() { + return ImageSingletons.lookup(VMOperationInfos.class); + } + + /** + * All arguments must be constants. If this isn't the case, then the image build will fail + * because the HostedMap would need to be reachable at run-time. + */ + @Fold + public static VMOperationInfo get(Class clazz, String name, SystemEffect systemEffect) { + synchronized (HostedMap) { + VMOperationKey key = new VMOperationKey(clazz, name); + VMOperationInfo result = HostedMap.get(key); + if (result == null) { + // Generate a unique id per (clazz, name) tuple. + int id = HostedMap.size(); + result = new VMOperationInfo(id, name, systemEffect); + HostedMap.put(key, result); + } + return result; + } + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static void cacheNames() { + Names = new String[HostedMap.size()]; + for (Entry entry : HostedMap.entrySet()) { + Names[entry.getValue().getId()] = entry.getKey().getName(); + } + } + + public static String[] getNames() { + return Names; + } + + private static class VMOperationKey { + private final Class clazz; + private final String name; + + VMOperationKey(Class clazz, String name) { + this.clazz = clazz; + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VMOperationKey that = (VMOperationKey) o; + return Objects.equals(clazz, that.clazz) && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(clazz, name); + } + } +} + +@AutomaticFeature +class VMOperationNamesFeatures implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(VMOperationInfos.class, new VMOperationInfos()); + } + + @Override + public void beforeCompilation(BeforeCompilationAccess access) { + VMOperationInfos.cacheNames(); + access.registerAsImmutable(VMOperationInfos.getNames()); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkWriter.java index 29ac182de361..6b2c77238b60 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkWriter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkWriter.java @@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.locks.ReentrantLock; +import com.oracle.svm.core.heap.VMOperationInfos; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.nativeimage.IsolateThread; @@ -153,7 +154,7 @@ public void closeFile(byte[] metadataDescriptor, JfrConstantPool[] repositories) JfrChangeEpochOperation op = new JfrChangeEpochOperation(); op.enqueue(); - /** + /* * After changing the epoch, all subsequently triggered JFR events will be recorded into the * data structures of the new epoch. This guarantees that the data in the old epoch can be * persisted to a file without a safepoint. @@ -366,7 +367,7 @@ public enum StringEncoding { private class JfrChangeEpochOperation extends JavaVMOperation { protected JfrChangeEpochOperation() { - super("JFR change epoch", SystemEffect.SAFEPOINT); + super(VMOperationInfos.get(JfrChangeEpochOperation.class, "JFR change epoch", SystemEffect.SAFEPOINT)); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java index 0b5aa0a2b458..b68f73f4ef83 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrTypeRepository.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; +import com.oracle.svm.core.heap.VMOperationInfos; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -227,14 +228,13 @@ private static int writeGCNames(JfrChunkWriter writer) { } private static int writeVMOperations(JfrChunkWriter writer) { - Class[] vmOperations = JfrVMOperations.singleton().getVMOperations(); - - assert vmOperations.length > 0; + String[] vmOperationNames = VMOperationInfos.getNames(); + assert vmOperationNames.length > 0; writer.writeCompressedLong(JfrType.VMOperation.getId()); - writer.writeCompressedLong(vmOperations.length); - for (int id = 0; id < vmOperations.length; id++) { - writer.writeCompressedLong(id + 1); // id starts with 1 - writer.writeString(vmOperations[id].getCanonicalName()); + writer.writeCompressedLong(vmOperationNames.length); + for (int id = 0; id < vmOperationNames.length; id++) { + writer.writeCompressedLong(id + 1); // id starts with 1 + writer.writeString(vmOperationNames[id]); } return NON_EMPTY; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java index 37a4900bc1f3..ba6ad94fbd01 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java @@ -27,7 +27,6 @@ import java.lang.reflect.Field; import java.util.List; -import com.oracle.svm.core.jfr.logging.JfrLogging; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.nativeimage.ImageSingletons; @@ -38,10 +37,12 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.VMOperationInfos; +import com.oracle.svm.core.jfr.events.ExecutionSampleEvent; +import com.oracle.svm.core.jfr.logging.JfrLogging; import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.ThreadListener; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.jfr.events.ExecutionSampleEvent; import jdk.jfr.Configuration; import jdk.jfr.internal.EventWriter; @@ -281,20 +282,15 @@ public void beginRecording() { chunkWriter.unlock(); } - JavaVMOperation.enqueueBlockingSafepoint("Jfr begin recording", () -> { - recording = true; - SubstrateJVM.getThreadRepo().registerRunningThreads(); - // After changing the value of recording to true, JFR events can be triggered at any - // time. - }); + JfrBeginRecordingOperation vmOp = new JfrBeginRecordingOperation(); + vmOp.enqueue(); } /** See {@link JVM#endRecording}. */ public void endRecording() { assert recording; - JavaVMOperation.enqueueBlockingSafepoint("JFR end recording", () -> recording = false); - // After the safepoint, it is guaranteed that all JfrNativeEventWriters finished their job - // and that no further JFR events will be triggered. + JfrEndRecordingOperation vmOp = new JfrEndRecordingOperation(); + vmOp.enqueue(); } /** See {@link JVM#isRecording}. This is not thread safe */ @@ -510,4 +506,31 @@ public boolean setCutoff(long eventTypeId, long cutoffTicks) { eventSettings[NumUtil.safeToInt(eventTypeId)].setCutoffTicks(cutoffTicks); return true; } + + private static class JfrBeginRecordingOperation extends JavaVMOperation { + JfrBeginRecordingOperation() { + super(VMOperationInfos.get(JfrBeginRecordingOperation.class, "JFR begin recording", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + SubstrateJVM.get().recording = true; + SubstrateJVM.getThreadRepo().registerRunningThreads(); + // After changing the value of recording to true, JFR events can be triggered at any + // time. + } + } + + private static class JfrEndRecordingOperation extends JavaVMOperation { + JfrEndRecordingOperation() { + super(VMOperationInfos.get(JfrEndRecordingOperation.class, "JFR end recording", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + SubstrateJVM.get().recording = false; + // After the safepoint, it is guaranteed that all JfrNativeEventWriters finished their + // job and that no further JFR events will be triggered. + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java index 98629ac4287c..d88a45667e72 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecuteVMOperationEvent.java @@ -26,21 +26,20 @@ package com.oracle.svm.core.jfr.events; +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.StackValue; + import com.oracle.svm.core.annotate.Uninterruptible; -import com.oracle.svm.core.jfr.JfrEvent; import com.oracle.svm.core.jfr.HasJfrSupport; +import com.oracle.svm.core.jfr.JfrEvent; import com.oracle.svm.core.jfr.JfrNativeEventWriter; import com.oracle.svm.core.jfr.JfrNativeEventWriterData; import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; import com.oracle.svm.core.jfr.JfrTicks; -import com.oracle.svm.core.jfr.JfrVMOperations; import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.thread.Safepoint; import com.oracle.svm.core.thread.VMOperation; -import org.graalvm.nativeimage.IsolateThread; -import org.graalvm.nativeimage.StackValue; - public class ExecuteVMOperationEvent { @Uninterruptible(reason = "Accesses a JFR buffer.") public static void emit(VMOperation vmOperation, IsolateThread requestingThread, long startTicks) { @@ -49,8 +48,6 @@ public static void emit(VMOperation vmOperation, IsolateThread requestingThread, } if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.ExecuteVMOperation)) { - int id = JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass()); - assert id != 0; JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); JfrNativeEventWriter.beginEventWrite(data, false); @@ -58,9 +55,9 @@ public static void emit(VMOperation vmOperation, IsolateThread requestingThread, JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, JfrVMOperations.singleton().getVMOperationId(vmOperation.getClass())); + JfrNativeEventWriter.putLong(data, vmOperation.getId() + 1); // id starts with 1 JfrNativeEventWriter.putBoolean(data, vmOperation.getCausesSafepoint()); - JfrNativeEventWriter.putBoolean(data, true); // Blocking + JfrNativeEventWriter.putBoolean(data, vmOperation.isBlocking()); JfrNativeEventWriter.putThread(data, requestingThread); JfrNativeEventWriter.putLong(data, vmOperation.getCausesSafepoint() ? Safepoint.Master.singleton().getSafepointId().rawValue() : 0); JfrNativeEventWriter.endEventWrite(data, false); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Continuation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Continuation.java index 82a6470c9a8c..4f89215c198f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Continuation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Continuation.java @@ -31,11 +31,11 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.heap.StoredContinuation; import com.oracle.svm.core.heap.StoredContinuationImpl; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.util.VMError; @@ -131,9 +131,9 @@ private void enter0() { } int tryPreempt(Thread thread) { - TryPreemptThunk thunk = new TryPreemptThunk(this, thread); - JavaVMOperation.enqueueBlockingSafepoint("tryForceYield0", thunk); - return thunk.preemptStatus; + TryPreemptOperation vmOp = new TryPreemptOperation(this, thread); + vmOp.enqueue(); + return vmOp.preemptStatus; } @NeverInline("access stack pointer") @@ -175,19 +175,20 @@ public void finish() { assert isEmpty(); } - static class TryPreemptThunk implements SubstrateUtil.Thunk { + static class TryPreemptOperation extends JavaVMOperation { int preemptStatus = YIELD_SUCCESS; final Continuation cont; final Thread thread; - TryPreemptThunk(Continuation cont, Thread thread) { + TryPreemptOperation(Continuation cont, Thread thread) { + super(VMOperationInfos.get(TryPreemptOperation.class, "Try to preempt continuation", SystemEffect.SAFEPOINT)); this.cont = cont; this.thread = thread; } @Override - public void invoke() { + public void operate() { IsolateThread vmThread = PlatformThreads.getIsolateThread(thread); Pointer rootSP = cont.sp; CodePointer rootIP = cont.ip; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java index 437595154d9f..5d7a7631e7bb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java @@ -28,9 +28,9 @@ import com.oracle.svm.core.SubstrateOptions.ConcealedOptions; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.SubstrateUtil.Thunk; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.VMOperationInfo; import com.oracle.svm.core.jdk.SplittableRandomAccessors; import com.oracle.svm.core.util.VMError; @@ -52,8 +52,8 @@ public abstract class JavaVMOperation extends VMOperation implements VMOperation private JavaVMOperation next; private volatile boolean finished; - protected JavaVMOperation(String name, SystemEffect systemEffect) { - super(name, systemEffect); + protected JavaVMOperation(VMOperationInfo info) { + super(info); /* * Calling SplittableRandomAccessors#getDefaultGen() here to prevent * SplittableRandomAccessors#initialize synchronized method call inside VMOperation lock, @@ -108,18 +108,6 @@ protected boolean hasWork() { return true; } - /** Convenience method for thunks that can be run by allocating a VMOperation. */ - public static void enqueueBlockingSafepoint(String name, Thunk thunk) { - ThunkOperation vmOperation = new ThunkOperation(name, SystemEffect.SAFEPOINT, thunk); - vmOperation.enqueue(); - } - - /** Convenience method for thunks that can be run by allocating a VMOperation. */ - public static void enqueueBlockingNoSafepoint(String name, Thunk thunk) { - ThunkOperation vmOperation = new ThunkOperation(name, SystemEffect.NONE, thunk); - vmOperation.enqueue(); - } - @Override public final void operate(NativeVMOperationData data) { operate(); @@ -127,19 +115,4 @@ public final void operate(NativeVMOperationData data) { @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Whitelisted because some operations may allocate.") protected abstract void operate(); - - /** A VMOperation that executes a thunk. */ - public static class ThunkOperation extends JavaVMOperation { - private Thunk thunk; - - ThunkOperation(String name, SystemEffect systemEffect, Thunk thunk) { - super(name, systemEffect); - this.thunk = thunk; - } - - @Override - public void operate() { - thunk.invoke(); - } - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java index a0c27be85a06..c4c510a6ffdb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java @@ -29,6 +29,7 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.VMOperationInfo; /** * An immutable VM operation that lives in the image heap. All mutable state is kept in native @@ -37,8 +38,8 @@ */ public abstract class NativeVMOperation extends VMOperation { @Platforms(value = Platform.HOSTED_ONLY.class) - protected NativeVMOperation(String name, SystemEffect systemEffect) { - super(name, systemEffect); + protected NativeVMOperation(VMOperationInfo info) { + super(info); } public void enqueue(NativeVMOperationData data) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java index 977511ab0351..acc4dc7048a0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java @@ -64,6 +64,7 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ReferenceHandler; import com.oracle.svm.core.heap.ReferenceHandlerThread; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.StackTraceUtils; import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.locks.VMMutex; @@ -690,25 +691,16 @@ protected static void wakeUpVMConditionWaiters(Thread thread) { static StackTraceElement[] getStackTrace(Thread thread) { assert !isVirtual(thread); - StackTraceElement[][] result = new StackTraceElement[1][0]; - JavaVMOperation.enqueueBlockingSafepoint("getStackTrace", () -> { - if (thread.isAlive()) { - result[0] = getStackTrace(getIsolateThread(thread)); - } else { - result[0] = Target_java_lang_Thread.EMPTY_STACK_TRACE; - } - }); - return result[0]; + + GetStackTraceOperation vmOp = new GetStackTraceOperation(thread); + vmOp.enqueue(); + return vmOp.result; } static Map getAllStackTraces() { - Map result = new HashMap<>(); - JavaVMOperation.enqueueBlockingSafepoint("getAllStackTraces", () -> { - for (IsolateThread cur = VMThreads.firstThread(); cur.isNonNull(); cur = VMThreads.nextThread(cur)) { - result.put(PlatformThreads.fromVMThread(cur), getStackTrace(cur)); - } - }); - return result; + GetAllStackTracesOperation vmOp = new GetAllStackTracesOperation(); + vmOp.enqueue(); + return vmOp.result; } @NeverInline("Starting a stack walk in the caller frame") @@ -869,6 +861,41 @@ static ThreadData getCurrentThreadData() { return (ThreadData) toTarget(Thread.currentThread()).threadData; } + private static class GetStackTraceOperation extends JavaVMOperation { + private final Thread thread; + private StackTraceElement[] result; + + GetStackTraceOperation(Thread thread) { + super(VMOperationInfos.get(GetStackTraceOperation.class, "Get stack trace", SystemEffect.SAFEPOINT)); + this.thread = thread; + } + + @Override + protected void operate() { + if (thread.isAlive()) { + result = getStackTrace(getIsolateThread(thread)); + } else { + result = Target_java_lang_Thread.EMPTY_STACK_TRACE; + } + } + } + + private static class GetAllStackTracesOperation extends JavaVMOperation { + private final Map result; + + GetAllStackTracesOperation() { + super(VMOperationInfos.get(GetAllStackTracesOperation.class, "Get all stack traces", SystemEffect.SAFEPOINT)); + result = new HashMap<>(); + } + + @Override + protected void operate() { + for (IsolateThread cur = VMThreads.firstThread(); cur.isNonNull(); cur = VMThreads.nextThread(cur)) { + result.put(PlatformThreads.fromVMThread(cur), getStackTrace(cur)); + } + } + } + /** * Builds a list of all application threads. This must be done in a VM operation because only * there we are allowed to allocate Java memory while holding the {@link VMThreads#THREAD_MUTEX} @@ -877,7 +904,7 @@ private static class FetchApplicationThreadsOperation extends JavaVMOperation { private final List list; FetchApplicationThreadsOperation(List list) { - super("FetchApplicationThreads", SystemEffect.NONE); + super(VMOperationInfos.get(FetchApplicationThreadsOperation.class, "Fetch application threads", SystemEffect.NONE)); this.list = list; } @@ -912,7 +939,7 @@ private static class CheckReadyForTearDownOperation extends JavaVMOperation { private boolean readyForTearDown; CheckReadyForTearDownOperation(Log trace, AtomicBoolean printLaggards) { - super("CheckReadyForTearDown", SystemEffect.NONE); + super(VMOperationInfos.get(CheckReadyForTearDownOperation.class, "Check ready for teardown", SystemEffect.NONE)); this.trace = trace; this.printLaggards = printLaggards; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java index dcef8d6c4b2f..4fcfe8184872 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java @@ -31,6 +31,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.VMOperationInfo; import com.oracle.svm.core.jfr.JfrTicks; import com.oracle.svm.core.jfr.events.ExecuteVMOperationEvent; import com.oracle.svm.core.log.Log; @@ -44,17 +45,20 @@ * unexpected exceptions while executing critical code. */ public abstract class VMOperation { - private final String name; - private final SystemEffect systemEffect; + private final VMOperationInfo info; - protected VMOperation(String name, SystemEffect systemEffect) { - this.name = name; - this.systemEffect = systemEffect; + protected VMOperation(VMOperationInfo info) { + this.info = info; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public final int getId() { + return info.getId(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public final String getName() { - return name; + return info.getName(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -64,7 +68,12 @@ protected boolean isGC() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public final boolean getCausesSafepoint() { - return SystemEffect.getCausesSafepoint(systemEffect); + return info.getCausesSafepoint(); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public final boolean isBlocking() { + return info.isBlocking(); } protected final void execute(NativeVMOperationData data) { @@ -77,7 +86,7 @@ protected final void execute(NativeVMOperationData data) { * The caller already does some filtering but it can still happen that we reach this * code even though no work needs to be done. */ - trace.string("[Skipping operation ").string(name).string("]"); + trace.string("[Skipping operation ").string(getName()).string("]"); return; } @@ -90,7 +99,7 @@ protected final void execute(NativeVMOperationData data) { control.setInProgress(this, requestingThread, CurrentIsolate.getCurrentThread(), true); long startTicks = JfrTicks.elapsedTicks(); try { - trace.string("[Executing operation ").string(name); + trace.string("[Executing operation ").string(getName()); operate(data); trace.string("]"); } catch (Throwable t) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java index 64c8475e7d09..0ebdb0a05799 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java @@ -29,8 +29,6 @@ import java.util.Collections; import java.util.List; -import com.oracle.svm.core.SubstrateOptions.ConcealedOptions; -import com.oracle.svm.core.thread.VMThreads.SafepointBehavior; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; @@ -43,18 +41,21 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateOptions.ConcealedOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.RestrictHeapAccess.Access; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.locks.VMCondition; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.nodes.CFunctionEpilogueNode; import com.oracle.svm.core.nodes.CFunctionPrologueNode; import com.oracle.svm.core.stack.StackOverflowCheck; +import com.oracle.svm.core.thread.VMThreads.SafepointBehavior; import com.oracle.svm.core.thread.VMThreads.StatusSupport; import com.oracle.svm.core.util.RingBuffer; import com.oracle.svm.core.util.VMError; @@ -139,13 +140,23 @@ public static void startVMOperationThread() { public static void shutdownAndDetachVMOperationThread() { assert useDedicatedVMOperationThread(); - VMOperationControl control = get(); - JavaVMOperation.enqueueBlockingNoSafepoint("Stop VMOperationThread", () -> { - control.dedicatedVMOperationThread.shutdown(); - }); + + StopVMOperationThread vmOp = new StopVMOperationThread(); + vmOp.enqueue(); waitUntilVMOperationThreadDetached(); - assert control.mainQueues.isEmpty(); + assert get().mainQueues.isEmpty(); + } + + private static class StopVMOperationThread extends JavaVMOperation { + StopVMOperationThread() { + super(VMOperationInfos.get(StopVMOperationThread.class, "Stop VM operation thread", VMOperation.SystemEffect.NONE)); + } + + @Override + protected void operate() { + VMOperationControl.get().dedicatedVMOperationThread.shutdown(); + } } @RestrictHeapAccess(access = Access.NO_ALLOCATION, reason = "Called during teardown") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index 7835fda07d2a..abc4ad61f737 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java @@ -47,6 +47,7 @@ import com.oracle.svm.core.c.function.CEntryPointErrors; import com.oracle.svm.core.c.function.CFunctionOptions; import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicWord; import com.oracle.svm.core.locks.VMCondition; @@ -463,21 +464,8 @@ private static void cleanupBeforeDetach(IsolateThread thread) { * considering the immediately following tear-down. */ public void detachAllThreadsExceptCurrentWithoutCleanupForTearDown() { - JavaVMOperation.enqueueBlockingSafepoint("detachAllThreadsExceptCurrent", () -> { - IsolateThread currentThread = CurrentIsolate.getCurrentThread(); - IsolateThread thread = firstThread(); - while (thread.isNonNull()) { - IsolateThread next = nextThread(thread); - if (thread.notEqual(currentThread)) { - Thread javaThread = PlatformThreads.fromVMThread(thread); - if (!PlatformThreads.wasStartedByCurrentIsolate(javaThread)) { - detachThreadInSafeContext(thread); - releaseThread(thread); - } - } - thread = next; - } - }); + DetachAllThreadsExceptCurrentOperation vmOp = new DetachAllThreadsExceptCurrentOperation(); + vmOp.enqueue(); } /** @@ -608,6 +596,29 @@ public static boolean printLocationInfo(Log log, UnsignedWord value, boolean all return false; } + private static class DetachAllThreadsExceptCurrentOperation extends JavaVMOperation { + DetachAllThreadsExceptCurrentOperation() { + super(VMOperationInfos.get(DetachAllThreadsExceptCurrentOperation.class, "Detach all threads except current", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + IsolateThread currentThread = CurrentIsolate.getCurrentThread(); + IsolateThread thread = firstThread(); + while (thread.isNonNull()) { + IsolateThread next = nextThread(thread); + if (thread.notEqual(currentThread)) { + Thread javaThread = PlatformThreads.fromVMThread(thread); + if (!PlatformThreads.wasStartedByCurrentIsolate(javaThread)) { + detachThreadInSafeContext(thread); + releaseThread(thread); + } + } + thread = next; + } + } + } + /* * Access to platform-specific implementations. */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java index 6164305c3a43..a20db7c5a48f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrFeature.java @@ -25,8 +25,6 @@ package com.oracle.svm.hosted.jfr; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -46,16 +44,15 @@ import com.oracle.svm.core.jdk.RuntimeSupport; import com.oracle.svm.core.jfr.JfrChunkWriter; import com.oracle.svm.core.jfr.JfrFrameTypeSerializer; +import com.oracle.svm.core.jfr.JfrGCNames; import com.oracle.svm.core.jfr.JfrGlobalMemory; import com.oracle.svm.core.jfr.JfrManager; -import com.oracle.svm.core.jfr.JfrGCNames; import com.oracle.svm.core.jfr.JfrNativeEventWriter; import com.oracle.svm.core.jfr.JfrNativeEventWriterData; import com.oracle.svm.core.jfr.JfrRecorderThread; import com.oracle.svm.core.jfr.JfrSerializerSupport; import com.oracle.svm.core.jfr.JfrThreadLocal; import com.oracle.svm.core.jfr.JfrThreadStateSerializer; -import com.oracle.svm.core.jfr.JfrVMOperations; import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.traceid.JfrTraceId; import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch; @@ -63,12 +60,10 @@ import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.thread.ThreadListenerFeature; import com.oracle.svm.core.thread.ThreadListenerSupport; -import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; -import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import com.sun.management.HotSpotDiagnosticMXBean; @@ -184,7 +179,6 @@ public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(JfrTraceIdMap.class, new JfrTraceIdMap()); ImageSingletons.add(JfrTraceIdEpoch.class, new JfrTraceIdEpoch()); ImageSingletons.add(JfrGCNames.class, new JfrGCNames()); - ImageSingletons.add(JfrVMOperations.class, new JfrVMOperations()); JfrSerializerSupport.get().register(new JfrFrameTypeSerializer()); JfrSerializerSupport.get().register(new JfrThreadStateSerializer()); @@ -234,23 +228,12 @@ public void beforeCompilation(BeforeCompilationAccess a) { // Scan all classes and build sets of packages, modules and class-loaders. Count all items. Collection types = ((FeatureImpl.CompilationAccessImpl) a).getTypes(); - Collection> vmOperations = new ArrayList<>(); for (SharedType type : types) { DynamicHub hub = type.getHub(); Class clazz = hub.getHostedJavaClass(); // Off-set by one for error-catcher JfrTraceId.assign(clazz, hub.getTypeID() + 1); - - // Capture concrete VMOperation types for ExecuteVMOperationEvent - HostedType ht = (HostedType) type; - if (VMOperation.class.isAssignableFrom(clazz) && - !clazz.isInterface() && - !Modifier.isAbstract(clazz.getModifiers()) && - ht.getWrapped().isReachable()) { - vmOperations.add(clazz); - } } - ImageSingletons.lookup(JfrVMOperations.class).addVMOperations(vmOperations); } private static void eventSubtypeReachable(DuringAnalysisAccess a, Class c) { From 8b1c4751761c65314588001cb62430f2e21031f5 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Tue, 8 Mar 2022 13:44:15 +0100 Subject: [PATCH 5/8] Update changelog. --- substratevm/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 7b7183834784..1a39c49065da 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -6,7 +6,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-36568) Add "Quick build" mode, enabled through option `-Ob`, for quicker native image builds. * (GR-35898) Improved handling of static synchronized methods: the lock is no longer stored in the secondary monitor map, but in the mutable DynamicHubCompanion object. * Remove support for JDK8. As a result, `JDK8OrEarlier` and `JDK11OrLater` have been deprecated and will be removed in a future release. -* (GR-26814) (GR-37018) (GR-37038) Red Hat added support for the following JFR events: `SafepointBegin`, `SafepointEnd`, `GarbageCollection`, `GCPhasePause`, and `GCPhasePauseLevel*`. All GC-related JFR events are currently limited to the serial GC. +* (GR-26814) (GR-37018) (GR-37038) (GR-GR-37311) Red Hat added support for the following JFR events: `SafepointBegin`, `SafepointEnd`, `GarbageCollection`, `GCPhasePause`, `GCPhasePauseLevel*`, and `ExecuteVMOperation`. All GC-related JFR events are currently limited to the serial GC. ## Version 22.0.0 * (GR-33930) Decouple HostedOptionParser setup from classpath/modulepath scanning (use ServiceLoader for collecting options). From 67f30bd9f52db82f024ac599aacc8738afa13ac6 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Tue, 8 Mar 2022 14:48:32 +0100 Subject: [PATCH 6/8] Minor fixes. --- .../src/com/oracle/svm/core/code/CodeInfoTable.java | 5 ++--- .../src/com/oracle/svm/core/heap/VMOperationInfo.java | 11 ++++++++++- .../com/oracle/svm/core/heap/VMOperationInfos.java | 4 ++-- .../src/com/oracle/svm/core/thread/VMOperation.java | 1 + 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 4b0e3fcf20e9..1dad5b64a155 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -27,9 +27,6 @@ import java.util.Arrays; import java.util.List; -import com.oracle.svm.core.heap.ReferenceMapIndex; -import com.oracle.svm.core.heap.VMOperationInfos; -import com.oracle.svm.core.thread.VMOperation.SystemEffect; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; @@ -48,6 +45,8 @@ import com.oracle.svm.core.deopt.SubstrateInstalledCode; import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.ObjectReferenceVisitor; +import com.oracle.svm.core.heap.ReferenceMapIndex; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.thread.JavaVMOperation; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java index 192274c3f1d9..205696fb9216 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfo.java @@ -25,15 +25,18 @@ package com.oracle.svm.core.heap; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMOperation.SystemEffect; public final class VMOperationInfo { + private final Class clazz; private final int id; private final String name; private final SystemEffect systemEffect; - VMOperationInfo(int id, String name, SystemEffect systemEffect) { + VMOperationInfo(int id, Class clazz, String name, SystemEffect systemEffect) { this.id = id; + this.clazz = clazz; this.name = name; this.systemEffect = systemEffect; } @@ -43,6 +46,11 @@ public int getId() { return id; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public Class getVMOperationClass() { + return clazz; + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public String getName() { return name; @@ -54,6 +62,7 @@ public boolean getCausesSafepoint() { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @SuppressWarnings("static-method") public boolean isBlocking() { return true; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java index 0c20e9f7901d..31d395b04539 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java @@ -47,7 +47,7 @@ */ public final class VMOperationInfos { @Platforms(Platform.HOSTED_ONLY.class) private static final HashMap HostedMap = new HashMap<>(); - @UnknownObjectField(types = VMOperationInfos[].class) static String[] Names = new String[0]; + @UnknownObjectField(types = String[].class) static String[] Names = new String[0]; @Fold public static VMOperationInfos singleton() { @@ -66,7 +66,7 @@ public static VMOperationInfo get(Class clazz, String nam if (result == null) { // Generate a unique id per (clazz, name) tuple. int id = HostedMap.size(); - result = new VMOperationInfo(id, name, systemEffect); + result = new VMOperationInfo(id, clazz, name, systemEffect); HostedMap.put(key, result); } return result; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java index 4fcfe8184872..bf63d43a3c74 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java @@ -48,6 +48,7 @@ public abstract class VMOperation { private final VMOperationInfo info; protected VMOperation(VMOperationInfo info) { + assert info.getVMOperationClass() == this.getClass(); this.info = info; } From 8bab4195fd95695556cf2cc6a48a5d4d47d8df10 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Wed, 23 Mar 2022 15:17:35 +0100 Subject: [PATCH 7/8] Mark JavaVMOperation.enqueueBlockingSafepoint as deprecated. --- .../svm/core/thread/JavaVMOperation.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java index 5d7a7631e7bb..23079aa0eb9a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java @@ -31,6 +31,7 @@ import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.heap.VMOperationInfo; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.SplittableRandomAccessors; import com.oracle.svm.core.util.VMError; @@ -108,6 +109,13 @@ protected boolean hasWork() { return true; } + /** Deprecated: use a dedicated {@link JavaVMOperation} subclass instead. */ + @Deprecated(forRemoval = true) + public static void enqueueBlockingSafepoint(@SuppressWarnings("unused") String name, SubstrateUtil.Thunk thunk) { + ThunkOperation vmOperation = new ThunkOperation(thunk); + vmOperation.enqueue(); + } + @Override public final void operate(NativeVMOperationData data) { operate(); @@ -115,4 +123,20 @@ public final void operate(NativeVMOperationData data) { @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Whitelisted because some operations may allocate.") protected abstract void operate(); + + /** Deprecated: use a dedicated {@link JavaVMOperation} subclass instead. */ + @Deprecated(forRemoval = true) + private static class ThunkOperation extends JavaVMOperation { + private SubstrateUtil.Thunk thunk; + + ThunkOperation(SubstrateUtil.Thunk thunk) { + super(VMOperationInfos.get(ThunkOperation.class, "Unnamed VM operation", SystemEffect.SAFEPOINT)); + this.thunk = thunk; + } + + @Override + public void operate() { + thunk.invoke(); + } + } } From 452eeef6b674f3599a14a1ba3693b8b3321fa08c Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Wed, 23 Mar 2022 15:18:46 +0100 Subject: [PATCH 8/8] Minor renaming. --- .../svm/core/heap/VMOperationInfos.java | 20 +++++++++---------- .../com/oracle/svm/core/thread/VMThreads.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java index 31d395b04539..4e98af982996 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/VMOperationInfos.java @@ -46,8 +46,8 @@ * The VM operation names are user facing as they are for example used in JFR events. */ public final class VMOperationInfos { - @Platforms(Platform.HOSTED_ONLY.class) private static final HashMap HostedMap = new HashMap<>(); - @UnknownObjectField(types = String[].class) static String[] Names = new String[0]; + @Platforms(Platform.HOSTED_ONLY.class) private static final HashMap hostedMap = new HashMap<>(); + @UnknownObjectField(types = String[].class) static String[] names = new String[0]; @Fold public static VMOperationInfos singleton() { @@ -60,14 +60,14 @@ public static VMOperationInfos singleton() { */ @Fold public static VMOperationInfo get(Class clazz, String name, SystemEffect systemEffect) { - synchronized (HostedMap) { + synchronized (hostedMap) { VMOperationKey key = new VMOperationKey(clazz, name); - VMOperationInfo result = HostedMap.get(key); + VMOperationInfo result = hostedMap.get(key); if (result == null) { // Generate a unique id per (clazz, name) tuple. - int id = HostedMap.size(); + int id = hostedMap.size(); result = new VMOperationInfo(id, clazz, name, systemEffect); - HostedMap.put(key, result); + hostedMap.put(key, result); } return result; } @@ -75,14 +75,14 @@ public static VMOperationInfo get(Class clazz, String nam @Platforms(Platform.HOSTED_ONLY.class) public static void cacheNames() { - Names = new String[HostedMap.size()]; - for (Entry entry : HostedMap.entrySet()) { - Names[entry.getValue().getId()] = entry.getKey().getName(); + names = new String[hostedMap.size()]; + for (Entry entry : hostedMap.entrySet()) { + names[entry.getValue().getId()] = entry.getKey().getName(); } } public static String[] getNames() { - return Names; + return names; } private static class VMOperationKey { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index 7f3247766c9d..c4aab9dbf5dc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java @@ -476,7 +476,7 @@ private static void cleanupBeforeDetach(IsolateThread thread) { * cleanup code needs to run in the detaching thread itself. We assume that this is tolerable * considering the immediately following tear-down. */ - public void detachAllThreadsExceptCurrentWithoutCleanupForTearDown() { + public static void detachAllThreadsExceptCurrentWithoutCleanupForTearDown() { DetachAllThreadsExceptCurrentOperation vmOp = new DetachAllThreadsExceptCurrentOperation(); vmOp.enqueue(); }