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 d6bc9dc3ad1d..8a11b2c33a73 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 @@ -54,7 +54,9 @@ public enum JfrEvent { SafepointBegin("jdk.SafepointBegin"), SafepointEnd("jdk.SafepointEnd"), ExecuteVMOperation("jdk.ExecuteVMOperation"), - JavaMonitorEnter("jdk.JavaMonitorEnter"); + JavaMonitorEnter("jdk.JavaMonitorEnter"), + ThreadSleep("jdk.ThreadSleep"), + JavaMonitorWait("jdk.JavaMonitorWait"); private final long id; 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 bb641aa862e8..1cbccf3dbd20 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 @@ -254,6 +254,11 @@ public long getStackTraceId(int skipCount) { return stackTraceRepo.getStackTraceId(skipCount, false); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public long getStackTraceId(JfrEvent eventType, int skipCount) { + return getStackTraceId(eventType.getId(), skipCount); + } + /** See {@link JVM#getThreadId}. */ public static long getThreadId(Thread thread) { if (HasJfrSupport.get()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecutionSampleEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecutionSampleEvent.java index a4c0cb673542..1f914beaacd4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecutionSampleEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ExecutionSampleEvent.java @@ -69,7 +69,7 @@ public static void writeExecutionSample(IsolateThread isolateThread, Thread.Stat JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ExecutionSample); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); JfrNativeEventWriter.putThread(data, isolateThread); - JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvent.ExecutionSample.getId(), 0)); + JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvent.ExecutionSample, 0)); JfrNativeEventWriter.putLong(data, JfrThreadState.getId(threadState)); JfrNativeEventWriter.endSmallEvent(data); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java index 20a6a81d9fd9..9fd5dd2af64b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorEnterEvent.java @@ -36,6 +36,8 @@ import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; import com.oracle.svm.core.jfr.JfrTicks; import com.oracle.svm.core.jfr.SubstrateJVM; +import org.graalvm.compiler.word.Word; + public class JavaMonitorEnterEvent { public static void emit(Object obj, long previousOwnerTid, long startTicks) { @@ -54,10 +56,10 @@ public static void emit0(Object obj, long previousOwnerTid, long startTicks) { JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter.getId(), 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter, 0)); JfrNativeEventWriter.putClass(data, obj.getClass()); JfrNativeEventWriter.putLong(data, previousOwnerTid); - JfrNativeEventWriter.putLong(data, org.graalvm.compiler.word.Word.objectToUntrackedPointer(obj).rawValue()); + JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); JfrNativeEventWriter.endSmallEvent(data); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java new file mode 100644 index 000000000000..d27fa5c1d8ce --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java @@ -0,0 +1,62 @@ +/* + * 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.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 org.graalvm.compiler.word.Word; +import com.oracle.svm.core.jfr.HasJfrSupport; +public class JavaMonitorWaitEvent { + public static void emit(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) { + if (HasJfrSupport.get()) { + emit0(startTicks, obj, notifier, timeout, timedOut); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + private static void emit0(long startTicks, Object obj, long notifier, long timeout, boolean timedOut) { + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.JavaMonitorWait)) { + JfrNativeEventWriterData data = org.graalvm.nativeimage.StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaMonitorWait); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorWait, 0)); + JfrNativeEventWriter.putClass(data, obj.getClass()); + JfrNativeEventWriter.putLong(data, notifier); + JfrNativeEventWriter.putLong(data, timeout); + JfrNativeEventWriter.putBoolean(data, timedOut); + JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadSleepEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadSleepEvent.java new file mode 100644 index 000000000000..e927dd0744a4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadSleepEvent.java @@ -0,0 +1,62 @@ +/* + * 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 org.graalvm.nativeimage.StackValue; + +import com.oracle.svm.core.annotate.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; + +public class ThreadSleepEvent { + + public static void emit(long time, long startTicks) { + if (com.oracle.svm.core.jfr.HasJfrSupport.get()) { + emit0(time, startTicks); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + private static void emit0(long time, long startTicks) { + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.ThreadSleep)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ThreadSleep); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadSleep, 0)); + JfrNativeEventWriter.putLong(data, time); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadStartEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadStartEvent.java index 0352112b672f..2445bce518f4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadStartEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadStartEvent.java @@ -47,7 +47,7 @@ public static void emit(IsolateThread isolateThread) { JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ThreadStart); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvent.ThreadStart.getId(), 0)); + JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvent.ThreadStart, 0)); JfrNativeEventWriter.putThread(data, isolateThread); JfrNativeEventWriter.putLong(data, SubstrateJVM.getParentThreadId(isolateThread)); JfrNativeEventWriter.endSmallEvent(data); 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 e767cfe423ac..03c6e5781c11 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 @@ -47,10 +47,12 @@ import com.oracle.svm.core.WeakIdentityHashMap; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Inject; +import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.RestrictHeapAccess.Access; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubCompanion; @@ -60,6 +62,7 @@ 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.JavaMonitorWaitEvent; import jdk.internal.misc.Unsafe; @@ -379,10 +382,18 @@ protected void doWait(Object obj, long timeoutMillis) throws InterruptedExceptio */ JavaMonitor lock = ensureLocked(obj); Condition condition = getOrCreateCondition(lock, true); + long startTicks = com.oracle.svm.core.jfr.JfrTicks.elapsedTicks(); if (timeoutMillis == 0L) { condition.await(); + com.oracle.svm.core.thread.Target_java_lang_Thread t = SubstrateUtil.cast(Thread.currentThread(), com.oracle.svm.core.thread.Target_java_lang_Thread.class); + JavaMonitorWaitEvent.emit(startTicks, obj, t.notifierTid, timeoutMillis, false); } else { - condition.await(timeoutMillis, TimeUnit.MILLISECONDS); + if (condition.await(timeoutMillis, TimeUnit.MILLISECONDS)) { + com.oracle.svm.core.thread.Target_java_lang_Thread t = SubstrateUtil.cast(Thread.currentThread(), com.oracle.svm.core.thread.Target_java_lang_Thread.class); + JavaMonitorWaitEvent.emit(startTicks, obj, t.notifierTid, timeoutMillis, false); + } else { + JavaMonitorWaitEvent.emit(startTicks, obj, 0, timeoutMillis, true); + } } } @@ -573,18 +584,80 @@ final class Target_com_oracle_svm_core_monitor_MultiThreadedMonitorSupport { final class Target_java_util_concurrent_locks_ReentrantLock { @Alias// Target_java_util_concurrent_locks_ReentrantLock_Sync sync; + } + @TargetClass(AbstractQueuedSynchronizer.class) final class Target_java_util_concurrent_locks_AbstractQueuedSynchronizer { @Alias // volatile int state; + @Alias + @TargetElement(name = "enqueue", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + final native void enqueue(Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_Node node); +} +@TargetClass(value = java.util.concurrent.locks.AbstractQueuedSynchronizer.class, innerClass = "Node", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) +final class Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_Node { + @Alias + Thread waiter; + @Alias + @TargetElement(name = "getAndUnsetStatus", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + final native int getAndUnsetStatus(int v); +} + +@TargetClass(value = java.util.concurrent.locks.AbstractQueuedSynchronizer.class, innerClass = "ConditionNode", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) +final class Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode { + + @Alias + @TargetElement(name = "nextWaiter", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode nextWaiter; + } @TargetClass(value = ConditionObject.class) final class Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionObject { /** Enclosing {@link AbstractQueuedSynchronizer} of this nested class. */ @Alias Target_java_util_concurrent_locks_AbstractQueuedSynchronizer this$0; + + @Alias + @TargetElement(name = "firstWaiter", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + private transient Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode firstWaiter; + @Alias + @TargetElement(name = "lastWaiter", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + private transient Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode lastWaiter; + + @Substitute + @TargetElement(name = "doSignal", onlyWith = com.oracle.svm.core.jdk.JDK17OrLater.class) + private void doSignal(Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode first, boolean all) { + while(true) { + Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionNode next; + label19: { + if (first != null) { + next = first.nextWaiter; + if ((this.firstWaiter = next) == null) { + this.lastWaiter = null; + } + Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_Node firstNode = SubstrateUtil.cast(first, Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_Node.class); + if ((firstNode.getAndUnsetStatus(2) & 2) == 0) { + break label19; + } + + com.oracle.svm.core.thread.Target_java_lang_Thread t = SubstrateUtil.cast(firstNode.waiter, com.oracle.svm.core.thread.Target_java_lang_Thread.class); + t.notifierTid = Thread.currentThread().getId(); + + this$0.enqueue(firstNode); + if (all) { + break label19; + } + } + + return; + } + + first = next; + } + } + } @TargetClass(value = ReferenceQueue.class, innerClass = "Lock", onlyWith = JDK17OrEarlier.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java index 5fa7372c4c6f..be79012a7b44 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java @@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import com.oracle.svm.core.jfr.events.ThreadSleepEvent; + import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -301,11 +303,13 @@ static void initializeNewThread( } static void sleep(long millis) throws InterruptedException { + long startTicks = com.oracle.svm.core.jfr.JfrTicks.elapsedTicks(); if (supportsVirtual() && isVirtualDisallowLoom(Thread.currentThread())) { VirtualThreads.singleton().sleepMillis(millis); } else { PlatformThreads.sleep(millis); } + ThreadSleepEvent.emit(millis, startTicks); } static boolean isAlive(Thread thread) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java index 633193862042..23048a818304 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java @@ -86,6 +86,9 @@ public final class Target_java_lang_Thread { @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // long parentThreadId; + @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + public long notifierTid; + @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = ThreadData.class)// UnacquiredThreadData threadData;