diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AuxiliaryImageHeap.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AuxiliaryImageHeap.java index 6b3cfcd7c00e..9045698063cc 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AuxiliaryImageHeap.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AuxiliaryImageHeap.java @@ -49,6 +49,8 @@ static AuxiliaryImageHeap singleton() { void walkObjects(ObjectVisitor visitor); + void walkHeapChunks(HeapChunkVisitor visitor); + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) void walkRegions(MemoryWalker.ImageHeapRegionVisitor visitor); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkLogging.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkLogging.java index 2f8e5e4b8cfb..87c2c53bf52e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkLogging.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkLogging.java @@ -72,7 +72,7 @@ public static void logChunks(Log log, UnalignedHeapChunk.UnalignedHeader firstCh } } - private static void logChunk(Log log, HeapChunk.Header chunk, Pointer bottom, Pointer top, Pointer end, boolean isAligned, String shortSpaceName, boolean isToSpace) { + public static void logChunk(Log log, HeapChunk.Header chunk, Pointer bottom, Pointer top, Pointer end, boolean isAligned, String shortSpaceName, boolean isToSpace) { log.string("|").zhex(chunk).string("|").zhex(bottom).string(", ").zhex(top).string(", ").zhex(end); log.string("|"); if (top.isNonNull()) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkVisitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkVisitor.java new file mode 100644 index 000000000000..be6253cb8ac8 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkVisitor.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.genscavenge; + +import static com.oracle.svm.core.heap.RestrictHeapAccess.Access.NO_ALLOCATION; + +import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; +import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; +import com.oracle.svm.core.heap.RestrictHeapAccess; + +public interface HeapChunkVisitor { + @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate while visiting the heap.") + void visitAlignedChunk(AlignedHeader chunk); + + @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate while visiting the heap.") + void visitUnalignedChunk(UnalignedHeader chunk); +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java index 1f82bbfee8ff..67890d8ae319 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java @@ -49,6 +49,8 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; +import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.metaspace.MetaspaceImpl; import com.oracle.svm.core.genscavenge.remset.RememberedSet; import com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets; @@ -104,6 +106,7 @@ public final class HeapImpl extends Heap { private final GCImpl gcImpl; private final RuntimeCodeInfoGCSupportImpl runtimeCodeInfoGcSupport; private final HeapAccounting accounting = new HeapAccounting(); + private final ImageHeapChunkLogger imageHeapChunkLogger = new ImageHeapChunkLogger(); /** Head of the linked list of currently pending (ready to be enqueued) {@link Reference}s. */ private Reference refPendingList; @@ -284,6 +287,13 @@ void logChunks(Log log, boolean allowUnsafe) { if (Metaspace.isSupported()) { MetaspaceImpl.singleton().logChunks(log); } + imageHeapChunkLogger.initialize(log); + for (ImageHeapInfo info : HeapImpl.getImageHeapInfos()) { + ImageHeapWalker.walkImageHeapChunks(info, imageHeapChunkLogger); + } + if (AuxiliaryImageHeap.isPresent()) { + AuxiliaryImageHeap.singleton().walkHeapChunks(imageHeapChunkLogger); + } getYoungGeneration().logChunks(log, allowUnsafe); getOldGeneration().logChunks(log); getChunkProvider().logFreeChunks(log); @@ -955,6 +965,30 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.indent(false); } } + + private static final class ImageHeapChunkLogger implements HeapChunkVisitor { + private Log log; + + @SuppressWarnings("hiding") + void initialize(Log log) { + this.log = log; + } + + @Override + public void visitAlignedChunk(AlignedHeader chunk) { + Pointer bottom = AlignedHeapChunk.getObjectsStart(chunk); + Pointer top = HeapChunk.getTopPointer(chunk); + Pointer end = AlignedHeapChunk.getObjectsEnd(chunk); + HeapChunkLogging.logChunk(log, chunk, bottom, top, end, true, "I", false); + } + + public void visitUnalignedChunk(UnalignedHeader chunk) { + Pointer bottom = UnalignedHeapChunk.getObjectStart(chunk); + Pointer top = HeapChunk.getTopPointer(chunk); + Pointer end = UnalignedHeapChunk.getObjectEnd(chunk); + HeapChunkLogging.logChunk(log, chunk, bottom, top, end, false, "I", false); + } + } } @TargetClass(value = java.lang.Runtime.class, onlyWith = UseSerialOrEpsilonGC.class) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapWalker.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapWalker.java index 8c8210699f1a..a1bf4ba1ad9c 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapWalker.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapWalker.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.genscavenge; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; @@ -33,6 +35,8 @@ import com.oracle.svm.core.MemoryWalker; import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; +import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectVisitor; import com.oracle.svm.core.hub.LayoutEncoding; @@ -66,6 +70,38 @@ public static void walkImageHeapObjects(ImageHeapInfo heapInfo, ObjectVisitor vi walkPartition(heapInfo.firstReadOnlyHugeObject, heapInfo.lastReadOnlyHugeObject, visitor, false); } + public static void walkImageHeapChunks(ImageHeapInfo heapInfo, HeapChunkVisitor visitor) { + /* Walk all aligned chunks (can only be at the start of the image heap). */ + Object firstObject = heapInfo.firstObject; + if (ObjectHeaderImpl.isAlignedObject(firstObject)) { + HeapChunk.Header alignedChunks = getImageHeapChunkForObject(firstObject, true); + walkChunks(alignedChunks, visitor, true); + } + + /* Walk all unaligned chunks (can only be at the end of the image heap). */ + Object firstUnalignedObject = heapInfo.firstWritableHugeObject; + if (firstUnalignedObject == null) { + firstUnalignedObject = heapInfo.firstReadOnlyHugeObject; + } + if (firstUnalignedObject != null) { + HeapChunk.Header unalignedChunks = getImageHeapChunkForObject(firstUnalignedObject, false); + walkChunks(unalignedChunks, visitor, false); + } + } + + @NeverInline("Not performance critical") + private static void walkChunks(HeapChunk.Header firstChunk, HeapChunkVisitor visitor, boolean alignedChunks) { + HeapChunk.Header currentChunk = firstChunk; + while (currentChunk.isNonNull()) { + if (alignedChunks) { + visitor.visitAlignedChunk((AlignedHeader) currentChunk); + } else { + visitor.visitUnalignedChunk((UnalignedHeader) currentChunk); + } + currentChunk = HeapChunk.getNext(currentChunk); + } + } + @NeverInline("Not performance critical") @Uninterruptible(reason = "Forced inlining (StoredContinuation objects must not move).") static void walkPartition(Object firstObject, Object lastObject, ObjectVisitor visitor, boolean alignedChunks) { @@ -83,16 +119,9 @@ static void walkPartitionInline(Object firstObject, Object lastObject, ObjectVis Pointer lastPointer = Word.objectToUntrackedPointer(lastObject); Pointer current = firstPointer; - /* Compute the enclosing chunk without assuming that the image heap is aligned. */ - Pointer base = Heap.getHeap().getImageHeapStart(); - Pointer offset = current.subtract(base); - UnsignedWord chunkOffset = alignedChunks ? UnsignedUtils.roundDown(offset, HeapParameters.getAlignedHeapChunkAlignment()) - : offset.subtract(UnalignedHeapChunk.getOffsetForObject(current)); - HeapChunk.Header currentChunk = (HeapChunk.Header) chunkOffset.add(base); - // Assumption: the order of chunks in their linked list is the same order as in memory, - // and objects are laid out as a continuous sequence without any gaps. - + // and objects in a chunk are laid out as a continuous sequence without any gaps. + HeapChunk.Header currentChunk = getImageHeapChunkForObject(firstObject, alignedChunks); do { Pointer limit = lastPointer; Pointer chunkTop = HeapChunk.getTopPointer(currentChunk); @@ -106,8 +135,8 @@ static void walkPartitionInline(Object firstObject, Object lastObject, ObjectVis } if (current.belowThan(lastPointer)) { currentChunk = HeapChunk.getNext(currentChunk); - current = alignedChunks ? AlignedHeapChunk.getObjectsStart((AlignedHeapChunk.AlignedHeader) currentChunk) - : UnalignedHeapChunk.getObjectStart((UnalignedHeapChunk.UnalignedHeader) currentChunk); + current = alignedChunks ? AlignedHeapChunk.getObjectsStart((AlignedHeader) currentChunk) + : UnalignedHeapChunk.getObjectStart((UnalignedHeader) currentChunk); // Note: current can be equal to lastPointer now, despite not having visited it yet } } while (current.belowOrEqual(lastPointer)); @@ -118,6 +147,17 @@ static void walkPartitionInline(Object firstObject, Object lastObject, ObjectVis private static void visitObjectInline(ObjectVisitor visitor, Object currentObject) { visitor.visitObject(currentObject); } + + /** Computes the enclosing chunk without assuming that the image heap is aligned. */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static HeapChunk.Header getImageHeapChunkForObject(Object object, boolean alignedChunks) { + Pointer objPtr = Word.objectToUntrackedPointer(object); + Pointer base = Heap.getHeap().getImageHeapStart(); + Pointer offset = objPtr.subtract(base); + UnsignedWord chunkOffset = alignedChunks ? UnsignedUtils.roundDown(offset, HeapParameters.getAlignedHeapChunkAlignment()) + : offset.subtract(UnalignedHeapChunk.getOffsetForObject(objPtr)); + return (HeapChunk.Header) chunkOffset.add(base); + } } abstract class MemoryWalkerAccessBase implements MemoryWalker.NativeImageHeapRegionAccess { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java index eb15ca7af9f3..6260c3129520 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java @@ -36,7 +36,9 @@ import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.os.CommittedMemoryProvider; +import com.oracle.svm.core.util.PointerUtils; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.VMError; @@ -145,7 +147,17 @@ public static long getIsolateId() { @Uninterruptible(reason = "Thread state not yet set up.") public static int checkIsolate(Isolate isolate) { - return isolate.isNull() ? CEntryPointErrors.NULL_ARGUMENT : CEntryPointErrors.NO_ERROR; + if (isolate.isNull()) { + return CEntryPointErrors.NULL_ARGUMENT; + } else if (SubstrateOptions.SpawnIsolates.getValue() && !PointerUtils.isAMultiple(isolate, Word.signed(Heap.getHeap().getHeapBaseAlignment()))) { + /* + * The Isolate pointer is currently the same as the heap base, so we can check if the + * alignment matches the one that is expected for the heap base. This will detect most + * (but not all) invalid isolates. + */ + return CEntryPointErrors.INVALID_ISOLATE_ARGUMENT; + } + return CEntryPointErrors.NO_ERROR; } @Uninterruptible(reason = "Thread state not yet set up.") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java index 64899977cdfd..70933ac4aad5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java @@ -754,6 +754,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev } } log.string(", stack(").zhex(VMThreads.StackEnd.get(thread)).string(",").zhex(VMThreads.StackBase.get(thread)).string(")"); + log.string(", OS thread ").signed(VMThreads.OSThreadIdTL.get(thread)).string(" (").zhex(VMThreads.OSThreadHandleTL.get(thread)).string(")"); log.newline(); printed++; } @@ -876,7 +877,10 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.string("Platform: ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); log.string("Page size: ").unsigned(SubstrateOptions.getPageSize()).newline(); log.string("Supports isolates: ").bool(SubstrateOptions.SpawnIsolates.getValue()).newline(); - log.string("Containerized: ").string(String.valueOf(Container.singleton().isContainerized())).newline(); + if (RuntimeCompilation.isEnabled()) { + log.string("Supports isolated compilation: ").bool(SubstrateOptions.supportCompileInIsolates()).newline(); + } + log.string("Container support: ").bool(Container.isSupported()).newline(); log.string("Object reference size: ").signed(ConfigurationValues.getObjectLayout().getReferenceSize()).newline(); log.string("CPU features used for AOT compiled code: ").string(getBuildTimeCpuFeatures()).newline(); log.indent(false); @@ -898,12 +902,18 @@ public int maxInvocationCount() { @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { log.string("Runtime information:").indent(true); - log.string("Isolate id: ").signed(Isolates.getIsolateId()).newline(); + log.string("Isolate id: ").signed(Isolates.getIsolateId()); + if (RuntimeCompilation.isEnabled() && IsolateArgumentParser.isCompilationIsolate()) { + log.string(" (compilation isolate)"); + } + log.newline(); + log.string("Heap base: ").zhex(KnownIntrinsics.heapBase()).newline(); if (SubstrateOptions.useRelativeCodePointers()) { log.string("Code base: ").zhex(KnownIntrinsics.codeBase()).newline(); } log.string("CGlobalData base: ").zhex(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS.getPointer()).newline(); + log.string("Containerized: ").bool(Container.singleton().isContainerized()).newline(); if (Container.singleton().isContainerized()) { log.string("CPU cores (container): "); @@ -962,6 +972,9 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev layerNumber++; } while (info.isNonNull()); + if (RuntimeCompilation.isEnabled()) { + log.string("Compile in isolates: ").bool(SubstrateOptions.shouldCompileInIsolates()).newline(); + } log.indent(false); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java index e07e6cf36267..e702ec77c7b6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java @@ -176,6 +176,9 @@ private CEntryPointErrors() { @Description("The isolate could not be created because only a single isolate is supported.") // public static final int SINGLE_ISOLATE_ALREADY_CREATED = 33; + @Description("An invalid isolate was passed as an argument.") // + public static final int INVALID_ISOLATE_ARGUMENT = 34; + public static String getDescription(int code) { String result = null; if (code >= 0 && code < DESCRIPTIONS.length) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index 0abc693282a8..39dca83771d3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java @@ -164,7 +164,7 @@ public static VMThreads singleton() { * this field after being removed from the linked list. */ public static final FastThreadLocalWord nextTL = FastThreadLocalFactory.createWord("VMThreads.nextTL"); - private static final FastThreadLocalWord OSThreadIdTL = FastThreadLocalFactory.createWord("VMThreads.OSThreadIdTL"); + public static final FastThreadLocalWord OSThreadIdTL = FastThreadLocalFactory.createWord("VMThreads.OSThreadIdTL"); public static final FastThreadLocalWord OSThreadHandleTL = FastThreadLocalFactory.createWord("VMThreads.OSThreadHandleTL"); public static final FastThreadLocalWord IsolateTL = FastThreadLocalFactory.createWord("VMThreads.IsolateTL"); /** The highest stack address. 0 if not available on this platform. */