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/JfrEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java index 49c4355ae3a8..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 @@ -32,13 +32,15 @@ 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() ? 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"); @@ -69,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"); @@ -77,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) @@ -95,6 +104,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/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/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/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/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..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 @@ -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, "maybeCollectOnAllocation"); + } } @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..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 @@ -90,6 +90,8 @@ private void validateEvents(List events) { found = true; break; } + + 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 39e761e7595e..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 @@ -90,6 +90,8 @@ private void validateEvents(List events) { (eventThread.equals(firstThread.getName()) || eventThread.equals(secondThread.getName()))) { foundCauseEnter = true; } + + 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 d31f508bb3d6..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 @@ -113,6 +113,8 @@ private void validateEvents(List events) { assertEquals("Wrong notifier", notifThread, producerName); } lastEventThreadName = eventThread; + + 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 9383d5ada0ef..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 @@ -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, "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 61e8cbf3ca07..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,6 +108,7 @@ private void validateEvents(List events) { waitersFound++; } + 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 08b57ac7b3fa..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,6 +134,7 @@ private void validateEvents(List events) { simpleWaitFound = true; } + 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 8e5e9157429a..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,10 +95,13 @@ 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"); } } 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..1d5d562ebebb --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestObjectAllocationSampleEvent.java @@ -0,0 +1,101 @@ +/* + * 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; + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); + + } else if (className.equals(byte[].class.getName())) { + foundByteArray = true; + checkStackTraceTrimming(event, "slowPathNewArrayLikeObject0"); + } + } + } + + 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..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 @@ -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, "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 6cb1b976081c..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,6 +96,7 @@ private static void validateEvents(List events) { parkUntilFound = true; } } + checkStackTraceTrimming(event, "park"); } assertTrue(parkNanosFound); 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..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,6 +115,8 @@ protected List validateEvents(List events, Class 0); assertTrue(frames.stream().anyMatch(e -> testName.getMethodName().equals(e.getMethod().getName()))); + checkStackTraceTrimming(event, "testSampling"); + long allocationTime = event.getLong("allocationTime"); assertTrue(allocationTime > 0); assertTrue(allocationTime <= startTime);