From b4ad385e8cdd73c0a8e3e620969f1b7f8ce0feff Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Wed, 4 Jan 2023 15:31:20 -0500 Subject: [PATCH 1/8] add monitor inflation event --- .../src/com/oracle/svm/core/jfr/JfrEvent.java | 1 + .../com/oracle/svm/core/jfr/JfrFeature.java | 1 + .../svm/core/jfr/JfrInflationCause.java | 62 +++++++++++++++++ .../core/jfr/JfrInflationCauseSerializer.java | 54 +++++++++++++++ .../src/com/oracle/svm/core/jfr/JfrType.java | 3 +- .../jfr/events/JavaMonitorInflateEvent.java | 66 +++++++++++++++++++ .../svm/core/jni/functions/JNIFunctions.java | 7 +- .../svm/core/monitor/MonitorSupport.java | 3 + .../monitor/MultiThreadedMonitorSupport.java | 49 ++++++++++---- .../monitor/SingleThreadedMonitorSupport.java | 10 ++- 10 files changed, 238 insertions(+), 18 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.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 3635233cfd5c..8fb9e575b793 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 @@ -63,6 +63,7 @@ public final class JfrEvent { public static final JfrEvent ThreadSleep = create("jdk.ThreadSleep", JDK17OrEarlier.class); public static final JfrEvent ThreadPark = create("jdk.ThreadPark"); public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait"); + public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate"); private final long id; private final String name; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java index 405cc7027057..eae3adcb4e1a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java @@ -163,6 +163,7 @@ public void afterRegistration(AfterRegistrationAccess access) { JfrSerializerSupport.get().register(new JfrFrameTypeSerializer()); JfrSerializerSupport.get().register(new JfrThreadStateSerializer()); + JfrSerializerSupport.get().register(new JfrInflationCauseSerializer()); ThreadListenerSupport.get().register(SubstrateJVM.getThreadLocal()); if (HOSTED_ENABLED) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java new file mode 100644 index 000000000000..410f58a6f22b --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.Uninterruptible; + +/** + * List of all possible inflation causes. + */ +public enum JfrInflationCause { + VM_INTERNAL("VM Internal"), + MONITOR_ENTER("Monitor Enter"), + WAIT("Monitor Wait"), + NOTIFY("Monitor Notify"), + HASH_CODE("Monitor Hash Code"), + JNI_ENTER("JNI Monitor Enter"), + JNI_EXIT("JNI Monitor Exit"); + + private final String text; + + @Platforms(Platform.HOSTED_ONLY.class) + JfrInflationCause(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public long getId() { + // First entry needs to have id 0. + return ordinal(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java new file mode 100644 index 000000000000..3464c4201f77 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +/** + * Used to serialize all possible inflation causes into the chunk. + */ +public class JfrInflationCauseSerializer implements JfrConstantPool { + + @Platforms(Platform.HOSTED_ONLY.class) + public JfrInflationCauseSerializer() { + } + + @Override + public int write(JfrChunkWriter writer) { + writer.writeCompressedLong(JfrType.InflationCause.getId()); + + JfrInflationCause[] inflationCauses = JfrInflationCause.values(); + writer.writeCompressedLong(inflationCauses.length); + for (int i = 0; i < inflationCauses.length; i++) { + writer.writeCompressedInt(i); + writer.writeString(inflationCauses[i].getText()); + } + + return NON_EMPTY; + } +} 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 b6294998bd17..2f05653350fb 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 @@ -42,7 +42,8 @@ public enum JfrType { FrameType("jdk.types.FrameType"), GCCause("jdk.types.GCCause"), GCName("jdk.types.GCName"), - VMOperation("jdk.types.VMOperationType"); + VMOperation("jdk.types.VMOperationType"), + InflationCause("jdk.types.InflateCause"); private final long id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java new file mode 100644 index 000000000000..a3cc6657337c --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.jfr.HasJfrSupport; +import org.graalvm.nativeimage.StackValue; + +import com.oracle.svm.core.Uninterruptible; +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.SubstrateJVM; +import com.oracle.svm.core.jfr.JfrInflationCause; +import org.graalvm.compiler.word.Word; + +public class JavaMonitorInflateEvent { + public static void emit(Object obj, long startTicks, JfrInflationCause cause) { + if (HasJfrSupport.get()) { + emit0(obj, startTicks, cause); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emit0(Object obj, long startTicks, JfrInflationCause cause) { + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.JavaMonitorInflate)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaMonitorInflate); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorInflate, 0)); + JfrNativeEventWriter.putClass(data, obj.getClass()); + JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); + JfrNativeEventWriter.putLong(data, cause.getId()); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java index e50288c04ae9..01eea859a2f9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java @@ -35,6 +35,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; +import com.oracle.svm.core.jfr.JfrInflationCause; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -1015,7 +1016,7 @@ static int MonitorEnter(JNIEnvironment env, JNIObjectHandle handle) { } boolean acquired = false; try { - MonitorSupport.singleton().monitorEnter(obj); + MonitorSupport.singleton().monitorEnter(obj, JfrInflationCause.JNI_ENTER); assert Thread.holdsLock(obj); acquired = true; @@ -1024,7 +1025,7 @@ static int MonitorEnter(JNIEnvironment env, JNIObjectHandle handle) { } catch (Throwable t) { try { if (acquired) { - MonitorSupport.singleton().monitorExit(obj); + MonitorSupport.singleton().monitorExit(obj, JfrInflationCause.JNI_EXIT); } if (pinned) { VirtualThreads.singleton().unpinCurrent(); @@ -1049,7 +1050,7 @@ static int MonitorExit(JNIEnvironment env, JNIObjectHandle handle) { if (!Thread.holdsLock(obj)) { throw new IllegalMonitorStateException(); } - MonitorSupport.singleton().monitorExit(obj); + MonitorSupport.singleton().monitorExit(obj, JfrInflationCause.JNI_EXIT); JNIThreadOwnedMonitors.exited(obj); if (VirtualThreads.isSupported() && JavaThreads.isCurrentThreadVirtual()) { try { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java index 23444cb249e7..40135611335a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java @@ -29,6 +29,7 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.thread.ThreadStatus; +import com.oracle.svm.core.jfr.JfrInflationCause; /** * This interface provides functions related to monitor operations (the Java "synchronized" keyword @@ -45,11 +46,13 @@ public static MonitorSupport singleton() { * Implements the semantics of the monitorenter bytecode. */ public abstract void monitorEnter(Object obj); + public abstract void monitorEnter(Object obj, JfrInflationCause cause); /** * Implements the semantics of the monitorexit bytecode. */ public abstract void monitorExit(Object obj); + public abstract void monitorExit(Object obj, JfrInflationCause cause); /* * Support for objects that are re-locked during deoptimization. This method is called when diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java index 5cb7a5a8c28f..44a882abb5e2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java @@ -54,6 +54,9 @@ import com.oracle.svm.core.thread.ThreadStatus; import com.oracle.svm.core.thread.VMOperationControl; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.jfr.events.JavaMonitorInflateEvent; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.JfrInflationCause; import jdk.internal.misc.Unsafe; @@ -239,7 +242,13 @@ private static void slowPathMonitorEnter(Object obj) { @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) @Override public void monitorEnter(Object obj) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true); + monitorEnter(obj, JfrInflationCause.MONITOR_ENTER); + } + + @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) + @Override + public void monitorEnter(Object obj, JfrInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); lockObject.monitorEnter(obj); } @@ -276,7 +285,13 @@ private static void slowPathMonitorExit(Object obj) { @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) @Override public void monitorExit(Object obj) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true); + monitorExit(obj, JfrInflationCause.VM_INTERNAL); + } + + @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) + @Override + public void monitorExit(Object obj, JfrInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); lockObject.monitorExit(); } @@ -325,7 +340,7 @@ protected void doWait(Object obj, long timeoutMillis) throws InterruptedExceptio * Ensure that the current thread holds the lock. Required by the specification of * Object.wait, and also required for our implementation. */ - JavaMonitor lock = ensureLocked(obj); + JavaMonitor lock = ensureLocked(obj, JfrInflationCause.WAIT); JavaMonitorConditionObject condition = lock.getOrCreateCondition(true); if (timeoutMillis == 0L) { condition.await(obj); @@ -337,7 +352,7 @@ protected void doWait(Object obj, long timeoutMillis) throws InterruptedExceptio @Override public void notify(Object obj, boolean notifyAll) { /* Make sure the current thread holds the lock on the receiver. */ - JavaMonitor lock = ensureLocked(obj); + JavaMonitor lock = ensureLocked(obj, JfrInflationCause.NOTIFY); /* Find the wait/notify condition of the receiver. */ JavaMonitorConditionObject condition = lock.getOrCreateCondition(false); /* If the receiver does not have a condition, then it has not been waited on. */ @@ -351,8 +366,8 @@ public void notify(Object obj, boolean notifyAll) { } /** Returns the lock of the object. */ - protected JavaMonitor ensureLocked(Object obj) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true); + protected JavaMonitor ensureLocked(Object obj, JfrInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); if (!lockObject.isHeldByCurrentThread()) { throw new IllegalMonitorStateException("Receiver is not locked by the current thread."); } @@ -376,42 +391,48 @@ protected static Object replaceObject(Object unreplacedObject) { } protected final JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotExisting) { + return getOrCreateMonitor(obj, createIfNotExisting, JfrInflationCause.VM_INTERNAL); + } + + protected final JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotExisting, JfrInflationCause cause) { int monitorOffset = getMonitorOffset(obj); if (monitorOffset != 0) { /* The common case: pointer to the monitor reserved in the object. */ - return getOrCreateMonitorFromObject(obj, createIfNotExisting, monitorOffset); + return getOrCreateMonitorFromObject(obj, createIfNotExisting, monitorOffset, cause); } else { - return getOrCreateMonitorSlow(obj, createIfNotExisting); + return getOrCreateMonitorSlow(obj, createIfNotExisting, cause); } } - private JavaMonitor getOrCreateMonitorSlow(Object unreplacedObject, boolean createIfNotExisting) { + private JavaMonitor getOrCreateMonitorSlow(Object unreplacedObject, boolean createIfNotExisting, JfrInflationCause cause) { Object replacedObject = replaceObject(unreplacedObject); if (replacedObject != unreplacedObject) { int monitorOffset = getMonitorOffset(replacedObject); if (monitorOffset != 0) { - return getOrCreateMonitorFromObject(replacedObject, createIfNotExisting, monitorOffset); + return getOrCreateMonitorFromObject(replacedObject, createIfNotExisting, monitorOffset, cause); } } /* No memory reserved for a lock in the object, fall back to secondary storage. */ - return getOrCreateMonitorFromMap(replacedObject, createIfNotExisting); + return getOrCreateMonitorFromMap(replacedObject, createIfNotExisting, cause); } - protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset) { + protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset, JfrInflationCause cause) { JavaMonitor existingMonitor = (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); if (existingMonitor != null || !createIfNotExisting) { return existingMonitor; } + long startTicks = JfrTicks.elapsedTicks(); /* Atomically put a new lock in place of the null at the monitorOffset. */ JavaMonitor newMonitor = newMonitorLock(); if (UNSAFE.compareAndSetObject(obj, monitorOffset, null, newMonitor)) { + JavaMonitorInflateEvent.emit(obj, startTicks, cause); return newMonitor; } /* We lost the race, use the lock some other thread installed. */ return (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); } - protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting) { + protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting, JfrInflationCause cause) { assert JavaVersionUtil.JAVA_SPEC > 17 || obj.getClass() != Target_java_lang_ref_ReferenceQueue_Lock.class : "ReferenceQueue.Lock must have a monitor field or we can deadlock accessing WeakIdentityHashMap below"; VMError.guarantee(!additionalMonitorsLock.isHeldByCurrentThread(), @@ -427,9 +448,11 @@ protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotE if (existingMonitor != null || !createIfNotExisting) { return existingMonitor; } + long startTicks = JfrTicks.elapsedTicks(); JavaMonitor newMonitor = newMonitorLock(); JavaMonitor previousEntry = additionalMonitors.put(obj, newMonitor); VMError.guarantee(previousEntry == null, "Replaced monitor in secondary storage map"); + JavaMonitorInflateEvent.emit(obj, startTicks, cause); return newMonitor; } finally { additionalMonitorsLock.unlock(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java index 7ada4e90ac9f..c097a903a08e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java @@ -26,6 +26,7 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.thread.ThreadStatus; +import com.oracle.svm.core.jfr.JfrInflationCause; /** * Without support for threads, there is no need for any monitor operations. @@ -36,11 +37,18 @@ public class SingleThreadedMonitorSupport extends MonitorSupport { public void monitorEnter(Object obj) { /* Synchronization is a no-op in single threaded mode. */ } - + @Override + public void monitorEnter(Object obj, JfrInflationCause cause) { + /* Synchronization is a no-op in single threaded mode. */ + } @Override public void monitorExit(Object obj) { /* Synchronization is a no-op in single threaded mode. */ } + @Override + public void monitorExit(Object obj, JfrInflationCause cause) { + /* Synchronization is a no-op in single threaded mode. */ + } @Override public Object prepareRelockObject(Object obj) { From 71bc1b3d97d53dc73a3e101aaa7b3e1a75301b50 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Wed, 4 Jan 2023 16:03:59 -0500 Subject: [PATCH 2/8] style --- .../src/com/oracle/svm/core/monitor/MonitorSupport.java | 2 ++ .../oracle/svm/core/monitor/SingleThreadedMonitorSupport.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java index 40135611335a..b99c32d7cfdf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java @@ -46,12 +46,14 @@ public static MonitorSupport singleton() { * Implements the semantics of the monitorenter bytecode. */ public abstract void monitorEnter(Object obj); + public abstract void monitorEnter(Object obj, JfrInflationCause cause); /** * Implements the semantics of the monitorexit bytecode. */ public abstract void monitorExit(Object obj); + public abstract void monitorExit(Object obj, JfrInflationCause cause); /* diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java index c097a903a08e..b16855c06cdf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java @@ -37,14 +37,17 @@ public class SingleThreadedMonitorSupport extends MonitorSupport { public void monitorEnter(Object obj) { /* Synchronization is a no-op in single threaded mode. */ } + @Override public void monitorEnter(Object obj, JfrInflationCause cause) { /* Synchronization is a no-op in single threaded mode. */ } + @Override public void monitorExit(Object obj) { /* Synchronization is a no-op in single threaded mode. */ } + @Override public void monitorExit(Object obj, JfrInflationCause cause) { /* Synchronization is a no-op in single threaded mode. */ From 2e63454fa99d61cd4a88805d142a08e2fbbca765 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 3 Feb 2023 10:56:46 -0500 Subject: [PATCH 3/8] add test --- .../jfr/events/JavaMonitorInflateEvent.java | 2 +- .../svm/test/jfr/utils/JfrFileParser.java | 2 + .../InflateCauseConstantPoolParser.java | 45 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java index a3cc6657337c..f154ab2322dc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java @@ -48,7 +48,7 @@ public static void emit(Object obj, long startTicks, JfrInflationCause cause) { @Uninterruptible(reason = "Accesses a JFR buffer.") public static void emit0(Object obj, long startTicks, JfrInflationCause cause) { - if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.JavaMonitorInflate)) { + if (JfrEvent.JavaMonitorInflate.shouldEmit()) { JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); 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 fbcac8ec2fa8..5d001148c2d1 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 @@ -51,6 +51,7 @@ 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 com.oracle.svm.test.jfr.utils.poolparsers.InflateCauseConstantPoolParser; import jdk.jfr.Recording; import org.junit.Assert; @@ -78,6 +79,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()); + supportedConstantPools.put(JfrType.InflationCause.getId(), new InflateCauseConstantPoolParser()); } public static HashMap getSupportedConstantPools() { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java new file mode 100644 index 000000000000..a8f192bc4709 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 java.io.IOException; + +import org.junit.Assert; + +import com.oracle.svm.test.jfr.utils.RecordingInput; + +public class InflateCauseConstantPoolParser extends ConstantPoolParser { + + @Override + public void parse(RecordingInput input) throws IOException { + int numberOfInflateCauses = input.readInt(); + for (int i = 0; i < numberOfInflateCauses; i++) { + addFoundId(input.readInt()); + Assert.assertFalse("Inflate cause is empty!", input.readUTF().isEmpty()); + } + } +} From a7d462c2fa506dfbdf420c8d742f07da58c77afd Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 3 Feb 2023 12:34:39 -0500 Subject: [PATCH 4/8] add test --- .../test/jfr/TestJavaMonitorInflateEvent.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java new file mode 100644 index 000000000000..17196b1d3ea9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java @@ -0,0 +1,106 @@ +/* + * 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; + +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import com.oracle.svm.core.jfr.JfrEvent; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; + +public class TestJavaMonitorInflateEvent extends JfrTest { + private static final int MILLIS = 60; + + static volatile boolean passedCheckpoint = false; + static Thread firstThread; + static Thread secondThread; + static final EnterHelper enterHelper = new EnterHelper(); + + @Override + public String[] getTestedEvents() { + return new String[]{JfrEvent.JavaMonitorInflate.getName()}; + } + + @Override + public void validateEvents() throws Throwable { + boolean foundCauseEnter = false; + for (RecordedEvent event : getEvents()) { + String eventThread = event. getValue("eventThread").getJavaName(); + if (event. getValue("monitorClass").getName().equals(EnterHelper.class.getName()) && firstThread.getName().equals(eventThread) && + event. getValue("cause").equals("Monitor Enter")) { + foundCauseEnter = true; + } + } + assertTrue("Expected monitor inflate event not found.", foundCauseEnter); + } + + @Test + public void test() throws Exception { + Runnable first = () -> { + try { + enterHelper.doWork(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }; + + Runnable second = () -> { + try { + passedCheckpoint = true; + enterHelper.doWork(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }; + + firstThread = new Thread(first); + secondThread = new Thread(second); + + // Generate event with "Monitor Enter" cause + firstThread.start(); + + firstThread.join(); + secondThread.join(); + } + + static class EnterHelper { + private synchronized void doWork() throws InterruptedException { + if (Thread.currentThread().equals(secondThread)) { + return; // second thread doesn't need to do work. + } + // ensure ordering of critical section entry + secondThread.start(); + + // spin until second thread blocks + while (!secondThread.getState().equals(Thread.State.BLOCKED) || !passedCheckpoint) { + Thread.sleep(10); + } + Thread.sleep(MILLIS); + } + } +} From f9c0d1945ee6630def4423ac0988272a863b8ada Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Wed, 8 Feb 2023 16:28:07 +0100 Subject: [PATCH 5/8] Cleanups and small fixes. --- substratevm/CHANGELOG.md | 2 +- .../com/oracle/svm/core/jfr/JfrFeature.java | 3 +- ...> JfrMonitorInflationCauseSerializer.java} | 12 ++-- .../src/com/oracle/svm/core/jfr/JfrType.java | 2 +- .../jfr/events/JavaMonitorInflateEvent.java | 18 ++++-- .../svm/core/jni/functions/JNIFunctions.java | 8 +-- .../jni/functions/JNIInvocationInterface.java | 5 +- .../MonitorInflationCause.java} | 18 +----- .../svm/core/monitor/MonitorSupport.java | 9 +-- .../monitor/MultiThreadedMonitorSupport.java | 63 ++++++++----------- .../monitor/SingleThreadedMonitorSupport.java | 16 +---- .../core/thread/SubstrateVirtualThread.java | 5 +- .../Target_java_lang_VirtualThread.java | 8 ++- .../test/jfr/TestJavaMonitorInflateEvent.java | 42 ++++++++----- .../svm/test/jfr/utils/JfrFileParser.java | 9 +-- ...itorInflationCauseConstantPoolParser.java} | 6 +- 16 files changed, 104 insertions(+), 122 deletions(-) rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/{JfrInflationCauseSerializer.java => JfrMonitorInflationCauseSerializer.java} (83%) rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/{jfr/JfrInflationCause.java => monitor/MonitorInflationCause.java} (80%) rename substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/{InflateCauseConstantPoolParser.java => MonitorInflationCauseConstantPoolParser.java} (90%) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index bb7ef03008ff..3368a436dc41 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -17,7 +17,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-42964) Deprecate `--enable-monitoring` without an argument. The option will no longer default to `all` in a future release. Instead, please always explicitly specify the list of monitoring features to be enabled (for example, `--enable-monitoring=heapdump,jfr,jvmstat`"). * (GR-19890) Native Image now sets up build environments for Windows users automatically. Running in an x64 Native Tools Command Prompt is no longer a requirement. * (GR-43410) Added support for the JFR event `ExecutionSample`. -* (GR-44058) Red Hat added support for the JFR event `ObjectAllocationInNewTLAB`. +* (GR-44058) (GR-44087) Red Hat added support for the JFR events `ObjectAllocationInNewTLAB` and `JavaMonitorInflate`. ## Version 22.3.0 * (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java index 0843bae67823..8a0d4a3b8a1f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java @@ -169,7 +169,8 @@ public void afterRegistration(AfterRegistrationAccess access) { JfrSerializerSupport.get().register(new JfrFrameTypeSerializer()); JfrSerializerSupport.get().register(new JfrThreadStateSerializer()); - JfrSerializerSupport.get().register(new JfrInflationCauseSerializer()); + JfrSerializerSupport.get().register(new JfrMonitorInflationCauseSerializer()); + ThreadListenerSupport.get().register(SubstrateJVM.getThreadLocal()); if (HOSTED_ENABLED) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrMonitorInflationCauseSerializer.java similarity index 83% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrMonitorInflationCauseSerializer.java index 3464c4201f77..73b0af20c249 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCauseSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrMonitorInflationCauseSerializer.java @@ -29,20 +29,18 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -/** - * Used to serialize all possible inflation causes into the chunk. - */ -public class JfrInflationCauseSerializer implements JfrConstantPool { +import com.oracle.svm.core.monitor.MonitorInflationCause; +public class JfrMonitorInflationCauseSerializer implements JfrConstantPool { @Platforms(Platform.HOSTED_ONLY.class) - public JfrInflationCauseSerializer() { + public JfrMonitorInflationCauseSerializer() { } @Override public int write(JfrChunkWriter writer) { - writer.writeCompressedLong(JfrType.InflationCause.getId()); + writer.writeCompressedLong(JfrType.MonitorInflationCause.getId()); - JfrInflationCause[] inflationCauses = JfrInflationCause.values(); + MonitorInflationCause[] inflationCauses = MonitorInflationCause.values(); writer.writeCompressedLong(inflationCauses.length); for (int i = 0; i < inflationCauses.length; i++) { writer.writeCompressedInt(i); 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 2f05653350fb..bdc206cf1ff9 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 @@ -43,7 +43,7 @@ public enum JfrType { GCCause("jdk.types.GCCause"), GCName("jdk.types.GCName"), VMOperation("jdk.types.VMOperationType"), - InflationCause("jdk.types.InflateCause"); + MonitorInflationCause("jdk.types.InflateCause"); private final long id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java index f154ab2322dc..aa958f255297 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorInflateEvent.java @@ -26,28 +26,28 @@ package com.oracle.svm.core.jfr.events; -import com.oracle.svm.core.jfr.HasJfrSupport; +import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.StackValue; import com.oracle.svm.core.Uninterruptible; +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.SubstrateJVM; -import com.oracle.svm.core.jfr.JfrInflationCause; -import org.graalvm.compiler.word.Word; +import com.oracle.svm.core.monitor.MonitorInflationCause; public class JavaMonitorInflateEvent { - public static void emit(Object obj, long startTicks, JfrInflationCause cause) { + public static void emit(Object obj, long startTicks, MonitorInflationCause cause) { if (HasJfrSupport.get()) { emit0(obj, startTicks, cause); } } @Uninterruptible(reason = "Accesses a JFR buffer.") - public static void emit0(Object obj, long startTicks, JfrInflationCause cause) { + public static void emit0(Object obj, long startTicks, MonitorInflationCause cause) { if (JfrEvent.JavaMonitorInflate.shouldEmit()) { JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); @@ -59,8 +59,14 @@ public static void emit0(Object obj, long startTicks, JfrInflationCause cause) { JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorInflate, 0)); JfrNativeEventWriter.putClass(data, obj.getClass()); JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); - JfrNativeEventWriter.putLong(data, cause.getId()); + JfrNativeEventWriter.putLong(data, getId(cause)); JfrNativeEventWriter.endSmallEvent(data); } } + + @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true) + private static long getId(MonitorInflationCause cause) { + /* First entry needs to have id 0. */ + return cause.ordinal(); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java index 01eea859a2f9..2d3202e81f62 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java @@ -35,7 +35,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import com.oracle.svm.core.jfr.JfrInflationCause; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -106,6 +105,7 @@ import com.oracle.svm.core.jni.headers.JNIVersion; import com.oracle.svm.core.jni.headers.JNIVersionJDK19OrLater; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.stack.StackOverflowCheck; @@ -1016,7 +1016,7 @@ static int MonitorEnter(JNIEnvironment env, JNIObjectHandle handle) { } boolean acquired = false; try { - MonitorSupport.singleton().monitorEnter(obj, JfrInflationCause.JNI_ENTER); + MonitorSupport.singleton().monitorEnter(obj, MonitorInflationCause.JNI_ENTER); assert Thread.holdsLock(obj); acquired = true; @@ -1025,7 +1025,7 @@ static int MonitorEnter(JNIEnvironment env, JNIObjectHandle handle) { } catch (Throwable t) { try { if (acquired) { - MonitorSupport.singleton().monitorExit(obj, JfrInflationCause.JNI_EXIT); + MonitorSupport.singleton().monitorExit(obj, MonitorInflationCause.JNI_ENTER); } if (pinned) { VirtualThreads.singleton().unpinCurrent(); @@ -1050,7 +1050,7 @@ static int MonitorExit(JNIEnvironment env, JNIObjectHandle handle) { if (!Thread.holdsLock(obj)) { throw new IllegalMonitorStateException(); } - MonitorSupport.singleton().monitorExit(obj, JfrInflationCause.JNI_EXIT); + MonitorSupport.singleton().monitorExit(obj, MonitorInflationCause.JNI_EXIT); JNIThreadOwnedMonitors.exited(obj); if (VirtualThreads.isSupported() && JavaThreads.isCurrentThreadVirtual()) { try { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java index d8b524f42588..0d2b18b0628f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java @@ -42,8 +42,8 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.UnmanagedMemoryUtil; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.UnmanagedMemoryUtil; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.c.function.CEntryPointActions; @@ -73,6 +73,7 @@ import com.oracle.svm.core.jni.headers.JNIJavaVMPointer; import com.oracle.svm.core.jni.headers.JNIVersion; import com.oracle.svm.core.log.FunctionPointerLogHandler; +import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.snippets.ImplicitExceptions; import com.oracle.svm.core.thread.PlatformThreads; @@ -353,7 +354,7 @@ static void attachCurrentThread(JNIJavaVM vm, JNIEnvironmentPointer penv, JNIJav static void releaseCurrentThreadOwnedMonitors() { JNIThreadOwnedMonitors.forEach((obj, depth) -> { for (int i = 0; i < depth; i++) { - MonitorSupport.singleton().monitorExit(obj); + MonitorSupport.singleton().monitorExit(obj, MonitorInflationCause.VM_INTERNAL); } assert !Thread.holdsLock(obj); }); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorInflationCause.java similarity index 80% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorInflationCause.java index 410f58a6f22b..a4ee24912a6d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrInflationCause.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorInflationCause.java @@ -24,39 +24,27 @@ * questions. */ -package com.oracle.svm.core.jfr; +package com.oracle.svm.core.monitor; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.Uninterruptible; - -/** - * List of all possible inflation causes. - */ -public enum JfrInflationCause { +public enum MonitorInflationCause { VM_INTERNAL("VM Internal"), MONITOR_ENTER("Monitor Enter"), WAIT("Monitor Wait"), NOTIFY("Monitor Notify"), - HASH_CODE("Monitor Hash Code"), JNI_ENTER("JNI Monitor Enter"), JNI_EXIT("JNI Monitor Exit"); private final String text; @Platforms(Platform.HOSTED_ONLY.class) - JfrInflationCause(String text) { + MonitorInflationCause(String text) { this.text = text; } public String getText() { return text; } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public long getId() { - // First entry needs to have id 0. - return ordinal(); - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java index b99c32d7cfdf..abbc7f94bc00 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java @@ -29,7 +29,6 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.thread.ThreadStatus; -import com.oracle.svm.core.jfr.JfrInflationCause; /** * This interface provides functions related to monitor operations (the Java "synchronized" keyword @@ -45,16 +44,12 @@ public static MonitorSupport singleton() { /** * Implements the semantics of the monitorenter bytecode. */ - public abstract void monitorEnter(Object obj); - - public abstract void monitorEnter(Object obj, JfrInflationCause cause); + public abstract void monitorEnter(Object obj, MonitorInflationCause cause); /** * Implements the semantics of the monitorexit bytecode. */ - public abstract void monitorExit(Object obj); - - public abstract void monitorExit(Object obj, JfrInflationCause cause); + public abstract void monitorExit(Object obj, MonitorInflationCause cause); /* * Support for objects that are re-locked during deoptimization. This method is called when diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java index 44a882abb5e2..c766f4315f7a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java @@ -40,23 +40,22 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.WeakIdentityHashMap; +import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.heap.RestrictHeapAccess.Access; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubCompanion; import com.oracle.svm.core.jdk.JDK17OrEarlier; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.events.JavaMonitorInflateEvent; import com.oracle.svm.core.monitor.JavaMonitorQueuedSynchronizer.JavaMonitorConditionObject; import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.thread.ThreadStatus; import com.oracle.svm.core.thread.VMOperationControl; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.jfr.events.JavaMonitorInflateEvent; -import com.oracle.svm.core.jfr.JfrTicks; -import com.oracle.svm.core.jfr.JfrInflationCause; import jdk.internal.misc.Unsafe; @@ -208,7 +207,7 @@ private static void slowPathMonitorEnter(Object obj) { StackOverflowCheck.singleton().makeYellowZoneAvailable(); VMOperationControl.guaranteeOkayToBlock("No Java synchronization must be performed within a VMOperation: if the object is already locked, the VM is deadlocked"); try { - singleton().monitorEnter(obj); + singleton().monitorEnter(obj, MonitorInflationCause.MONITOR_ENTER); } catch (OutOfMemoryError ex) { /* @@ -241,14 +240,8 @@ private static void slowPathMonitorEnter(Object obj) { @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) @Override - public void monitorEnter(Object obj) { - monitorEnter(obj, JfrInflationCause.MONITOR_ENTER); - } - - @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) - @Override - public void monitorEnter(Object obj, JfrInflationCause cause) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); + public void monitorEnter(Object obj, MonitorInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, cause); lockObject.monitorEnter(obj); } @@ -257,7 +250,7 @@ public void monitorEnter(Object obj, JfrInflationCause cause) { private static void slowPathMonitorExit(Object obj) { StackOverflowCheck.singleton().makeYellowZoneAvailable(); try { - singleton().monitorExit(obj); + singleton().monitorExit(obj, MonitorInflationCause.MONITOR_ENTER); } catch (OutOfMemoryError ex) { /* @@ -284,14 +277,8 @@ private static void slowPathMonitorExit(Object obj) { @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) @Override - public void monitorExit(Object obj) { - monitorExit(obj, JfrInflationCause.VM_INTERNAL); - } - - @RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED) - @Override - public void monitorExit(Object obj, JfrInflationCause cause) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); + public void monitorExit(Object obj, MonitorInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, cause); lockObject.monitorExit(); } @@ -311,7 +298,7 @@ public Object prepareRelockObject(Object obj) { * internal state of the lock immediately here. The actual state patching therefore happens * later in doRelockObject. */ - return getOrCreateMonitor(obj, true); + return getOrCreateMonitor(obj, MonitorInflationCause.VM_INTERNAL); } @Uninterruptible(reason = "called during deoptimization") @@ -323,13 +310,13 @@ public void doRelockObject(Object obj, Object lockData) { @Override public boolean isLockedByCurrentThread(Object obj) { - JavaMonitor lockObject = getOrCreateMonitor(obj, false); + JavaMonitor lockObject = getMonitor(obj); return lockObject != null && lockObject.isHeldByCurrentThread(); } @Override public boolean isLockedByAnyThread(Object obj) { - JavaMonitor lockObject = getOrCreateMonitor(obj, false); + JavaMonitor lockObject = getMonitor(obj); return lockObject != null && lockObject.isLocked(); } @@ -340,7 +327,7 @@ protected void doWait(Object obj, long timeoutMillis) throws InterruptedExceptio * Ensure that the current thread holds the lock. Required by the specification of * Object.wait, and also required for our implementation. */ - JavaMonitor lock = ensureLocked(obj, JfrInflationCause.WAIT); + JavaMonitor lock = ensureLocked(obj, MonitorInflationCause.WAIT); JavaMonitorConditionObject condition = lock.getOrCreateCondition(true); if (timeoutMillis == 0L) { condition.await(obj); @@ -352,7 +339,7 @@ protected void doWait(Object obj, long timeoutMillis) throws InterruptedExceptio @Override public void notify(Object obj, boolean notifyAll) { /* Make sure the current thread holds the lock on the receiver. */ - JavaMonitor lock = ensureLocked(obj, JfrInflationCause.NOTIFY); + JavaMonitor lock = ensureLocked(obj, MonitorInflationCause.NOTIFY); /* Find the wait/notify condition of the receiver. */ JavaMonitorConditionObject condition = lock.getOrCreateCondition(false); /* If the receiver does not have a condition, then it has not been waited on. */ @@ -366,8 +353,8 @@ public void notify(Object obj, boolean notifyAll) { } /** Returns the lock of the object. */ - protected JavaMonitor ensureLocked(Object obj, JfrInflationCause cause) { - JavaMonitor lockObject = getOrCreateMonitor(obj, true, cause); + protected JavaMonitor ensureLocked(Object obj, MonitorInflationCause cause) { + JavaMonitor lockObject = getOrCreateMonitor(obj, cause); if (!lockObject.isHeldByCurrentThread()) { throw new IllegalMonitorStateException("Receiver is not locked by the current thread."); } @@ -390,11 +377,15 @@ protected static Object replaceObject(Object unreplacedObject) { return unreplacedObject; } - protected final JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotExisting) { - return getOrCreateMonitor(obj, createIfNotExisting, JfrInflationCause.VM_INTERNAL); + protected final JavaMonitor getMonitor(Object obj) { + return getOrCreateMonitor(obj, false, null); + } + + protected final JavaMonitor getOrCreateMonitor(Object obj, MonitorInflationCause cause) { + return getOrCreateMonitor(obj, true, cause); } - protected final JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotExisting, JfrInflationCause cause) { + private JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotExisting, MonitorInflationCause cause) { int monitorOffset = getMonitorOffset(obj); if (monitorOffset != 0) { /* The common case: pointer to the monitor reserved in the object. */ @@ -404,7 +395,7 @@ protected final JavaMonitor getOrCreateMonitor(Object obj, boolean createIfNotEx } } - private JavaMonitor getOrCreateMonitorSlow(Object unreplacedObject, boolean createIfNotExisting, JfrInflationCause cause) { + private JavaMonitor getOrCreateMonitorSlow(Object unreplacedObject, boolean createIfNotExisting, MonitorInflationCause cause) { Object replacedObject = replaceObject(unreplacedObject); if (replacedObject != unreplacedObject) { int monitorOffset = getMonitorOffset(replacedObject); @@ -416,7 +407,7 @@ private JavaMonitor getOrCreateMonitorSlow(Object unreplacedObject, boolean crea return getOrCreateMonitorFromMap(replacedObject, createIfNotExisting, cause); } - protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset, JfrInflationCause cause) { + protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset, MonitorInflationCause cause) { JavaMonitor existingMonitor = (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); if (existingMonitor != null || !createIfNotExisting) { return existingMonitor; @@ -432,7 +423,7 @@ protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfN return (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset); } - protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting, JfrInflationCause cause) { + protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting, MonitorInflationCause cause) { assert JavaVersionUtil.JAVA_SPEC > 17 || obj.getClass() != Target_java_lang_ref_ReferenceQueue_Lock.class : "ReferenceQueue.Lock must have a monitor field or we can deadlock accessing WeakIdentityHashMap below"; VMError.guarantee(!additionalMonitorsLock.isHeldByCurrentThread(), diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java index b16855c06cdf..c0f7ad2d5063 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/SingleThreadedMonitorSupport.java @@ -26,30 +26,18 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.thread.ThreadStatus; -import com.oracle.svm.core.jfr.JfrInflationCause; /** * Without support for threads, there is no need for any monitor operations. */ public class SingleThreadedMonitorSupport extends MonitorSupport { - - @Override - public void monitorEnter(Object obj) { - /* Synchronization is a no-op in single threaded mode. */ - } - - @Override - public void monitorEnter(Object obj, JfrInflationCause cause) { - /* Synchronization is a no-op in single threaded mode. */ - } - @Override - public void monitorExit(Object obj) { + public void monitorEnter(Object obj, MonitorInflationCause cause) { /* Synchronization is a no-op in single threaded mode. */ } @Override - public void monitorExit(Object obj, JfrInflationCause cause) { + public void monitorExit(Object obj, MonitorInflationCause cause) { /* Synchronization is a no-op in single threaded mode. */ } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java index 590c089890c3..12260400e20a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java @@ -44,6 +44,7 @@ import com.oracle.svm.core.jdk.ContinuationsSupported; import com.oracle.svm.core.jdk.JDK17OrEarlier; import com.oracle.svm.core.jdk.NotLoomJDK; +import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.stack.JavaFrameAnchor; import com.oracle.svm.core.stack.JavaFrameAnchors; @@ -450,13 +451,13 @@ private Object acquireInterruptLockMaybeSwitch() { PlatformThreads.setCurrentThread(carrier, carrier); token = this; } - MonitorSupport.singleton().monitorEnter(interruptLock()); + MonitorSupport.singleton().monitorEnter(interruptLock(), MonitorInflationCause.VM_INTERNAL); return token; } /** @see #acquireInterruptLockMaybeSwitch */ private void releaseInterruptLockMaybeSwitchBack(Object token) { - MonitorSupport.singleton().monitorExit(interruptLock()); + MonitorSupport.singleton().monitorExit(interruptLock(), MonitorInflationCause.VM_INTERNAL); if (token != null) { assert token == this && Thread.currentThread() == carrierThread; PlatformThreads.setCurrentThread(carrierThread, this); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java index ae0874ae9e8e..309d099697fb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java @@ -41,12 +41,14 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.jdk.JDK20OrLater; import com.oracle.svm.core.jdk.LoomJDK; +import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.util.VMError; @TargetClass(className = "java.lang.VirtualThread", onlyWith = LoomJDK.class) public final class Target_java_lang_VirtualThread { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// + @Alias + @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// private static boolean notifyJvmtiEvents; // Checkstyle: stop @@ -287,14 +289,14 @@ static Object acquireInterruptLockMaybeSwitch(Target_java_lang_VirtualThread sel token = self; } Object lock = asTarget(self).interruptLock; - MonitorSupport.singleton().monitorEnter(lock); + MonitorSupport.singleton().monitorEnter(lock, MonitorInflationCause.VM_INTERNAL); return token; } /** @see #acquireInterruptLockMaybeSwitch */ static void releaseInterruptLockMaybeSwitchBack(Target_java_lang_VirtualThread self, Object token) { Object lock = asTarget(self).interruptLock; - MonitorSupport.singleton().monitorExit(lock); + MonitorSupport.singleton().monitorExit(lock, MonitorInflationCause.VM_INTERNAL); if (token != null) { assert token == self; Thread carrier = asVTarget(token).carrierThread; diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java index 17196b1d3ea9..9edcbfde57d6 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorInflateEvent.java @@ -27,19 +27,20 @@ package com.oracle.svm.test.jfr; import static org.junit.Assert.assertTrue; + import org.junit.Test; + import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.monitor.MonitorInflationCause; + import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedThread; public class TestJavaMonitorInflateEvent extends JfrTest { - private static final int MILLIS = 60; - - static volatile boolean passedCheckpoint = false; - static Thread firstThread; - static Thread secondThread; - static final EnterHelper enterHelper = new EnterHelper(); + private static final EnterHelper ENTER_HELPER = new EnterHelper(); + private static Thread firstThread; + private static Thread secondThread; @Override public String[] getTestedEvents() { @@ -51,8 +52,11 @@ public void validateEvents() throws Throwable { boolean foundCauseEnter = false; for (RecordedEvent event : getEvents()) { String eventThread = event. getValue("eventThread").getJavaName(); - if (event. getValue("monitorClass").getName().equals(EnterHelper.class.getName()) && firstThread.getName().equals(eventThread) && - event. getValue("cause").equals("Monitor Enter")) { + String monitorClass = event. getValue("monitorClass").getName(); + String cause = event.getValue("cause"); + if (monitorClass.equals(EnterHelper.class.getName()) && + cause.equals(MonitorInflationCause.MONITOR_ENTER.getText()) && + (eventThread.equals(firstThread.getName()) || eventThread.equals(secondThread.getName()))) { foundCauseEnter = true; } } @@ -63,7 +67,7 @@ event. getValue("cause").equals("Monitor Enter")) { public void test() throws Exception { Runnable first = () -> { try { - enterHelper.doWork(); + ENTER_HELPER.doWork(); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -71,8 +75,8 @@ public void test() throws Exception { Runnable second = () -> { try { - passedCheckpoint = true; - enterHelper.doWork(); + EnterHelper.passedCheckpoint = true; + ENTER_HELPER.doWork(); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -81,17 +85,23 @@ public void test() throws Exception { firstThread = new Thread(first); secondThread = new Thread(second); - // Generate event with "Monitor Enter" cause + /* Generate event with "Monitor Enter" cause. */ firstThread.start(); firstThread.join(); secondThread.join(); } - static class EnterHelper { - private synchronized void doWork() throws InterruptedException { + private static class EnterHelper { + static volatile boolean passedCheckpoint = false; + + synchronized void doWork() throws InterruptedException { if (Thread.currentThread().equals(secondThread)) { - return; // second thread doesn't need to do work. + /* + * The second thread only needs to enter the critical section but doesn't need to do + * work. + */ + return; } // ensure ordering of critical section entry secondThread.start(); @@ -100,7 +110,7 @@ private synchronized void doWork() throws InterruptedException { while (!secondThread.getState().equals(Thread.State.BLOCKED) || !passedCheckpoint) { Thread.sleep(10); } - Thread.sleep(MILLIS); + Thread.sleep(60); } } } 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 5d001148c2d1..85d4cb520a18 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 @@ -32,10 +32,11 @@ import java.io.IOException; import java.util.HashMap; +import org.junit.Assert; + import com.oracle.svm.core.jfr.JfrChunkWriter; import com.oracle.svm.core.jfr.JfrTicks; import com.oracle.svm.core.jfr.JfrType; - import com.oracle.svm.test.jfr.utils.poolparsers.ClassConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.ClassLoaderConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.ConstantPoolParser; @@ -44,6 +45,7 @@ import com.oracle.svm.test.jfr.utils.poolparsers.GCNameConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.MethodConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.ModuleConstantPoolParser; +import com.oracle.svm.test.jfr.utils.poolparsers.MonitorInflationCauseConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.PackageConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.StacktraceConstantPoolParser; import com.oracle.svm.test.jfr.utils.poolparsers.SymbolConstantPoolParser; @@ -51,9 +53,8 @@ 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 com.oracle.svm.test.jfr.utils.poolparsers.InflateCauseConstantPoolParser; + import jdk.jfr.Recording; -import org.junit.Assert; public class JfrFileParser { @@ -79,7 +80,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()); - supportedConstantPools.put(JfrType.InflationCause.getId(), new InflateCauseConstantPoolParser()); + supportedConstantPools.put(JfrType.MonitorInflationCause.getId(), new MonitorInflationCauseConstantPoolParser()); } public static HashMap getSupportedConstantPools() { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/MonitorInflationCauseConstantPoolParser.java similarity index 90% rename from substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java rename to substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/MonitorInflationCauseConstantPoolParser.java index a8f192bc4709..75777994b4be 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/InflateCauseConstantPoolParser.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/utils/poolparsers/MonitorInflationCauseConstantPoolParser.java @@ -32,12 +32,12 @@ import com.oracle.svm.test.jfr.utils.RecordingInput; -public class InflateCauseConstantPoolParser extends ConstantPoolParser { +public class MonitorInflationCauseConstantPoolParser extends ConstantPoolParser { @Override public void parse(RecordingInput input) throws IOException { - int numberOfInflateCauses = input.readInt(); - for (int i = 0; i < numberOfInflateCauses; i++) { + int count = input.readInt(); + for (int i = 0; i < count; i++) { addFoundId(input.readInt()); Assert.assertFalse("Inflate cause is empty!", input.readUTF().isEmpty()); } From f5809a753c19fd419ad14669afb273a7a7957a91 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Thu, 9 Feb 2023 12:07:58 +0100 Subject: [PATCH 6/8] Address review comments. --- .../src/com/oracle/svm/core/jni/functions/JNIFunctions.java | 3 ++- .../svm/core/monitor/MultiThreadedMonitorSupport.java | 6 +++++- .../svm/core/thread/Target_java_lang_VirtualThread.java | 3 +-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java index 2d3202e81f62..5482f8a7bb83 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java @@ -1025,7 +1025,8 @@ static int MonitorEnter(JNIEnvironment env, JNIObjectHandle handle) { } catch (Throwable t) { try { if (acquired) { - MonitorSupport.singleton().monitorExit(obj, MonitorInflationCause.JNI_ENTER); + /* The thread acquired the monitor, so monitor inflation can't happen here. */ + MonitorSupport.singleton().monitorExit(obj, MonitorInflationCause.VM_INTERNAL); } if (pinned) { VirtualThreads.singleton().unpinCurrent(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java index c766f4315f7a..cf1844f26a5b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java @@ -250,7 +250,11 @@ public void monitorEnter(Object obj, MonitorInflationCause cause) { private static void slowPathMonitorExit(Object obj) { StackOverflowCheck.singleton().makeYellowZoneAvailable(); try { - singleton().monitorExit(obj, MonitorInflationCause.MONITOR_ENTER); + /* + * Monitor inflation cannot happen here because Graal enforces structured locking and + * unlocking, see comment below. + */ + singleton().monitorExit(obj, MonitorInflationCause.VM_INTERNAL); } catch (OutOfMemoryError ex) { /* diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java index 309d099697fb..c64b0b6727c8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_VirtualThread.java @@ -47,8 +47,7 @@ @TargetClass(className = "java.lang.VirtualThread", onlyWith = LoomJDK.class) public final class Target_java_lang_VirtualThread { - @Alias - @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// private static boolean notifyJvmtiEvents; // Checkstyle: stop From 1d1f6172466d8ebf8a1f45774291bf09f43ef622 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 13 Feb 2023 10:44:02 +0100 Subject: [PATCH 7/8] Removed no longer needed check. --- .../oracle/svm/core/jni/functions/JNIInvocationInterface.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java index 0d2b18b0628f..9551a2b62037 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java @@ -268,9 +268,6 @@ static int AttachCurrentThreadAsDaemon(JNIJavaVM vm, JNIEnvironmentPointer penv, @CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadEnsureJavaThreadPrologue.class, epilogue = LeaveDetachThreadEpilogue.class) static int DetachCurrentThread(JNIJavaVM vm) { int result = JNIErrors.JNI_OK(); - if (!vm.equal(JNIFunctionTables.singleton().getGlobalJavaVM())) { - result = JNIErrors.JNI_ERR(); - } // JNI specification requires releasing all owned monitors Support.releaseCurrentThreadOwnedMonitors(); return result; From 57e511e9183b878fb9168c17170363e398d31226 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 13 Feb 2023 17:06:01 +0100 Subject: [PATCH 8/8] Style fix. --- .../oracle/svm/core/jni/functions/JNIInvocationInterface.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java index 9551a2b62037..64f31b677262 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java @@ -266,7 +266,7 @@ static int AttachCurrentThreadAsDaemon(JNIJavaVM vm, JNIEnvironmentPointer penv, */ @CEntryPoint(include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) @CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadEnsureJavaThreadPrologue.class, epilogue = LeaveDetachThreadEpilogue.class) - static int DetachCurrentThread(JNIJavaVM vm) { + static int DetachCurrentThread(@SuppressWarnings("unused") JNIJavaVM vm) { int result = JNIErrors.JNI_OK(); // JNI specification requires releasing all owned monitors Support.releaseCurrentThreadOwnedMonitors();