From a46c6be634debb58bdf99364c79daae84370bee2 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 27 Jan 2022 15:29:13 -0500 Subject: [PATCH] Implementation of GC phase pause events --- .../oracle/svm/core/genscavenge/GCImpl.java | 21 +++- .../core/genscavenge/JfrGCEventSupport.java | 115 ++++++++++++++++++ .../com/oracle/svm/core/jfr/JfrEvents.java | 9 +- 3 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/JfrGCEventSupport.java diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index 374e361323cf..d25c1bdbfb9d 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -207,6 +207,8 @@ private boolean collectImpl(GCCause cause, long requestingNanoTime, boolean forc precondition(); NoAllocationVerifier nav = noAllocationVerifier.open(); + + long startTicks = JfrGCEventSupport.startGCPhasePause(); try { outOfMemory = doCollectImpl(cause, requestingNanoTime, forceFullGC, false); if (outOfMemory) { @@ -219,6 +221,7 @@ private boolean collectImpl(GCCause cause, long requestingNanoTime, boolean forc } } } finally { + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), cause.getName(), startTicks); nav.close(); } @@ -231,14 +234,19 @@ private boolean doCollectImpl(GCCause cause, long requestingNanoTime, boolean fo boolean incremental = !forceNoIncremental && !policy.shouldCollectCompletely(false); boolean outOfMemory = false; + if (incremental) { + long startTicks = JfrGCEventSupport.startGCPhasePause(); outOfMemory = doCollectOnce(cause, requestingNanoTime, false, false); + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Incremental GC", startTicks); } if (!incremental || outOfMemory || forceFullGC || policy.shouldCollectCompletely(incremental)) { if (incremental) { // uncommit unaligned chunks CommittedMemoryProvider.get().afterGarbageCollection(); } + long startTicks = JfrGCEventSupport.startGCPhasePause(); outOfMemory = doCollectOnce(cause, requestingNanoTime, true, incremental); + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Full GC", startTicks); } HeapImpl.getChunkProvider().freeExcessAlignedChunks(); @@ -495,8 +503,10 @@ public boolean isCompleteCollection() { /** Scavenge, either from dirty roots or from all roots, and process discovered references. */ private void scavenge(boolean incremental) { GreyToBlackObjRefVisitor.Counters counters = greyToBlackObjRefVisitor.openCounters(); + long startTicks; try { Timer rootScanTimer = timers.rootScan.open(); + startTicks = JfrGCEventSupport.startGCPhasePause(); try { if (incremental) { cheneyScanFromDirtyRoots(); @@ -504,6 +514,7 @@ private void scavenge(boolean incremental) { cheneyScanFromRoots(); } } finally { + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), incremental ? "Incremental Scan Roots" : "Scan Roots", startTicks); rootScanTimer.close(); } @@ -515,23 +526,28 @@ private void scavenge(boolean incremental) { * operation. To avoid side-effects between the code cache cleaning and the GC * core, it is crucial that all the GC core work finished before. */ + startTicks = JfrGCEventSupport.startGCPhasePause(); cleanRuntimeCodeCache(); } finally { + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Clean Runtime CodeCache", startTicks); cleanCodeCacheTimer.close(); } } Timer referenceObjectsTimer = timers.referenceObjects.open(); try { + startTicks = JfrGCEventSupport.startGCPhasePause(); Reference newlyPendingList = ReferenceObjectProcessing.processRememberedReferences(); HeapImpl.getHeapImpl().addToReferencePendingList(newlyPendingList); } finally { + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Process Remembered References", startTicks); referenceObjectsTimer.close(); } Timer releaseSpacesTimer = timers.releaseSpaces.open(); try { assert chunkReleaser.isEmpty(); + startTicks = JfrGCEventSupport.startGCPhasePause(); releaseSpaces(); /* @@ -542,10 +558,13 @@ private void scavenge(boolean incremental) { boolean keepAllAlignedChunks = incremental; chunkReleaser.release(keepAllAlignedChunks); } finally { + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Release Spaces", startTicks); releaseSpacesTimer.close(); } + startTicks = JfrGCEventSupport.startGCPhasePause(); swapSpaces(); + JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Swap Spaces", startTicks); } finally { counters.close(); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/JfrGCEventSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/JfrGCEventSupport.java new file mode 100644 index 000000000000..f1dda708851d --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/JfrGCEventSupport.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.genscavenge; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.word.UnsignedWord; + +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.jfr.JfrBuffer; +import com.oracle.svm.core.jfr.JfrEnabled; +import com.oracle.svm.core.jfr.JfrEvents; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrThreadLocal; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.SubstrateJVM; +import com.oracle.svm.core.util.VMError; + +class JfrGCEventSupport { + public static final int MAX_PHASE_LEVEL = 4; + private static int currentPhase; + + @Platforms(Platform.HOSTED_ONLY.class) + JfrGCEventSupport() { + } + + public static long startGCPhasePause() { + if (!JfrEnabled.get()) { + return 0; + } + pushPausePhaseLevel(); + return JfrTicks.elapsedTicks(); + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emitGCPhasePauseEvent(UnsignedWord gcEpoch, String name, long startTicks) { + if (!JfrEnabled.get()) { + return; + } + + int level = popPausePhaseLevel(); + JfrEvents event = getGCPhasePauseEvent(level); + if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(event)) { + long end = JfrTicks.elapsedTicks(); + JfrBuffer buffer = ((JfrThreadLocal) SubstrateJVM.getThreadLocal()).getNativeBuffer(); + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initialize(data, buffer); + + JfrNativeEventWriter.beginEventWrite(data, false); + JfrNativeEventWriter.putLong(data, event.getId()); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, end - startTicks); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, gcEpoch.rawValue()); + JfrNativeEventWriter.putString(data, name); + JfrNativeEventWriter.endEventWrite(data, false); + } + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + private static JfrEvents getGCPhasePauseEvent(int level) { + switch (level) { + case 0: + return JfrEvents.GCPhasePauseEvent; + case 1: + return JfrEvents.GCPhasePauseLevel1Event; + case 2: + return JfrEvents.GCPhasePauseLevel2Event; + case 3: + return JfrEvents.GCPhasePauseLevel3Event; + case 4: + return JfrEvents.GCPhasePauseLevel4Event; + default: + throw VMError.shouldNotReachHere("At most " + MAX_PHASE_LEVEL + " levels"); + } + } + + private static void pushPausePhaseLevel() { + assert currentPhase < MAX_PHASE_LEVEL; + currentPhase++; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + private static int popPausePhaseLevel() { + assert currentPhase > 0; + currentPhase--; + return currentPhase; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvents.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvents.java index a0535728acc1..51273dd261ca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvents.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,12 @@ public enum JfrEvents { OSInformation("jdk.OSInformation"), PhysicalMemory("jdk.PhysicalMemory"), ExecutionSample("jdk.ExecutionSample"), - NativeMethodSample("jdk.NativeMethodSample"); + NativeMethodSample("jdk.NativeMethodSample"), + GCPhasePauseEvent("jdk.GCPhasePause"), + GCPhasePauseLevel1Event("jdk.GCPhasePauseLevel1"), + GCPhasePauseLevel2Event("jdk.GCPhasePauseLevel2"), + GCPhasePauseLevel3Event("jdk.GCPhasePauseLevel3"), + GCPhasePauseLevel4Event("jdk.GCPhasePauseLevel4"); private final long id;