diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFrameType.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFrameType.java index 6efce2d29d42..151f97c9c503 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFrameType.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFrameType.java @@ -49,8 +49,8 @@ public String getText() { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public byte getId() { + public long getId() { // First entry needs to have id 0. - return (byte) ordinal(); + return ordinal(); } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadLocal.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadLocal.java index 1737da94ef85..fafaf2c3066c 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadLocal.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadLocal.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.jfr; +import com.oracle.svm.jfr.events.ExecutionSampleEvent; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; @@ -98,6 +99,9 @@ public void beforeThreadRun(IsolateThread isolateThread, Thread javaThread) { // Emit ThreadStart event before thread.run(). ThreadStartEvent.emit(isolateThread); + + // Register ExecutionSampleEvent after ThreadStart event and before thread.run(). + ExecutionSampleEvent.tryToRegisterExecutionSampleEventCallback(); } @Uninterruptible(reason = "Accesses a JFR buffer.") diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadState.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadState.java index c73dbb78ce1e..a05716b51763 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadState.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrThreadState.java @@ -53,13 +53,13 @@ public String getText() { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public byte getId() { + public long getId() { // First entry needs to have id 0. - return (byte) ordinal(); + return ordinal(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static byte getId(Thread.State threadState) { + public static long getId(Thread.State threadState) { return threadStateToJfrThreadState(threadState).getId(); } @@ -79,7 +79,7 @@ private static JfrThreadState threadStateToJfrThreadState(Thread.State threadSta case TERMINATED: return TERMINATED; default: - throw VMError.shouldNotReachHere("Unknown thread state - " + threadState); + throw VMError.shouldNotReachHere("Unknown thread state!"); } } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/SubstrateJVM.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/SubstrateJVM.java index 45f51ac3de67..299e81455e47 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/SubstrateJVM.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/SubstrateJVM.java @@ -40,6 +40,7 @@ import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.ThreadListener; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.jfr.events.ExecutionSampleEvent; import com.oracle.svm.jfr.logging.JfrLogging; import jdk.jfr.Configuration; @@ -237,6 +238,15 @@ public boolean destroyJFR() { return true; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public long getStackTraceId(long eventTypeId, int skipCount) { + if (isStackTraceEnabled(eventTypeId)) { + return getStackTraceId(skipCount); + } else { + return 0L; + } + } + /** See {@link JVM#getStackTraceId}. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getStackTraceId(int skipCount) { @@ -348,8 +358,20 @@ public void setMemorySize(long size) { } /** See {@link JVM#setMethodSamplingInterval}. */ - public void setMethodSamplingInterval(@SuppressWarnings("unused") long type, @SuppressWarnings("unused") long intervalMillis) { - // Not supported but this method is called during JFR startup, so we can't throw an error. + public void setMethodSamplingInterval(long type, long intervalMillis) { + long millis = intervalMillis; + if (type != JfrEvents.ExecutionSample.getId()) { + // JFR is currently only supporting ExecutionSample event, but this method is called + // during JFR startup, so we can't throw an error. + return; + } + + if (millis > 0) { + SubstrateJVM.get().setEnabled(type, true); + } else { + millis = 0; + } + ExecutionSampleEvent.setSamplingInterval(millis); } /** See {@link JVM#setSampleThreads}. */ diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ExecutionSampleEvent.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ExecutionSampleEvent.java new file mode 100644 index 000000000000..3989ac8d3ad6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ExecutionSampleEvent.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.jfr.events; + +import java.util.concurrent.TimeUnit; + +import org.graalvm.nativeimage.CurrentIsolate; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.Threading; +import org.graalvm.nativeimage.impl.ThreadingSupport; + +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.jfr.JfrEvents; +import com.oracle.svm.jfr.JfrNativeEventWriter; +import com.oracle.svm.jfr.JfrNativeEventWriterData; +import com.oracle.svm.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.jfr.JfrThreadState; +import com.oracle.svm.jfr.JfrTicks; +import com.oracle.svm.jfr.SubstrateJVM; + +public final class ExecutionSampleEvent { + + private static long intervalMillis; + private static final ExecutionSampleEventCallback CALLBACK = new ExecutionSampleEventCallback(); + + @Uninterruptible(reason = "Called from uninterruptible code.", calleeMustBe = false) + public static void tryToRegisterExecutionSampleEventCallback() { + if (SubstrateJVM.get().isEnabled(JfrEvents.ExecutionSample) && intervalMillis > 0) { + ImageSingletons.lookup(ThreadingSupport.class).registerRecurringCallback(intervalMillis, TimeUnit.MILLISECONDS, CALLBACK); + } + } + + public static void setSamplingInterval(long intervalMillis) { + ExecutionSampleEvent.intervalMillis = intervalMillis; + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void writeExecutionSample(IsolateThread isolateThread, Thread.State threadState) { + SubstrateJVM svm = SubstrateJVM.get(); + if (SubstrateJVM.isRecording() && svm.isEnabled(JfrEvents.ExecutionSample)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginEventWrite(data, false); + JfrNativeEventWriter.putLong(data, JfrEvents.ExecutionSample.getId()); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); + JfrNativeEventWriter.putThread(data, isolateThread); + JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvents.ExecutionSample.getId(), 0)); + JfrNativeEventWriter.putLong(data, JfrThreadState.getId(threadState)); + JfrNativeEventWriter.endEventWrite(data, false); + } + } + + private static final class ExecutionSampleEventCallback implements Threading.RecurringCallback { + + @Override + public void run(Threading.RecurringCallbackAccess access) { + IsolateThread isolateThread = CurrentIsolate.getCurrentThread(); + Thread javaThread = JavaThreads.fromVMThread(isolateThread); + ExecutionSampleEvent.writeExecutionSample(isolateThread, javaThread.getState()); + } + } +} diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ThreadStartEvent.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ThreadStartEvent.java index 69cc101a5c49..f5c8f94f7bae 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ThreadStartEvent.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/events/ThreadStartEvent.java @@ -48,11 +48,7 @@ public static void emit(IsolateThread isolateThread) { JfrNativeEventWriter.putLong(data, JfrEvents.ThreadStart.getId()); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); JfrNativeEventWriter.putEventThread(data); - if (svm.isStackTraceEnabled(JfrEvents.ThreadStart.getId())) { - JfrNativeEventWriter.putLong(data, svm.getStackTraceId(0)); - } else { - JfrNativeEventWriter.putLong(data, 0L); - } + JfrNativeEventWriter.putLong(data, svm.getStackTraceId(JfrEvents.ThreadStart.getId(), 0)); JfrNativeEventWriter.putThread(data, isolateThread); JfrNativeEventWriter.putLong(data, SubstrateJVM.getParentThreadId(isolateThread)); JfrNativeEventWriter.endEventWrite(data, false); diff --git a/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestThreadEvent.java b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestThreadEvent.java new file mode 100644 index 000000000000..7eb6e3785777 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/TestThreadEvent.java @@ -0,0 +1,57 @@ +/* + * 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.jdk11.jfr; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; + +/** + * Test if event ({@link TestThreadEvent}) with {@link Thread} payload is working. + */ +public class TestThreadEvent extends JFRTest { + + @Test + public void test() throws Exception { + JFR jfr = new LocalJFR(); + Recording recording = jfr.startRecording("TestThreadEvent"); + + ThreadEvent event = new ThreadEvent(); + event.thread = Thread.currentThread(); + event.commit(); + + jfr.endRecording(recording); + try (RecordingFile recordingFile = new RecordingFile(recording.getDestination())) { + assertNotNull(recordingFile); + } finally { + jfr.cleanupRecording(recording); + } + } +} diff --git a/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/ThreadEvent.java b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/ThreadEvent.java new file mode 100644 index 000000000000..c00676cbe5df --- /dev/null +++ b/substratevm/src/com.oracle.svm.test.jdk11/src/com/oracle/svm/test/jdk11/jfr/ThreadEvent.java @@ -0,0 +1,40 @@ +/* + * 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.jdk11.jfr; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.StackTrace; + +@Label("Thread Event") +@Description("An event with a thread payload") +@StackTrace(false) +public class ThreadEvent extends Event { + + @Label("Thread") public Thread thread; +}