From 9da1e01e07691836c4f910ec1180926a956b18b9 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 27 Dec 2024 12:14:17 -0500 Subject: [PATCH 01/11] trim internal stacktraces --- .../com/oracle/svm/core/SubstrateOptions.java | 3 + .../oracle/svm/core/code/FrameSourceInfo.java | 1 + .../doc-files/JfrTrimInternalStackTraces.txt | 3 + .../com/oracle/svm/core/jfr/JfrManager.java | 2 +- .../svm/core/jfr/JfrStackTraceRepository.java | 10 +++ .../SamplerJfrStackTraceSerializer.java | 8 ++- .../oracle/svm/test/jfr/AbstractJfrTest.java | 6 ++ .../svm/test/jfr/TestTrimStackTraces.java | 67 +++++++++++++++++++ .../svm/test/jfr/events/StackTraceEvent.java | 2 + 9 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 33e3e3e347ab..52dbe9bde7dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1145,6 +1145,9 @@ public Boolean getValue(OptionValues values) { @Option(help = "file:doc-files/StartFlightRecordingHelp.txt")// public static final RuntimeOptionKey StartFlightRecording = new RuntimeOptionKey<>("", Immutable); + @Option(help = "file:doc-files/JfrTrimInternalStackTraces.txt")// + public static final RuntimeOptionKey JfrTrimInternalStackTraces = new RuntimeOptionKey<>(true); + @Option(help = "file:doc-files/FlightRecorderLoggingHelp.txt")// public static final RuntimeOptionKey FlightRecorderLogging = new RuntimeOptionKey<>("all=warning", Immutable); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java index e0c9285e029d..a7172ae05365 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java @@ -68,6 +68,7 @@ public Class getSourceClass() { return sourceClass; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public String getSourceClassName() { Class clazz = getSourceClass(); return (clazz != null) ? clazz.getName() : ""; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt new file mode 100644 index 000000000000..14ff49633c24 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt @@ -0,0 +1,3 @@ +Determine whether to trim internal frames from JFR stacktraces. Default is true. + +Usage: -XX:JfrTrimInternalStackTraces=false diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java index 359c869c7955..bc851dfd95a8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java @@ -84,7 +84,7 @@ public static RuntimeSupport.Hook initializationHook() { public static RuntimeSupport.Hook startupHook() { return isFirstIsolate -> { periodicEventSetup(); - + SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(SubstrateOptions.JfrTrimInternalStackTraces.getValue()); boolean startRecording = SubstrateOptions.FlightRecorder.getValue() || !SubstrateOptions.StartFlightRecording.getValue().isEmpty(); if (startRecording) { initRecording(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java index 6db818c4cfe3..c0fcdf4fd619 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java @@ -71,6 +71,7 @@ public class JfrStackTraceRepository implements JfrRepository { private final JfrStackTraceEpochData epochData1; private int stackTraceDepth; + private boolean trimInternalStackTraces; @Platforms(Platform.HOSTED_ONLY.class) JfrStackTraceRepository() { @@ -89,6 +90,15 @@ public int getStackTraceDepth() { return stackTraceDepth; } + public void setTrimInternalStackTraces(boolean value) { + trimInternalStackTraces = value; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public boolean getTrimInternalStackTraces() { + return trimInternalStackTraces; + } + public void teardown() { epochData0.teardown(); epochData1.teardown(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index 7f1cc49c86d7..da90bb60ff51 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -38,6 +38,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.UntetheredCodeInfo; +import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jfr.JfrBuffer; import com.oracle.svm.core.jfr.JfrFrameType; import com.oracle.svm.core.jfr.JfrNativeEventWriter; @@ -56,6 +57,7 @@ public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSerializer { /** This value is used by multiple threads but only by a single thread at a time. */ private static final CodeInfoDecoder.FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder.FrameInfoCursor(); + private static final String SUBSTRATEVM_PREFIX = "com.oracle.svm"; @Override @Uninterruptible(reason = "Prevent JFR recording and epoch change.") @@ -167,8 +169,12 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo, int numStackTraceElements = 0; FRAME_INFO_CURSOR.initialize(codeInfo, ip, false); while (FRAME_INFO_CURSOR.advance()) { + FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); + if (SubstrateJVM.getStackTraceRepo().getTrimInternalStackTraces() && + UninterruptibleUtils.String.startsWith(frame.getSourceClassName(), SUBSTRATEVM_PREFIX)) { + continue; + } if (data.isNonNull()) { - FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); serializeStackTraceElement(data, frame); } numStackTraceElements++; diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java index 3d1ba5c5da32..4ab50ae52e69 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java @@ -44,6 +44,7 @@ import org.graalvm.nativeimage.hosted.RuntimeProxyCreation; import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.BeforeClass; import com.oracle.svm.core.jfr.HasJfrSupport; @@ -65,6 +66,11 @@ public static void checkForJFR() { assumeTrue("skipping JFR tests", !ImageInfo.inImageCode() || HasJfrSupport.get()); } + @Before + public void disableTrimStacktraces() { + SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(false); + } + @After public void deleteTemporaryFiles() throws Throwable { if (!isDebuggingEnabled()) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java new file mode 100644 index 000000000000..fbe4826514c4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2025, 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 com.oracle.svm.test.jfr.events.StackTraceEvent; +import com.oracle.svm.core.jfr.SubstrateJVM; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestTrimStackTraces extends JfrRecordingTest { + @Rule public TestName testName = new TestName(); + + @Test + public void test() throws Throwable { + String[] events = new String[]{"com.jfr.StackTrace"}; + SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(true); + Recording recording = startRecording(events); + + StackTraceEvent event = new StackTraceEvent(); + event.commit(); + + stopRecording(recording, this::validateEvents); + } + + private void validateEvents(List events) { + assertEquals(1, events.size()); + RecordedEvent event = events.getFirst(); + List frames = event.getStackTrace().getFrames(); + assertTrue(frames.size() > 0); + assertFalse(frames.stream().anyMatch(e -> testName.getMethodName().equals(e.getMethod().getName()))); + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java index 6deb2a40b0cb..38fddb9c57e4 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java @@ -28,8 +28,10 @@ import jdk.jfr.Description; import jdk.jfr.Event; import jdk.jfr.Label; +import jdk.jfr.Name; import jdk.jfr.StackTrace; +@Name("com.jfr.StackTrace") @Label("Stacktrace Event") @Description("An event with a stacktrace payload") @StackTrace() From 9b3f21dc464264af89aca4afc04d102cfa9b2ef8 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 27 Dec 2024 13:48:48 -0500 Subject: [PATCH 02/11] fix help text --- .../oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt index 14ff49633c24..88795c048e70 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt @@ -1,3 +1,3 @@ Determine whether to trim internal frames from JFR stacktraces. Default is true. -Usage: -XX:JfrTrimInternalStackTraces=false +Usage: -XX:-JfrTrimInternalStackTraces From 5e2568b80ea2b7fdb18096c89780381178258dae Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 27 Dec 2024 14:21:17 -0500 Subject: [PATCH 03/11] fix tests --- .../src/com/oracle/svm/test/jfr/TestTrimStackTraces.java | 2 +- .../src/com/oracle/svm/test/jfr/events/StackTraceEvent.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java index fbe4826514c4..8d9d54462ee0 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java @@ -47,7 +47,7 @@ public class TestTrimStackTraces extends JfrRecordingTest { @Test public void test() throws Throwable { - String[] events = new String[]{"com.jfr.StackTrace"}; + String[] events = new String[]{StackTraceEvent.class.getName()}; SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(true); Recording recording = startRecording(events); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java index 38fddb9c57e4..6deb2a40b0cb 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/events/StackTraceEvent.java @@ -28,10 +28,8 @@ import jdk.jfr.Description; import jdk.jfr.Event; import jdk.jfr.Label; -import jdk.jfr.Name; import jdk.jfr.StackTrace; -@Name("com.jfr.StackTrace") @Label("Stacktrace Event") @Description("An event with a stacktrace payload") @StackTrace() From a8101b2a27ce68a6bd4cb7edc165a57cca309658 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 6 Jan 2025 09:09:55 -0500 Subject: [PATCH 04/11] Update substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java Co-authored-by: Fabio Niephaus --- .../src/com/oracle/svm/core/SubstrateOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 52dbe9bde7dd..f3f9f91af225 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1145,7 +1145,7 @@ public Boolean getValue(OptionValues values) { @Option(help = "file:doc-files/StartFlightRecordingHelp.txt")// public static final RuntimeOptionKey StartFlightRecording = new RuntimeOptionKey<>("", Immutable); - @Option(help = "file:doc-files/JfrTrimInternalStackTraces.txt")// + @Option(help = "Determine whether to trim internal frames from JFR stacktraces (defaults to true).")// public static final RuntimeOptionKey JfrTrimInternalStackTraces = new RuntimeOptionKey<>(true); @Option(help = "file:doc-files/FlightRecorderLoggingHelp.txt")// From c16e3d8109b220e86704eda9225b610919afbe0d Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 6 Jan 2025 09:56:08 -0500 Subject: [PATCH 05/11] remove doc file --- .../oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt deleted file mode 100644 index 88795c048e70..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/JfrTrimInternalStackTraces.txt +++ /dev/null @@ -1,3 +0,0 @@ -Determine whether to trim internal frames from JFR stacktraces. Default is true. - -Usage: -XX:-JfrTrimInternalStackTraces From 2a1c556cef56df2de6b5cb2f58d056e35eebb755 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 6 Jan 2025 15:38:53 -0500 Subject: [PATCH 06/11] use a Hosted option instead --- substratevm/mx.substratevm/mx_substratevm.py | 1 + .../com/oracle/svm/core/SubstrateOptions.java | 3 - .../oracle/svm/core/VMInspectionOptions.java | 3 + .../com/oracle/svm/core/jfr/JfrManager.java | 1 - .../svm/core/jfr/JfrStackTraceRepository.java | 10 --- .../SamplerJfrStackTraceSerializer.java | 3 +- .../oracle/svm/test/jfr/AbstractJfrTest.java | 6 -- .../svm/test/jfr/TestTrimStackTraces.java | 67 ------------------- 8 files changed, 6 insertions(+), 88 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index d36e4d5d043c..25f70eb1b31a 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -545,6 +545,7 @@ def native_unittests_task(extra_build_args=None): out.write(f"Simple file{i}" + '\n') additional_build_args = svm_experimental_options([ + '-H:-JfrTrimInternalStackTraces', '-H:AdditionalSecurityProviders=com.oracle.svm.test.SecurityServiceTest$NoOpProvider', '-H:AdditionalSecurityServiceTypes=com.oracle.svm.test.SecurityServiceTest$JCACompliantNoOpService', '-cp', cp_entry_name diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index f3f9f91af225..33e3e3e347ab 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1145,9 +1145,6 @@ public Boolean getValue(OptionValues values) { @Option(help = "file:doc-files/StartFlightRecordingHelp.txt")// public static final RuntimeOptionKey StartFlightRecording = new RuntimeOptionKey<>("", Immutable); - @Option(help = "Determine whether to trim internal frames from JFR stacktraces (defaults to true).")// - public static final RuntimeOptionKey JfrTrimInternalStackTraces = new RuntimeOptionKey<>(true); - @Option(help = "file:doc-files/FlightRecorderLoggingHelp.txt")// public static final RuntimeOptionKey FlightRecorderLogging = new RuntimeOptionKey<>("all=warning", Immutable); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java index 114c811bafa1..a1800e21dfb8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java @@ -89,6 +89,9 @@ public final class VMInspectionOptions { AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter(), VMInspectionOptions::validateEnableMonitoringFeatures); + @Option(help = "Determine whether to trim internal frames from JFR stacktraces (defaults to true).")// + public static final HostedOptionKey JfrTrimInternalStackTraces = new HostedOptionKey<>(true, VMInspectionOptions::notSupportedOnWindows); + @Option(help = "Dumps all runtime compiled methods on SIGUSR2.", type = OptionType.User) // public static final HostedOptionKey DumpRuntimeCompilationOnSignal = new HostedOptionKey<>(false, VMInspectionOptions::notSupportedOnWindows); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java index bc851dfd95a8..3457e6aada27 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java @@ -84,7 +84,6 @@ public static RuntimeSupport.Hook initializationHook() { public static RuntimeSupport.Hook startupHook() { return isFirstIsolate -> { periodicEventSetup(); - SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(SubstrateOptions.JfrTrimInternalStackTraces.getValue()); boolean startRecording = SubstrateOptions.FlightRecorder.getValue() || !SubstrateOptions.StartFlightRecording.getValue().isEmpty(); if (startRecording) { initRecording(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java index c0fcdf4fd619..6db818c4cfe3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java @@ -71,7 +71,6 @@ public class JfrStackTraceRepository implements JfrRepository { private final JfrStackTraceEpochData epochData1; private int stackTraceDepth; - private boolean trimInternalStackTraces; @Platforms(Platform.HOSTED_ONLY.class) JfrStackTraceRepository() { @@ -90,15 +89,6 @@ public int getStackTraceDepth() { return stackTraceDepth; } - public void setTrimInternalStackTraces(boolean value) { - trimInternalStackTraces = value; - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean getTrimInternalStackTraces() { - return trimInternalStackTraces; - } - public void teardown() { epochData0.teardown(); epochData1.teardown(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index da90bb60ff51..98b179fee93f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.sampler; +import com.oracle.svm.core.VMInspectionOptions; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.type.CIntPointer; @@ -170,7 +171,7 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo, FRAME_INFO_CURSOR.initialize(codeInfo, ip, false); while (FRAME_INFO_CURSOR.advance()) { FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); - if (SubstrateJVM.getStackTraceRepo().getTrimInternalStackTraces() && + if (VMInspectionOptions.JfrTrimInternalStackTraces.getValue() && UninterruptibleUtils.String.startsWith(frame.getSourceClassName(), SUBSTRATEVM_PREFIX)) { continue; } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java index 4ab50ae52e69..3d1ba5c5da32 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/AbstractJfrTest.java @@ -44,7 +44,6 @@ import org.graalvm.nativeimage.hosted.RuntimeProxyCreation; import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.BeforeClass; import com.oracle.svm.core.jfr.HasJfrSupport; @@ -66,11 +65,6 @@ public static void checkForJFR() { assumeTrue("skipping JFR tests", !ImageInfo.inImageCode() || HasJfrSupport.get()); } - @Before - public void disableTrimStacktraces() { - SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(false); - } - @After public void deleteTemporaryFiles() throws Throwable { if (!isDebuggingEnabled()) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java deleted file mode 100644 index 8d9d54462ee0..000000000000 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestTrimStackTraces.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2025, 2025, 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 com.oracle.svm.test.jfr.events.StackTraceEvent; -import com.oracle.svm.core.jfr.SubstrateJVM; - -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordedEvent; -import jdk.jfr.consumer.RecordedFrame; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class TestTrimStackTraces extends JfrRecordingTest { - @Rule public TestName testName = new TestName(); - - @Test - public void test() throws Throwable { - String[] events = new String[]{StackTraceEvent.class.getName()}; - SubstrateJVM.getStackTraceRepo().setTrimInternalStackTraces(true); - Recording recording = startRecording(events); - - StackTraceEvent event = new StackTraceEvent(); - event.commit(); - - stopRecording(recording, this::validateEvents); - } - - private void validateEvents(List events) { - assertEquals(1, events.size()); - RecordedEvent event = events.getFirst(); - List frames = event.getStackTrace().getFrames(); - assertTrue(frames.size() > 0); - assertFalse(frames.stream().anyMatch(e -> testName.getMethodName().equals(e.getMethod().getName()))); - } -} From 24f3a22492686b0e2e1195d6790d053ae3b03db8 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 6 Jan 2025 15:41:21 -0500 Subject: [PATCH 07/11] imports style --- .../svm/core/sampler/SamplerJfrStackTraceSerializer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index 98b179fee93f..bcf8762e2c2b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -25,7 +25,6 @@ package com.oracle.svm.core.sampler; -import com.oracle.svm.core.VMInspectionOptions; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.type.CIntPointer; @@ -50,6 +49,8 @@ import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.events.ExecutionSampleEvent; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.VMInspectionOptions; + /** * A concrete implementation of {@link SamplerStackTraceSerializer} designed for JFR stack trace From c27627a041596fcc30c44055d30c1c8f6768f595 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 6 Jan 2025 15:54:34 -0500 Subject: [PATCH 08/11] checkstyle fix --- .../oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index f6676245f6d0..5098da9f2649 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -51,7 +51,6 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.core.VMInspectionOptions; - /** * A concrete implementation of {@link SamplerStackTraceSerializer} designed for JFR stack trace * serialization. From 936b8fcca8751dfc8da7b03a063db3df1aea0a69 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Wed, 8 Jan 2025 16:50:44 -0500 Subject: [PATCH 09/11] use skipcount and add new tests --- substratevm/mx.substratevm/mx_substratevm.py | 1 - .../oracle/svm/core/code/FrameSourceInfo.java | 1 - .../src/com/oracle/svm/core/jfr/JfrEvent.java | 39 ++++--- .../com/oracle/svm/core/jfr/JfrManager.java | 1 + .../com/oracle/svm/core/jfr/SubstrateJVM.java | 4 +- .../events/AllocationRequiringGCEvent.java | 2 +- .../jfr/events/JavaMonitorEnterEvent.java | 2 +- .../jfr/events/JavaMonitorInflateEvent.java | 2 +- .../core/jfr/events/JavaMonitorWaitEvent.java | 2 +- .../core/jfr/events/JfrAllocationEvents.java | 4 +- .../svm/core/jfr/events/SystemGCEvent.java | 2 +- .../svm/core/jfr/events/ThreadParkEvent.java | 2 +- .../svm/core/jfr/events/ThreadStartEvent.java | 2 +- .../jfr/oldobject/JfrOldObjectSampler.java | 2 +- .../SamplerJfrStackTraceSerializer.java | 9 +- .../oracle/svm/test/jfr/JfrRecordingTest.java | 11 ++ .../jfr/TestAllocationRequiringGCEvent.java | 3 + .../test/jfr/TestJavaMonitorEnterEvent.java | 2 + .../test/jfr/TestJavaMonitorInflateEvent.java | 2 + .../test/jfr/TestJavaMonitorWaitEvent.java | 2 + .../TestJavaMonitorWaitInterruptEvent.java | 2 + .../TestJavaMonitorWaitNotifyAllEvent.java | 1 + .../jfr/TestJavaMonitorWaitTimeoutEvent.java | 1 + .../TestObjectAllocationInNewTLABEvent.java | 2 + .../jfr/TestObjectAllocationSampleEvent.java | 100 ++++++++++++++++++ .../svm/test/jfr/TestSystemGCEvent.java | 2 + .../svm/test/jfr/TestThreadParkEvents.java | 6 ++ .../svm/test/jfr/TestThreadStartEvents.java | 69 ++++++++++++ .../test/jfr/oldobject/JfrOldObjectTest.java | 2 + 29 files changed, 246 insertions(+), 34 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadStartEvents.java diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index fd39fe87e4b3..6f7238f60a35 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -545,7 +545,6 @@ def native_unittests_task(extra_build_args=None): out.write(f"Simple file{i}" + '\n') additional_build_args = svm_experimental_options([ - '-H:-JfrTrimInternalStackTraces', '-H:AdditionalSecurityProviders=com.oracle.svm.test.services.SecurityServiceTest$NoOpProvider', '-H:AdditionalSecurityServiceTypes=com.oracle.svm.test.services.SecurityServiceTest$JCACompliantNoOpService', '-cp', cp_entry_name diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java index a7172ae05365..e0c9285e029d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameSourceInfo.java @@ -68,7 +68,6 @@ public Class getSourceClass() { return sourceClass; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public String getSourceClassName() { Class clazz = getSourceClass(); return (clazz != null) ? clazz.getName() : ""; 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 49c4355ae3a8..b29e2660b486 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 @@ -32,13 +32,16 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.collections.EnumBitmask; import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.VMInspectionOptions; /** * This file contains the VM-level events that Native Image supports on all JDK versions. The event * IDs depend on the JDK version (see metadata.xml file) and are computed at image build time. */ public final class JfrEvent { - public static final JfrEvent ThreadStart = create("jdk.ThreadStart"); + private static final int defaultInternalSkipCount = VMInspectionOptions.JfrTrimInternalStackTraces.getValue() ? 3 : 0; + + public static final JfrEvent ThreadStart = create("jdk.ThreadStart", defaultInternalSkipCount); public static final JfrEvent ThreadEnd = create("jdk.ThreadEnd"); public static final JfrEvent ThreadCPULoad = create("jdk.ThreadCPULoad"); public static final JfrEvent DataLoss = create("jdk.DataLoss"); @@ -60,34 +63,41 @@ public final class JfrEvent { public static final JfrEvent SafepointBegin = create("jdk.SafepointBegin", JfrEventFlags.HasDuration); public static final JfrEvent SafepointEnd = create("jdk.SafepointEnd", JfrEventFlags.HasDuration); public static final JfrEvent ExecuteVMOperation = create("jdk.ExecuteVMOperation", JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorEnter = create("jdk.JavaMonitorEnter", JfrEventFlags.HasDuration); - public static final JfrEvent ThreadPark = create("jdk.ThreadPark", JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait", JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate", JfrEventFlags.HasDuration); - public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB"); + public static final JfrEvent JavaMonitorEnter = create("jdk.JavaMonitorEnter", defaultInternalSkipCount, JfrEventFlags.HasDuration); + public static final JfrEvent ThreadPark = create("jdk.ThreadPark", defaultInternalSkipCount, JfrEventFlags.HasDuration); + public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait", defaultInternalSkipCount, JfrEventFlags.HasDuration); + public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate", defaultInternalSkipCount, JfrEventFlags.HasDuration); + public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB", defaultInternalSkipCount); public static final JfrEvent GCHeapSummary = create("jdk.GCHeapSummary"); public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics"); - public static final JfrEvent SystemGC = create("jdk.SystemGC", JfrEventFlags.HasDuration); - public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC"); - public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample"); - public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", JfrEventFlags.SupportsThrottling); + public static final JfrEvent SystemGC = create("jdk.SystemGC", defaultInternalSkipCount, JfrEventFlags.HasDuration); + public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC", defaultInternalSkipCount); + public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample", defaultInternalSkipCount); + public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", defaultInternalSkipCount, JfrEventFlags.SupportsThrottling); public static final JfrEvent NativeMemoryUsage = create("jdk.NativeMemoryUsage"); public static final JfrEvent NativeMemoryUsageTotal = create("jdk.NativeMemoryUsageTotal"); private final long id; private final String name; private final int flags; + private final int skipCount; @Platforms(Platform.HOSTED_ONLY.class) public static JfrEvent create(String name, JfrEventFlags... flags) { - return new JfrEvent(name, flags); + return new JfrEvent(name, 0, flags); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static JfrEvent create(String name, int skipCount, JfrEventFlags... flags) { + return new JfrEvent(name, skipCount, flags); } @Platforms(Platform.HOSTED_ONLY.class) - private JfrEvent(String name, JfrEventFlags... flags) { + private JfrEvent(String name, int skipCount, JfrEventFlags... flags) { this.id = JfrMetadataTypeLibrary.lookupPlatformEvent(name); this.name = name; this.flags = EnumBitmask.computeBitmask(flags); + this.skipCount = skipCount; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @@ -95,6 +105,11 @@ public long getId() { return id; } + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public int getSkipCount() { + return skipCount; + } + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public String getName() { return name; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java index 3457e6aada27..359c869c7955 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrManager.java @@ -84,6 +84,7 @@ public static RuntimeSupport.Hook initializationHook() { public static RuntimeSupport.Hook startupHook() { return isFirstIsolate -> { periodicEventSetup(); + boolean startRecording = SubstrateOptions.FlightRecorder.getValue() || !SubstrateOptions.StartFlightRecording.getValue().isEmpty(); if (startRecording) { initRecording(); 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 d29ec2501445..481544477cf7 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 @@ -293,8 +293,8 @@ public long getStackTraceId(int skipCount) { } @Uninterruptible(reason = "Result is only valid until epoch changes.", callerMustBe = true) - public long getStackTraceId(JfrEvent eventType, int skipCount) { - return getStackTraceId(eventType.getId(), skipCount); + public long getStackTraceId(JfrEvent eventType) { + return getStackTraceId(eventType.getId(), eventType.getSkipCount()); } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java index 1357b40ae495..0f98edbfdb40 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java @@ -54,7 +54,7 @@ private static void emit0(UnsignedWord gcId, UnsignedWord size) { JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.AllocationRequiringGC); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.AllocationRequiringGC, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.AllocationRequiringGC)); JfrNativeEventWriter.putLong(data, gcId.rawValue()); JfrNativeEventWriter.putLong(data, size.rawValue()); 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 671924ba1a48..ff1b6de5428d 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 @@ -56,7 +56,7 @@ public static void emit0(Object obj, long previousOwnerTid, long startTicks) { JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter)); JfrNativeEventWriter.putClass(data, obj.getClass()); JfrNativeEventWriter.putLong(data, previousOwnerTid); JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); 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 d663c70fd559..e0a5f54f709b 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 @@ -57,7 +57,7 @@ public static void emit0(Object obj, long startTicks, MonitorInflationCause caus JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorInflate, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorInflate)); JfrNativeEventWriter.putClass(data, obj.getClass()); JfrNativeEventWriter.putLong(data, Word.objectToUntrackedPointer(obj).rawValue()); JfrNativeEventWriter.putLong(data, getId(cause)); 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 index 27322780e18a..67018d1183d4 100644 --- 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 @@ -58,7 +58,7 @@ private static void emit0(long startTicks, Object obj, long notifier, long timeo JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorWait, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorWait)); JfrNativeEventWriter.putClass(data, obj.getClass()); JfrNativeEventWriter.putLong(data, notifier); JfrNativeEventWriter.putLong(data, timeout); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JfrAllocationEvents.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JfrAllocationEvents.java index 28a4ca1f6df5..5f0a23bb3af5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JfrAllocationEvents.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JfrAllocationEvents.java @@ -68,7 +68,7 @@ private static void emitObjectAllocationInNewTLAB(long startTicks, DynamicHub hu JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ObjectAllocationInNewTLAB); JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ObjectAllocationInNewTLAB, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ObjectAllocationInNewTLAB)); JfrNativeEventWriter.putClass(data, DynamicHub.toClass(hub)); JfrNativeEventWriter.putLong(data, allocationSize.rawValue()); JfrNativeEventWriter.putLong(data, tlabSize.rawValue()); @@ -88,7 +88,7 @@ private static void emitObjectAllocationSample(long startTicks, DynamicHub hub) JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ObjectAllocationSample); JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ObjectAllocationSample, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ObjectAllocationSample)); JfrNativeEventWriter.putClass(data, DynamicHub.toClass(hub)); JfrNativeEventWriter.putLong(data, weight); JfrNativeEventWriter.endSmallEvent(data); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java index 8123b992f3ca..444d07ae1183 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java @@ -55,7 +55,7 @@ private static void emit0(long startTicks, boolean invokedConcurrent) { JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.SystemGC, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.SystemGC)); JfrNativeEventWriter.putBoolean(data, invokedConcurrent); JfrNativeEventWriter.endSmallEvent(data); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadParkEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadParkEvent.java index da810978b7ed..feb255c31711 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadParkEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadParkEvent.java @@ -68,7 +68,7 @@ private static void emit0(long startTicks, Object obj, boolean isAbsolute, long JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadPark, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadPark)); JfrNativeEventWriter.putClass(data, parkedClass); JfrNativeEventWriter.putLong(data, timeout); JfrNativeEventWriter.putLong(data, until); 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 1eb36418442e..b829f32335a5 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 @@ -45,7 +45,7 @@ public static void emit(Thread thread) { JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ThreadStart); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); JfrNativeEventWriter.putEventThread(data); - JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadStart, 0)); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.ThreadStart)); JfrNativeEventWriter.putThread(data, thread); JfrNativeEventWriter.putLong(data, JavaThreads.getParentThreadId(thread)); JfrNativeEventWriter.endSmallEvent(data); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectSampler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectSampler.java index 19258c9da4a3..f79188681600 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectSampler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectSampler.java @@ -148,7 +148,7 @@ private void release(JfrOldObject sample) { private void store(Object obj, UnsignedWord span, UnsignedWord allocatedSize, int arrayLength) { Thread thread = JavaThreads.getCurrentThreadOrNull(); long threadId = thread == null ? 0L : JavaThreads.getThreadId(thread); - long stackTraceId = thread == null ? 0L : SubstrateJVM.get().getStackTraceId(JfrEvent.OldObjectSample, 0); + long stackTraceId = thread == null ? 0L : SubstrateJVM.get().getStackTraceId(JfrEvent.OldObjectSample); UnsignedWord heapUsedAfterLastGC = Heap.getHeap().getUsedMemoryAfterLastGC(); JfrOldObject sample = (JfrOldObject) freeList.pop(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index 5098da9f2649..e5b929a73350 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -38,7 +38,6 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.UntetheredCodeInfo; -import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jfr.JfrBuffer; import com.oracle.svm.core.jfr.JfrFrameType; import com.oracle.svm.core.jfr.JfrNativeEventWriter; @@ -49,7 +48,6 @@ import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.events.ExecutionSampleEvent; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.VMInspectionOptions; /** * A concrete implementation of {@link SamplerStackTraceSerializer} designed for JFR stack trace @@ -58,7 +56,6 @@ public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSerializer { /** This value is used by multiple threads but only by a single thread at a time. */ private static final CodeInfoDecoder.FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder.FrameInfoCursor(); - private static final String SUBSTRATEVM_PREFIX = "com.oracle.svm"; @Override @Uninterruptible(reason = "Prevent JFR recording and epoch change.") @@ -170,12 +167,8 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo, int numStackTraceElements = 0; FRAME_INFO_CURSOR.initialize(codeInfo, ip, false); while (FRAME_INFO_CURSOR.advance()) { - FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); - if (VMInspectionOptions.JfrTrimInternalStackTraces.getValue() && - UninterruptibleUtils.String.startsWith(frame.getSourceClassName(), SUBSTRATEVM_PREFIX)) { - continue; - } if (data.isNonNull()) { + FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); serializeStackTraceElement(data, frame); } numStackTraceElements++; diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrRecordingTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrRecordingTest.java index 231b14bdf8ac..9038b23e6b4a 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrRecordingTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/JfrRecordingTest.java @@ -31,14 +31,19 @@ import java.time.Duration; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; import org.junit.After; import jdk.jfr.Configuration; import jdk.jfr.Recording; +import static org.junit.Assert.assertTrue; + /** Base class for JFR unit tests. */ public abstract class JfrRecordingTest extends AbstractJfrTest { private final Map recordingStates = Collections.synchronizedMap(new IdentityHashMap<>()); @@ -83,6 +88,12 @@ protected Recording prepareRecording(String[] events, Configuration config, Map< return recording; } + protected static void checkStackTraceTrimming(RecordedEvent event, String methodName) { + List frames = event.getStackTrace().getFrames(); + assertTrue(frames.size() > 0); + assertTrue(frames.getFirst().getMethod().getName().equals(methodName)); + } + private static Recording createRecording(Configuration config) { if (config == null) { return new Recording(); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java index a3f675115d8d..4b3129abac08 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java @@ -54,6 +54,9 @@ public void test() throws Throwable { private static void validateEvents(List events) { assertTrue(events.size() > 0); + for (RecordedEvent event : events) { + checkStackTraceTrimming(event, "emit"); + } } @NeverInline("Prevent escape analysis.") diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java index 0d38f527c060..cd50dd5e8a7d 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java @@ -90,6 +90,8 @@ private void validateEvents(List events) { found = true; break; } + + checkStackTraceTrimming(event, "emit"); } assertTrue("Expected monitor blocked event not found", found); } 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 39e761e7595e..f556b17eaf99 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 @@ -90,6 +90,8 @@ private void validateEvents(List events) { (eventThread.equals(firstThread.getName()) || eventThread.equals(secondThread.getName()))) { foundCauseEnter = true; } + + checkStackTraceTrimming(event, "emit"); } assertTrue("Expected monitor inflate event not found.", foundCauseEnter); } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java index d31f508bb3d6..9226acf75414 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java @@ -113,6 +113,8 @@ private void validateEvents(List events) { assertEquals("Wrong notifier", notifThread, producerName); } lastEventThreadName = eventThread; + + checkStackTraceTrimming(event, "emit"); } assertFalse("Wrong number of events: " + prodCount + " " + consCount, abs(prodCount - consCount) > 1 || abs(consCount - COUNT) > 1); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java index 9383d5ada0ef..cd8dfd4aab6a 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java @@ -139,6 +139,8 @@ private void validateEvents(List events) { assertEquals("Notifier of simple wait is incorrect: " + notifThread + " " + simpleNotifyThread.getName(), notifThread, simpleNotifyThread.getName()); simpleWaitFound = true; } + + checkStackTraceTrimming(event, "emit"); } assertTrue("Couldn't find expected wait events. SimpleWaiter: " + simpleWaitFound + " interrupted: " + interruptedFound, simpleWaitFound && interruptedFound); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java index 61e8cbf3ca07..ec45754a9fde 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java @@ -108,6 +108,7 @@ private void validateEvents(List events) { waitersFound++; } + checkStackTraceTrimming(event, "emit"); } assertTrue("Couldn't find expected wait events. NotifierFound: " + notifierFound + " waitersFound: " + waitersFound, notifierFound && waitersFound == 2); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java index 08b57ac7b3fa..fc9f885cb79b 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java @@ -134,6 +134,7 @@ private void validateEvents(List events) { simpleWaitFound = true; } + checkStackTraceTrimming(event, "emit"); } assertTrue("Couldn't find expected wait events. SimpleWaiter: " + simpleWaitFound + " timeout: " + timeoutFound, simpleWaitFound && timeoutFound); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java index 8e5e9157429a..38f8dcf0abec 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java @@ -100,6 +100,8 @@ private static void validateEvents(List events) { } else if (tlabSize == alignedHeapChunkSize && className.equals(Helper.class.getName())) { foundInstance = true; } + + checkStackTraceTrimming(event, "emit"); } assertTrue(foundBigCharArray); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java new file mode 100644 index 000000000000..2ea0d3ba7f38 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2025, 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 com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.genscavenge.HeapParameters; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.util.UnsignedUtils; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertTrue; + +public class TestObjectAllocationSampleEvent extends JfrRecordingTest { + + @Test + public void test() throws Throwable { + String[] events = new String[]{JfrEvent.ObjectAllocationSample.getName()}; + Recording recording = startRecording(events); + + final int alignedHeapChunkSize = UnsignedUtils.safeToInt(HeapParameters.getAlignedHeapChunkSize()); + + // Allocate large arrays (always need a new TLAB). + allocateByteArray(2 * alignedHeapChunkSize); + allocateCharArray(alignedHeapChunkSize); + + stopRecording(recording, TestObjectAllocationSampleEvent::validateEvents); + } + + private static void validateEvents(List events) { + long alignedHeapChunkSize = HeapParameters.getAlignedHeapChunkSize().rawValue(); + + boolean foundByteArray = false; + boolean foundCharArray = false; + + for (RecordedEvent event : events) { + String eventThread = event. getValue("eventThread").getJavaName(); + if (!eventThread.equals("main")) { + continue; + } + + long allocationSize = event. getValue("weight"); + String className = event. getValue("objectClass").getName(); + + // >= To account for size of reference + if (allocationSize >= 2 * alignedHeapChunkSize) { + // verify previous owner + if (className.equals(char[].class.getName())) { + foundCharArray = true; + } else if (className.equals(byte[].class.getName())) { + foundByteArray = true; + } + } + + checkStackTraceTrimming(event, "emit"); + } + + assertTrue(foundCharArray); + assertTrue(foundByteArray); + } + + @NeverInline("Prevent escape analysis.") + private static byte[] allocateByteArray(int length) { + return new byte[length]; + } + + @NeverInline("Prevent escape analysis.") + private static char[] allocateCharArray(int length) { + return new char[length]; + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java index 655607f23bf7..cb7204c77203 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java @@ -57,6 +57,8 @@ private static void validateEvents(List events) { assertNotNull(event.getStartTime()); assertTrue(event.getStartTime().toEpochMilli() <= System.currentTimeMillis()); assertFalse(event.getBoolean("invokedConcurrent")); + + checkStackTraceTrimming(event, "emit"); } } } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java index 6cb1b976081c..d21019b3c07d 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.concurrent.locks.LockSupport; +import jdk.jfr.consumer.RecordedFrame; import org.junit.Test; import com.oracle.svm.core.jfr.JfrEvent; @@ -79,6 +80,11 @@ private static void validateEvents(List events) { boolean parkUnparkFound = false; for (RecordedEvent event : events) { + List frames = event.getStackTrace().getFrames(); + assertTrue(frames.size() > 0); + System.out.println(frames.getFirst().getMethod().getName()); + assertTrue(frames.getFirst().getMethod().getName().equals("emit")); + long timeout = event. getValue("timeout"); long until = event. getValue("until"); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadStartEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadStartEvents.java new file mode 100644 index 000000000000..ee179abd5210 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadStartEvents.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2025, 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 com.oracle.svm.core.jfr.JfrEvent; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.locks.LockSupport; + +import static org.junit.Assert.assertTrue; + +public class TestThreadStartEvents extends JfrRecordingTest { + + private static final String NAME = "worker-name"; + + @Test + public void test() throws Throwable { + String[] events = new String[]{JfrEvent.ThreadStart.getName()}; + Recording recording = startRecording(events); + + Runnable work = () -> LockSupport.parkNanos(1); + Thread worker = new Thread(work); + worker.setName(NAME); + worker.start(); + worker.join(); + stopRecording(recording, TestThreadStartEvents::validateEvents); + } + + private static void validateEvents(List events) { + boolean foundEvent = false; + for (RecordedEvent event : events) { + RecordedThread eventThread = event.getThread("eventThread"); + if (eventThread.getJavaName().equals(NAME)) { + foundEvent = true; + } + + checkStackTraceTrimming(event, "beforeThreadStart"); + } + assertTrue(foundEvent); + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java index a873ee5d79c3..bb9bf4444282 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java @@ -115,6 +115,8 @@ protected List validateEvents(List events, Class 0); assertTrue(frames.stream().anyMatch(e -> testName.getMethodName().equals(e.getMethod().getName()))); + checkStackTraceTrimming(event, "sample"); + long allocationTime = event.getLong("allocationTime"); assertTrue(allocationTime > 0); assertTrue(allocationTime <= startTime); From cab4264fe4e882ad699646d54a8449339bdc4bf1 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 10 Jan 2025 09:52:27 -0500 Subject: [PATCH 10/11] use the same skipcount for all events --- .../src/com/oracle/svm/core/jfr/JfrEvent.java | 35 +++++++------------ .../svm/test/jfr/TestThreadParkEvents.java | 7 +--- 2 files changed, 14 insertions(+), 28 deletions(-) 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 b29e2660b486..80c39dc49656 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 @@ -39,9 +39,7 @@ * IDs depend on the JDK version (see metadata.xml file) and are computed at image build time. */ public final class JfrEvent { - private static final int defaultInternalSkipCount = VMInspectionOptions.JfrTrimInternalStackTraces.getValue() ? 3 : 0; - - public static final JfrEvent ThreadStart = create("jdk.ThreadStart", defaultInternalSkipCount); + public static final JfrEvent ThreadStart = create("jdk.ThreadStart"); public static final JfrEvent ThreadEnd = create("jdk.ThreadEnd"); public static final JfrEvent ThreadCPULoad = create("jdk.ThreadCPULoad"); public static final JfrEvent DataLoss = create("jdk.DataLoss"); @@ -63,41 +61,34 @@ public final class JfrEvent { public static final JfrEvent SafepointBegin = create("jdk.SafepointBegin", JfrEventFlags.HasDuration); public static final JfrEvent SafepointEnd = create("jdk.SafepointEnd", JfrEventFlags.HasDuration); public static final JfrEvent ExecuteVMOperation = create("jdk.ExecuteVMOperation", JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorEnter = create("jdk.JavaMonitorEnter", defaultInternalSkipCount, JfrEventFlags.HasDuration); - public static final JfrEvent ThreadPark = create("jdk.ThreadPark", defaultInternalSkipCount, JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait", defaultInternalSkipCount, JfrEventFlags.HasDuration); - public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate", defaultInternalSkipCount, JfrEventFlags.HasDuration); - public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB", defaultInternalSkipCount); + public static final JfrEvent JavaMonitorEnter = create("jdk.JavaMonitorEnter", JfrEventFlags.HasDuration); + public static final JfrEvent ThreadPark = create("jdk.ThreadPark", JfrEventFlags.HasDuration); + public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait", JfrEventFlags.HasDuration); + public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate", JfrEventFlags.HasDuration); + public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB"); public static final JfrEvent GCHeapSummary = create("jdk.GCHeapSummary"); public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics"); - public static final JfrEvent SystemGC = create("jdk.SystemGC", defaultInternalSkipCount, JfrEventFlags.HasDuration); - public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC", defaultInternalSkipCount); - public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample", defaultInternalSkipCount); - public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", defaultInternalSkipCount, JfrEventFlags.SupportsThrottling); + public static final JfrEvent SystemGC = create("jdk.SystemGC", JfrEventFlags.HasDuration); + public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC"); + public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample"); + public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", JfrEventFlags.SupportsThrottling); public static final JfrEvent NativeMemoryUsage = create("jdk.NativeMemoryUsage"); public static final JfrEvent NativeMemoryUsageTotal = create("jdk.NativeMemoryUsageTotal"); private final long id; private final String name; private final int flags; - private final int skipCount; @Platforms(Platform.HOSTED_ONLY.class) public static JfrEvent create(String name, JfrEventFlags... flags) { - return new JfrEvent(name, 0, flags); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public static JfrEvent create(String name, int skipCount, JfrEventFlags... flags) { - return new JfrEvent(name, skipCount, flags); + return new JfrEvent(name, flags); } @Platforms(Platform.HOSTED_ONLY.class) - private JfrEvent(String name, int skipCount, JfrEventFlags... flags) { + private JfrEvent(String name, JfrEventFlags... flags) { this.id = JfrMetadataTypeLibrary.lookupPlatformEvent(name); this.name = name; this.flags = EnumBitmask.computeBitmask(flags); - this.skipCount = skipCount; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @@ -107,7 +98,7 @@ public long getId() { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public int getSkipCount() { - return skipCount; + return VMInspectionOptions.JfrTrimInternalStackTraces.getValue() ? 3 : 0; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java index d21019b3c07d..cc7b63871733 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.concurrent.locks.LockSupport; -import jdk.jfr.consumer.RecordedFrame; import org.junit.Test; import com.oracle.svm.core.jfr.JfrEvent; @@ -80,11 +79,6 @@ private static void validateEvents(List events) { boolean parkUnparkFound = false; for (RecordedEvent event : events) { - List frames = event.getStackTrace().getFrames(); - assertTrue(frames.size() > 0); - System.out.println(frames.getFirst().getMethod().getName()); - assertTrue(frames.getFirst().getMethod().getName().equals("emit")); - long timeout = event. getValue("timeout"); long until = event. getValue("until"); @@ -102,6 +96,7 @@ private static void validateEvents(List events) { parkUntilFound = true; } } + checkStackTraceTrimming(event, "emit"); } assertTrue(parkNanosFound); From 5e4048d07ec9f82bc76f0b39d369ff59f86b92fd Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 13 Jan 2025 16:03:41 -0500 Subject: [PATCH 11/11] fix skipCount to apply to virtual frames. --- .../src/com/oracle/svm/core/jfr/JfrEvent.java | 18 +++-- .../oracle/svm/core/jfr/JfrStackWalker.java | 13 +-- .../core/sampler/SamplerBuffersAccess.java | 9 ++- .../SamplerJfrStackTraceSerializer.java | 79 +++++++++++++++++-- .../svm/core/sampler/SamplerSampleWriter.java | 4 +- .../sampler/SamplerStackTraceSerializer.java | 3 +- .../jfr/TestAllocationRequiringGCEvent.java | 2 +- .../test/jfr/TestJavaMonitorEnterEvent.java | 2 +- .../test/jfr/TestJavaMonitorInflateEvent.java | 2 +- .../test/jfr/TestJavaMonitorWaitEvent.java | 2 +- .../TestJavaMonitorWaitInterruptEvent.java | 2 +- .../TestJavaMonitorWaitNotifyAllEvent.java | 2 +- .../jfr/TestJavaMonitorWaitTimeoutEvent.java | 2 +- .../TestObjectAllocationInNewTLABEvent.java | 5 +- .../jfr/TestObjectAllocationSampleEvent.java | 5 +- .../svm/test/jfr/TestSystemGCEvent.java | 2 +- .../svm/test/jfr/TestThreadParkEvents.java | 2 +- .../test/jfr/oldobject/JfrOldObjectTest.java | 2 +- 18 files changed, 113 insertions(+), 43 deletions(-) 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 80c39dc49656..e639862c8606 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 @@ -39,7 +39,8 @@ * IDs depend on the JDK version (see metadata.xml file) and are computed at image build time. */ public final class JfrEvent { - public static final JfrEvent ThreadStart = create("jdk.ThreadStart"); + private static final int defaultInternalSkipCount = VMInspectionOptions.JfrTrimInternalStackTraces.getValue() ? 5 : 0; + public static final JfrEvent ThreadStart = create("jdk.ThreadStart", defaultInternalSkipCount - 1); public static final JfrEvent ThreadEnd = create("jdk.ThreadEnd"); public static final JfrEvent ThreadCPULoad = create("jdk.ThreadCPULoad"); public static final JfrEvent DataLoss = create("jdk.DataLoss"); @@ -70,7 +71,7 @@ public final class JfrEvent { public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics"); public static final JfrEvent SystemGC = create("jdk.SystemGC", JfrEventFlags.HasDuration); public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC"); - public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample"); + public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample", 7); public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", JfrEventFlags.SupportsThrottling); public static final JfrEvent NativeMemoryUsage = create("jdk.NativeMemoryUsage"); public static final JfrEvent NativeMemoryUsageTotal = create("jdk.NativeMemoryUsageTotal"); @@ -78,17 +79,24 @@ public final class JfrEvent { private final long id; private final String name; private final int flags; + private final int skipCount; @Platforms(Platform.HOSTED_ONLY.class) public static JfrEvent create(String name, JfrEventFlags... flags) { - return new JfrEvent(name, flags); + return new JfrEvent(name, defaultInternalSkipCount, flags); } @Platforms(Platform.HOSTED_ONLY.class) - private JfrEvent(String name, JfrEventFlags... flags) { + public static JfrEvent create(String name, int skipCount, JfrEventFlags... flags) { + return new JfrEvent(name, skipCount, flags); + } + + @Platforms(Platform.HOSTED_ONLY.class) + private JfrEvent(String name, int skipCount, JfrEventFlags... flags) { this.id = JfrMetadataTypeLibrary.lookupPlatformEvent(name); this.name = name; this.flags = EnumBitmask.computeBitmask(flags); + this.skipCount = skipCount; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @@ -98,7 +106,7 @@ public long getId() { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public int getSkipCount() { - return VMInspectionOptions.JfrTrimInternalStackTraces.getValue() ? 3 : 0; + return skipCount; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackWalker.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackWalker.java index ab264cbfd782..c8fd55cb7cc2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackWalker.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackWalker.java @@ -272,9 +272,7 @@ private static int recordIp(SamplerSampleWriterData data, CodePointer ip) { /* Increment the number of seen frames. */ data.setSeenFrames(data.getSeenFrames() + 1); - if (shouldSkipFrame(data)) { - return NO_ERROR; - } else if (shouldTruncate(data)) { + if (shouldTruncate(data)) { return TRUNCATED; } @@ -288,15 +286,10 @@ private static int recordIp(SamplerSampleWriterData data, CodePointer ip) { return BUFFER_SIZE_EXCEEDED; } - @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - private static boolean shouldSkipFrame(SamplerSampleWriterData data) { - return data.getSeenFrames() <= data.getSkipCount(); - } - @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static boolean shouldTruncate(SamplerSampleWriterData data) { - int numFrames = data.getSeenFrames() - data.getSkipCount(); - if (numFrames > data.getMaxDepth()) { + int maxFrames = data.getMaxDepth() + data.getSkipCount(); + if (data.getSeenFrames() > maxFrames) { /* The stack size exceeds given depth. Stop walk! */ data.setTruncated(true); return true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBuffersAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBuffersAccess.java index d10a6c9569ba..a904f7d2dda4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBuffersAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBuffersAccess.java @@ -115,7 +115,8 @@ private static void serializeStackTraces(SamplerBuffer rawStackTraceBuffer) { int sampleSize = current.readInt(0); current = current.add(Integer.BYTES); - /* Padding. */ + /* Sample size, excluding the header and the end marker. */ + int skipCount = current.readInt(0); current = current.add(Integer.BYTES); /* Tick. */ @@ -132,7 +133,7 @@ private static void serializeStackTraces(SamplerBuffer rawStackTraceBuffer) { assert current.subtract(entryStart).equal(SamplerSampleWriter.getHeaderSize()); - current = serializeStackTrace(current, end, sampleSize, sampleHash, isTruncated, sampleTick, threadId, threadState); + current = serializeStackTrace(current, end, sampleSize, sampleHash, isTruncated, sampleTick, threadId, threadState, skipCount); } SamplerBufferAccess.reinitialize(rawStackTraceBuffer); @@ -140,8 +141,8 @@ private static void serializeStackTraces(SamplerBuffer rawStackTraceBuffer) { @Uninterruptible(reason = "Wraps the call to the possibly interruptible serializer.", calleeMustBe = false) private static Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int sampleSize, int sampleHash, - boolean isTruncated, long sampleTick, long threadId, long threadState) { + boolean isTruncated, long sampleTick, long threadId, long threadState, int skipCount) { return SamplerStackTraceSerializer.singleton().serializeStackTrace(rawStackTrace, bufferEnd, sampleSize, - sampleHash, isTruncated, sampleTick, threadId, threadState); + sampleHash, isTruncated, sampleTick, threadId, threadState, skipCount); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java index e5b929a73350..a78dff172d76 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerJfrStackTraceSerializer.java @@ -57,10 +57,16 @@ public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSe /** This value is used by multiple threads but only by a single thread at a time. */ private static final CodeInfoDecoder.FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder.FrameInfoCursor(); + /* + * This is static so that a single instance can be preallocated and reused. Only one thread ever + * serializes at a given time. + */ + private static final FrameCountData FRAME_COUNT_DATA = new FrameCountData(); + @Override @Uninterruptible(reason = "Prevent JFR recording and epoch change.") public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int sampleSize, int sampleHash, - boolean isTruncated, long sampleTick, long threadId, long threadState) { + boolean isTruncated, long sampleTick, long threadId, long threadState, int skipCount) { Pointer current = rawStackTrace; CIntPointer statusPtr = StackValue.get(CIntPointer.class); JfrStackTraceRepository.JfrStackTraceTableEntry entry = SubstrateJVM.getStackTraceRepo().getOrPutStackTrace(current, Word.unsigned(sampleSize), sampleHash, statusPtr); @@ -70,7 +76,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int if (status == JfrStackTraceRepository.JfrStackTraceTableEntryStatus.INSERTED || status == JfrStackTraceRepository.JfrStackTraceTableEntryStatus.EXISTING_RAW) { /* Walk the IPs and serialize the stacktrace. */ assert current.add(sampleSize).belowThan(bufferEnd); - boolean serialized = serializeStackTrace(current, sampleSize, isTruncated, stackTraceId); + boolean serialized = serializeStackTrace(current, sampleSize, isTruncated, stackTraceId, skipCount); if (serialized) { SubstrateJVM.getStackTraceRepo().commitSerializedStackTrace(entry); } @@ -100,7 +106,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int } @Uninterruptible(reason = "Prevent JFR recording and epoch change.") - private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize, boolean isTruncated, long stackTraceId) { + private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize, boolean isTruncated, long stackTraceId, int skipCount) { assert sampleSize % Long.BYTES == 0; JfrBuffer targetBuffer = SubstrateJVM.getStackTraceRepo().getCurrentBuffer(); @@ -112,8 +118,10 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize * One IP may correspond to multiple Java-level stack frames. We need to precompute the * number of stack trace elements because the count can't be patched later on * (JfrNativeEventWriter.putInt() would not necessarily reserve enough bytes). + * + * The first pass-through also sets FRAME_COUNT_DATA.isTruncated(). */ - int numStackTraceElements = visitRawStackTrace(rawStackTrace, sampleSize, Word.nullPointer()); + int numStackTraceElements = visitRawStackTrace(rawStackTrace, sampleSize, Word.nullPointer(), skipCount); if (numStackTraceElements == 0) { return false; } @@ -121,9 +129,9 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initialize(data, targetBuffer); JfrNativeEventWriter.putLong(data, stackTraceId); - JfrNativeEventWriter.putBoolean(data, isTruncated); + JfrNativeEventWriter.putBoolean(data, isTruncated || FRAME_COUNT_DATA.isTruncated()); JfrNativeEventWriter.putInt(data, numStackTraceElements); - visitRawStackTrace(rawStackTrace, sampleSize, data); + visitRawStackTrace(rawStackTrace, sampleSize, data, skipCount); boolean success = JfrNativeEventWriter.commit(data); /* Buffer can get replaced with a larger one. */ @@ -132,10 +140,14 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize } @Uninterruptible(reason = "Prevent JFR recording and epoch change.") - private static int visitRawStackTrace(Pointer rawStackTrace, int sampleSize, JfrNativeEventWriterData data) { + private static int visitRawStackTrace(Pointer rawStackTrace, int sampleSize, JfrNativeEventWriterData data, int skipCount) { int numStackTraceElements = 0; Pointer rawStackTraceEnd = rawStackTrace.add(sampleSize); Pointer ipPtr = rawStackTrace; + + // Reset FrameCountData before every serialization of a new stacktrace. + FRAME_COUNT_DATA.reset(skipCount); + while (ipPtr.belowThan(rawStackTraceEnd)) { long ip = ipPtr.readLong(0); numStackTraceElements += visitFrame(data, ip); @@ -167,10 +179,18 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo, int numStackTraceElements = 0; FRAME_INFO_CURSOR.initialize(codeInfo, ip, false); while (FRAME_INFO_CURSOR.advance()) { + if (FRAME_COUNT_DATA.shouldSkip()) { + FRAME_COUNT_DATA.incrementSkipped(); + continue; + } else if (FRAME_COUNT_DATA.shouldTruncate()) { + FRAME_COUNT_DATA.setTruncated(); + break; + } if (data.isNonNull()) { FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get(); serializeStackTraceElement(data, frame); } + FRAME_COUNT_DATA.incrementTotal(); numStackTraceElements++; } return numStackTraceElements; @@ -185,4 +205,49 @@ private static void serializeStackTraceElement(JfrNativeEventWriterData data, Fr JfrNativeEventWriter.putInt(data, stackTraceElement.getBci()); JfrNativeEventWriter.putLong(data, JfrFrameType.FRAME_AOT_COMPILED.getId()); } + + private static final class FrameCountData { + private int skipcount; + private int totalCount; + private int skippedCount; + private boolean truncated; + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void reset(int skipCount) { + this.skipcount = skipCount; + totalCount = 0; + skippedCount = 0; + truncated = false; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) // + public boolean shouldSkip() { + return skippedCount < skipcount; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) // + public boolean shouldTruncate() { + return totalCount > SubstrateJVM.getStackTraceRepo().getStackTraceDepth(); + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void setTruncated() { + truncated = true; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public boolean isTruncated() { + return truncated; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void incrementSkipped() { + skippedCount++; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void incrementTotal() { + totalCount++; + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerSampleWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerSampleWriter.java index 322b3bceac11..bfb9c3a5b0a5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerSampleWriter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerSampleWriter.java @@ -62,8 +62,8 @@ public static void begin(SamplerSampleWriterData data) { SamplerSampleWriter.putInt(data, 0); /* Sample size. (will be patched later) */ SamplerSampleWriter.putInt(data, 0); - /* Padding so that the long values below are aligned. */ - SamplerSampleWriter.putInt(data, 0); + /* Skipcount will be used later during serialization. */ + SamplerSampleWriter.putInt(data, data.getSkipCount()); SamplerSampleWriter.putLong(data, JfrTicks.elapsedTicks()); SamplerSampleWriter.putLong(data, SubstrateJVM.getCurrentThreadId()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerStackTraceSerializer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerStackTraceSerializer.java index f69df38bb144..001107433a1b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerStackTraceSerializer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerStackTraceSerializer.java @@ -52,8 +52,9 @@ static SamplerStackTraceSerializer singleton() { * @param sampleTick The timestamp of the sample. * @param threadId A unique identifier for the sampled thread. * @param threadState The state of the sampled thread. + * @param skipCount The number of top frames to omit. * @return A pointer to the next stack trace entry or the end of the buffer. */ Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int sampleSize, int sampleHash, - boolean isTruncated, long sampleTick, long threadId, long threadState); + boolean isTruncated, long sampleTick, long threadId, long threadState, int skipCount); } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java index 4b3129abac08..131334bacfa1 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java @@ -55,7 +55,7 @@ public void test() throws Throwable { private static void validateEvents(List events) { assertTrue(events.size() > 0); for (RecordedEvent event : events) { - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "maybeCollectOnAllocation"); } } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java index cd50dd5e8a7d..a772add00f37 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorEnterEvent.java @@ -91,7 +91,7 @@ private void validateEvents(List events) { break; } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "monitorEnter"); } assertTrue("Expected monitor blocked event not found", found); } 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 f556b17eaf99..4793541c9f21 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 @@ -91,7 +91,7 @@ private void validateEvents(List events) { foundCauseEnter = true; } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "monitorEnter"); } assertTrue("Expected monitor inflate event not found.", foundCauseEnter); } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java index 9226acf75414..7761cc613f9a 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitEvent.java @@ -114,7 +114,7 @@ private void validateEvents(List events) { } lastEventThreadName = eventThread; - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "await"); } assertFalse("Wrong number of events: " + prodCount + " " + consCount, abs(prodCount - consCount) > 1 || abs(consCount - COUNT) > 1); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java index cd8dfd4aab6a..40b5fa6f1c20 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitInterruptEvent.java @@ -140,7 +140,7 @@ private void validateEvents(List events) { simpleWaitFound = true; } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "await"); } assertTrue("Couldn't find expected wait events. SimpleWaiter: " + simpleWaitFound + " interrupted: " + interruptedFound, simpleWaitFound && interruptedFound); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java index ec45754a9fde..5d2b59b66c6c 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitNotifyAllEvent.java @@ -108,7 +108,7 @@ private void validateEvents(List events) { waitersFound++; } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "await"); } assertTrue("Couldn't find expected wait events. NotifierFound: " + notifierFound + " waitersFound: " + waitersFound, notifierFound && waitersFound == 2); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java index fc9f885cb79b..15b0bdba689b 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJavaMonitorWaitTimeoutEvent.java @@ -134,7 +134,7 @@ private void validateEvents(List events) { simpleWaitFound = true; } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "await"); } assertTrue("Couldn't find expected wait events. SimpleWaiter: " + simpleWaitFound + " timeout: " + timeoutFound, simpleWaitFound && timeoutFound); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java index 38f8dcf0abec..7f5158848415 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationInNewTLABEvent.java @@ -95,13 +95,14 @@ private static void validateEvents(List events) { } else if (className.equals(byte[].class.getName())) { foundBigByteArray = true; } + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); } else if (allocationSize >= K && tlabSize == alignedHeapChunkSize && className.equals(byte[].class.getName())) { foundSmallByteArray = true; + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); } else if (tlabSize == alignedHeapChunkSize && className.equals(Helper.class.getName())) { foundInstance = true; + checkStackTraceTrimming(event, "slowPathNewInstanceWithoutAllocating"); } - - checkStackTraceTrimming(event, "emit"); } assertTrue(foundBigCharArray); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java index 2ea0d3ba7f38..1d5d562ebebb 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java @@ -76,12 +76,13 @@ private static void validateEvents(List events) { // verify previous owner if (className.equals(char[].class.getName())) { foundCharArray = true; + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); + } else if (className.equals(byte[].class.getName())) { foundByteArray = true; + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); } } - - checkStackTraceTrimming(event, "emit"); } assertTrue(foundCharArray); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java index cb7204c77203..bec16081ff89 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java @@ -58,7 +58,7 @@ private static void validateEvents(List events) { assertTrue(event.getStartTime().toEpochMilli() <= System.currentTimeMillis()); assertFalse(event.getBoolean("invokedConcurrent")); - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "gc"); } } } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java index cc7b63871733..c1dc183322a4 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadParkEvents.java @@ -96,7 +96,7 @@ private static void validateEvents(List events) { parkUntilFound = true; } } - checkStackTraceTrimming(event, "emit"); + checkStackTraceTrimming(event, "park"); } assertTrue(parkNanosFound); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java index bb9bf4444282..5f549a3d097c 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/oldobject/JfrOldObjectTest.java @@ -115,7 +115,7 @@ protected List validateEvents(List events, Class 0); assertTrue(frames.stream().anyMatch(e -> testName.getMethodName().equals(e.getMethod().getName()))); - checkStackTraceTrimming(event, "sample"); + checkStackTraceTrimming(event, "testSampling"); long allocationTime = event.getLong("allocationTime"); assertTrue(allocationTime > 0);