diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AlignedHeapChunk.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AlignedHeapChunk.java index e035233ba932..09b15b87976f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AlignedHeapChunk.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AlignedHeapChunk.java @@ -97,6 +97,10 @@ public static Pointer getObjectsStart(AlignedHeader that) { return HeapChunk.asPointer(that).add(getObjectsStartOffset()); } + public static Pointer getObjectsEnd(AlignedHeader that) { + return HeapChunk.getEndPointer(that); + } + /** Allocate uninitialized memory within this AlignedHeapChunk. */ static Pointer allocateMemory(AlignedHeader that, UnsignedWord size) { Pointer result = WordFactory.nullPointer(); 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 a4629b00c2eb..6e81c022d534 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 @@ -302,7 +302,7 @@ private void printGCBefore(String cause) { verboseGCLog.string(" AlignedChunkSize: ").unsigned(HeapPolicy.getAlignedHeapChunkSize()).newline(); verboseGCLog.string(" LargeArrayThreshold: ").unsigned(HeapPolicy.getLargeArrayThreshold()).string("]").newline(); if (HeapOptions.PrintHeapShape.getValue()) { - HeapImpl.getHeapImpl().logImageHeapPartitionBoundaries(verboseGCLog).newline(); + HeapImpl.getHeapImpl().logImageHeapPartitionBoundaries(verboseGCLog); } } if (SubstrateGCOptions.VerboseGC.getValue()) { @@ -399,10 +399,10 @@ private static void verbosePostCondition() { log.string("[GCImpl.postcondition: Eden space should be empty after a collection.").newline(); /* Print raw fields before trying to walk the chunk lists. */ log.string(" These should all be 0:").newline(); - log.string(" Eden space first AlignedChunk: ").hex(youngGen.getEden().getFirstAlignedHeapChunk()).newline(); - log.string(" Eden space last AlignedChunk: ").hex(youngGen.getEden().getLastAlignedHeapChunk()).newline(); - log.string(" Eden space first UnalignedChunk: ").hex(youngGen.getEden().getFirstUnalignedHeapChunk()).newline(); - log.string(" Eden space last UnalignedChunk: ").hex(youngGen.getEden().getLastUnalignedHeapChunk()).newline(); + log.string(" Eden space first AlignedChunk: ").zhex(youngGen.getEden().getFirstAlignedHeapChunk()).newline(); + log.string(" Eden space last AlignedChunk: ").zhex(youngGen.getEden().getLastAlignedHeapChunk()).newline(); + log.string(" Eden space first UnalignedChunk: ").zhex(youngGen.getEden().getFirstUnalignedHeapChunk()).newline(); + log.string(" Eden space last UnalignedChunk: ").zhex(youngGen.getEden().getLastUnalignedHeapChunk()).newline(); youngGen.getEden().report(log, true).newline(); log.string("]").newline(); } @@ -411,10 +411,10 @@ private static void verbosePostCondition() { log.string("[GCImpl.postcondition: Survivor toSpace should be empty after a collection.").newline(); /* Print raw fields before trying to walk the chunk lists. */ log.string(" These should all be 0:").newline(); - log.string(" Survivor space ").signed(i).string(" first AlignedChunk: ").hex(youngGen.getSurvivorToSpaceAt(i).getFirstAlignedHeapChunk()).newline(); - log.string(" Survivor space ").signed(i).string(" last AlignedChunk: ").hex(youngGen.getSurvivorToSpaceAt(i).getLastAlignedHeapChunk()).newline(); - log.string(" Survivor space ").signed(i).string(" first UnalignedChunk: ").hex(youngGen.getSurvivorToSpaceAt(i).getFirstUnalignedHeapChunk()).newline(); - log.string(" Survivor space ").signed(i).string(" last UnalignedChunk: ").hex(youngGen.getSurvivorToSpaceAt(i).getLastUnalignedHeapChunk()).newline(); + log.string(" Survivor space ").signed(i).string(" first AlignedChunk: ").zhex(youngGen.getSurvivorToSpaceAt(i).getFirstAlignedHeapChunk()).newline(); + log.string(" Survivor space ").signed(i).string(" last AlignedChunk: ").zhex(youngGen.getSurvivorToSpaceAt(i).getLastAlignedHeapChunk()).newline(); + log.string(" Survivor space ").signed(i).string(" first UnalignedChunk: ").zhex(youngGen.getSurvivorToSpaceAt(i).getFirstUnalignedHeapChunk()).newline(); + log.string(" Survivor space ").signed(i).string(" last UnalignedChunk: ").zhex(youngGen.getSurvivorToSpaceAt(i).getLastUnalignedHeapChunk()).newline(); youngGen.getSurvivorToSpaceAt(i).report(log, true).newline(); log.string("]").newline(); } @@ -423,10 +423,10 @@ private static void verbosePostCondition() { log.string("[GCImpl.postcondition: oldGen toSpace should be empty after a collection.").newline(); /* Print raw fields before trying to walk the chunk lists. */ log.string(" These should all be 0:").newline(); - log.string(" oldGen toSpace first AlignedChunk: ").hex(oldGen.getToSpace().getFirstAlignedHeapChunk()).newline(); - log.string(" oldGen toSpace last AlignedChunk: ").hex(oldGen.getToSpace().getLastAlignedHeapChunk()).newline(); - log.string(" oldGen.toSpace first UnalignedChunk: ").hex(oldGen.getToSpace().getFirstUnalignedHeapChunk()).newline(); - log.string(" oldGen.toSpace last UnalignedChunk: ").hex(oldGen.getToSpace().getLastUnalignedHeapChunk()).newline(); + log.string(" oldGen toSpace first AlignedChunk: ").zhex(oldGen.getToSpace().getFirstAlignedHeapChunk()).newline(); + log.string(" oldGen toSpace last AlignedChunk: ").zhex(oldGen.getToSpace().getLastAlignedHeapChunk()).newline(); + log.string(" oldGen.toSpace first UnalignedChunk: ").zhex(oldGen.getToSpace().getFirstUnalignedHeapChunk()).newline(); + log.string(" oldGen.toSpace last UnalignedChunk: ").zhex(oldGen.getToSpace().getLastUnalignedHeapChunk()).newline(); oldGen.getToSpace().report(log, true).newline(); oldGen.getFromSpace().report(log, true).newline(); log.string("]").newline(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyObjectsWalker.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyObjectsWalker.java index d1c39c54051c..3788ec2572df 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyObjectsWalker.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyObjectsWalker.java @@ -62,11 +62,11 @@ void setScanStart(Space s) { space = s; AlignedHeapChunk.AlignedHeader aChunk = s.getLastAlignedHeapChunk(); alignedHeapChunk = aChunk; - trace.string(" alignedHeapChunk: ").hex(alignedHeapChunk).string(" isNull: ").bool(aChunk.isNull()); + trace.string(" alignedHeapChunk: ").zhex(alignedHeapChunk).string(" isNull: ").bool(aChunk.isNull()); alignedTop = (aChunk.isNonNull() ? HeapChunk.getTopPointer(aChunk) : WordFactory.nullPointer()); - trace.string(" alignedTop: ").hex(alignedTop); + trace.string(" alignedTop: ").zhex(alignedTop); unalignedHeapChunk = s.getLastUnalignedHeapChunk(); - trace.string(" unalignedChunkPointer: ").hex(unalignedHeapChunk).string("]").newline(); + trace.string(" unalignedChunkPointer: ").zhex(unalignedHeapChunk).string("]").newline(); } /** Compare the snapshot to the current state of the Space to see if there are grey Objects. */ diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java index 12ae60131ee4..a233a2b182e4 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java @@ -91,7 +91,7 @@ public boolean visitObjectInline(Object o) { } /** A ring buffer of visited objects for diagnostics. */ - static final class DiagnosticReporter implements DiagnosticThunk { + static final class DiagnosticReporter extends DiagnosticThunk { static class Options { @Option(help = "Length of GreyToBlackObjectVisitor history for diagnostics. 0 implies no history is kept.") // @@ -131,9 +131,14 @@ public void noteObject(Object o) { historyCount += 1; } + @Override + public int maxInvocations() { + return 1; + } + @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") - public void invokeWithoutAllocation(Log log) { + public void printDiagnostics(Log log, int invocationCount) { if (historyCount > 0) { log.string("[GreyToBlackObjectVisitor.RealDiagnosticReporter.invoke:") .string(" history / count: ") @@ -149,11 +154,11 @@ public void invokeWithoutAllocation(Log log) { int index = countToIndex(historyCount + count); log.string(" index: ").unsigned(index, 3, Log.RIGHT_ALIGN); Word objectEntry = objectHistory[index]; - log.string(" objectEntry: ").hex(objectEntry); + log.string(" objectEntry: ").zhex(objectEntry); UnsignedWord headerEntry = headerHistory[index]; Pointer headerHub = (Pointer) ObjectHeaderImpl.clearBits(headerEntry); UnsignedWord headerHeaderBits = ObjectHeaderImpl.getHeaderBitsFromHeaderCarefully(headerEntry); - log.string(" headerEntry: ").hex(headerEntry).string(" = ").hex(headerHub).string(" | ").hex(headerHeaderBits).string(" / "); + log.string(" headerEntry: ").zhex(headerEntry).string(" = ").zhex(headerHub).string(" | ").zhex(headerHeaderBits).string(" / "); boolean headerInImageHeap = imageHeapInfo.isInReadOnlyReferencePartition(headerHub) || imageHeapInfo.isInReadOnlyRelocatablePartition(headerHub); if (headerInImageHeap) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java index 9d327253006f..8b6a25d35fe8 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java @@ -101,7 +101,7 @@ AlignedHeader produceAlignedChunk() { log().string("[HeapChunkProvider.produceAlignedChunk chunk size: ").unsigned(chunkSize).newline(); AlignedHeader result = popUnusedAlignedChunk(); - log().string(" unused chunk: ").hex(result).newline(); + log().string(" unused chunk: ").zhex(result).newline(); if (result.isNull()) { /* Unused list was empty, need to allocate memory. */ @@ -110,7 +110,7 @@ AlignedHeader produceAlignedChunk() { if (result.isNull()) { throw ALIGNED_OUT_OF_MEMORY_ERROR; } - log().string(" new chunk: ").hex(result).newline(); + log().string(" new chunk: ").zhex(result).newline(); AlignedHeapChunk.initialize(result, chunkSize); } @@ -123,7 +123,7 @@ AlignedHeader produceAlignedChunk() { HeapPolicy.increaseEdenUsedBytes(chunkSize); - log().string(" result chunk: ").hex(result).string(" ]").newline(); + log().string(" result chunk: ").zhex(result).string(" ]").newline(); return result; } @@ -174,13 +174,13 @@ private void pushUnusedAlignedChunk(AlignedHeader chunk) { if (SubstrateOptions.MultiThreaded.getValue()) { VMThreads.guaranteeOwnsThreadMutex("Should hold the lock when pushing to the global list."); } - log().string(" old list top: ").hex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); + log().string(" old list top: ").zhex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); HeapChunk.setNext(chunk, unusedAlignedChunks.get()); unusedAlignedChunks.set(chunk); bytesInUnusedAlignedChunks.addAndGet(HeapPolicy.getAlignedHeapChunkSize()); - log().string(" new list top: ").hex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); + log().string(" new list top: ").zhex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); } /** @@ -193,14 +193,14 @@ private void pushUnusedAlignedChunk(AlignedHeader chunk) { * uninterruptible so it can not be interrupted by a safepoint. */ private AlignedHeader popUnusedAlignedChunk() { - log().string(" old list top: ").hex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); + log().string(" old list top: ").zhex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); AlignedHeader result = popUnusedAlignedChunkUninterruptibly(); if (result.isNull()) { return WordFactory.nullPointer(); } else { bytesInUnusedAlignedChunks.subtractAndGet(HeapPolicy.getAlignedHeapChunkSize()); - log().string(" new list top: ").hex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); + log().string(" new list top: ").zhex(unusedAlignedChunks.get()).string(" list bytes ").signed(bytesInUnusedAlignedChunks.get()).newline(); return result; } } @@ -224,7 +224,7 @@ private AlignedHeader popUnusedAlignedChunkUninterruptibly() { /** Acquire an UnalignedHeapChunk from the operating system. */ UnalignedHeader produceUnalignedChunk(UnsignedWord objectSize) { UnsignedWord chunkSize = UnalignedHeapChunk.getChunkSizeForObject(objectSize); - log().string("[HeapChunkProvider.produceUnalignedChunk objectSize: ").unsigned(objectSize).string(" chunkSize: ").hex(chunkSize).newline(); + log().string("[HeapChunkProvider.produceUnalignedChunk objectSize: ").unsigned(objectSize).string(" chunkSize: ").zhex(chunkSize).newline(); noteFirstAllocationTime(); UnalignedHeader result = (UnalignedHeader) CommittedMemoryProvider.get().allocate(chunkSize, CommittedMemoryProvider.UNALIGNED, false); @@ -241,7 +241,7 @@ UnalignedHeader produceUnalignedChunk(UnsignedWord objectSize) { HeapPolicy.increaseEdenUsedBytes(chunkSize); - log().string(" returns ").hex(result).string(" ]").newline(); + log().string(" returns ").zhex(result).string(" ]").newline(); return result; } @@ -256,14 +256,14 @@ static void consumeUnalignedChunks(UnalignedHeader firstChunk) { private static void zap(Header chunk, WordBase value) { Pointer start = HeapChunk.getTopPointer(chunk); Pointer limit = HeapChunk.getEndPointer(chunk); - log().string(" zap chunk: ").hex(chunk).string(" start: ").hex(start).string(" limit: ").hex(limit).string(" value: ").hex(value).newline(); + log().string(" zap chunk: ").zhex(chunk).string(" start: ").zhex(start).string(" limit: ").zhex(limit).string(" value: ").zhex(value).newline(); for (Pointer p = start; p.belowThan(limit); p = p.add(FrameAccess.wordSize())) { p.writeWord(0, value); } } Log report(Log log, boolean traceHeapChunks) { - log.string("[Unused:").indent(true); + log.string("Unused:").indent(true); log.string("aligned: ").signed(bytesInUnusedAlignedChunks.get()) .string("/") .signed(bytesInUnusedAlignedChunks.get().unsignedDivide(HeapPolicy.getAlignedHeapChunkSize())); @@ -271,12 +271,12 @@ Log report(Log log, boolean traceHeapChunks) { if (unusedAlignedChunks.get().isNonNull()) { log.newline().string("aligned chunks:").redent(true); for (AlignedHeapChunk.AlignedHeader aChunk = unusedAlignedChunks.get(); aChunk.isNonNull(); aChunk = HeapChunk.getNext(aChunk)) { - log.newline().hex(aChunk).string(" (").hex(AlignedHeapChunk.getObjectsStart(aChunk)).string("-").hex(HeapChunk.getTopPointer(aChunk)).string(")"); + log.newline().zhex(aChunk).string(" (").zhex(AlignedHeapChunk.getObjectsStart(aChunk)).string("-").zhex(HeapChunk.getTopPointer(aChunk)).string(")"); } log.redent(false); } } - log.redent(false).string("]"); + log.redent(false); return log; } 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 0433f7a41549..22c9b1ef4206 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 @@ -51,6 +51,9 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; +import com.oracle.svm.core.genscavenge.ThreadLocalAllocation.Descriptor; +import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.remset.RememberedSet; import com.oracle.svm.core.heap.GC; import com.oracle.svm.core.heap.Heap; @@ -61,6 +64,7 @@ import com.oracle.svm.core.heap.ReferenceHandlerThreadSupport; import com.oracle.svm.core.heap.ReferenceInternals; import com.oracle.svm.core.heap.RuntimeCodeInfoGCSupport; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicReference; import com.oracle.svm.core.locks.VMCondition; import com.oracle.svm.core.locks.VMMutex; @@ -68,6 +72,7 @@ import com.oracle.svm.core.nodes.CFunctionEpilogueNode; import com.oracle.svm.core.nodes.CFunctionPrologueNode; import com.oracle.svm.core.os.CommittedMemoryProvider; +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.thread.ThreadStatus; import com.oracle.svm.core.thread.VMOperation; @@ -79,7 +84,7 @@ public final class HeapImpl extends Heap { /** Synchronization means for notifying {@link #refPendingList} waiters without deadlocks. */ - private static final VMMutex REF_MUTEX = new VMMutex(); + private static final VMMutex REF_MUTEX = new VMMutex("referencePendingList"); private static final VMCondition REF_CONDITION = new VMCondition(REF_MUTEX); // Singleton instances, created during image generation. @@ -110,7 +115,8 @@ public HeapImpl(FeatureAccess access) { this.gcImpl = new GCImpl(access); this.runtimeCodeInfoGcSupport = new RuntimeCodeInfoGCSupportImpl(); this.heapPolicy = new HeapPolicy(); - SubstrateDiagnostics.DiagnosticThunkRegister.getSingleton().register(new HeapDiagnosticsPrinter()); + SubstrateDiagnostics.DiagnosticThunkRegister.getSingleton().register(new DumpHeapSettingsAndStatistics()); + SubstrateDiagnostics.DiagnosticThunkRegister.getSingleton().register(new DumpChunkInformation()); } @Fold @@ -141,8 +147,8 @@ public boolean isInImageHeap(Object obj) { @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isInImageHeap(Pointer pointer) { - return imageHeapInfo.isInImageHeap(pointer) || (AuxiliaryImageHeap.isPresent() && AuxiliaryImageHeap.singleton().containsObject(pointer)); + public boolean isInImageHeap(Pointer objPointer) { + return imageHeapInfo.isInImageHeap(objPointer) || (AuxiliaryImageHeap.isPresent() && AuxiliaryImageHeap.singleton().containsObject(objPointer)); } @Override @@ -264,20 +270,17 @@ void report(Log log) { report(log, HeapPolicyOptions.TraceHeapChunks.getValue()); } - Log report(Log log, boolean traceHeapChunks) { - log.newline().string("[Heap:").indent(true); + void report(Log log, boolean traceHeapChunks) { + log.string("Heap:").indent(true); getYoungGeneration().report(log, traceHeapChunks).newline(); getOldGeneration().report(log, traceHeapChunks).newline(); - getChunkProvider().report(log, traceHeapChunks); - log.redent(false).string("]"); - return log; + getChunkProvider().report(log, traceHeapChunks).indent(false); } - Log logImageHeapPartitionBoundaries(Log log) { - log.string("[Native image heap boundaries: ").indent(true); - ImageHeapWalker.logPartitionBoundaries(log, imageHeapInfo); - log.redent(false).string("]"); - return log; + void logImageHeapPartitionBoundaries(Log log) { + log.string("Native image heap boundaries:").indent(true); + imageHeapInfo.print(log); + log.indent(false); } /** Log the zap values to make it easier to search for them. */ @@ -585,26 +588,185 @@ public Reference getAndClearReferencePendingList() { } } - private static class HeapDiagnosticsPrinter implements DiagnosticThunk { + @Override + public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess) { + if (SubstrateOptions.SpawnIsolates.getValue() && value.equal(KnownIntrinsics.heapBase())) { + log.string("is the heap base"); + return true; + } + + Pointer ptr = (Pointer) value; + if (printLocationInfo(log, ptr)) { + if (allowJavaHeapAccess && objectHeaderImpl.pointsToObjectHeader(ptr)) { + DynamicHub hub = objectHeaderImpl.readDynamicHubFromPointer(ptr); + log.indent(true); + log.string("hub=").string(hub.getName()); + log.redent(false); + } + return true; + } + return false; + } + + private boolean printLocationInfo(Log log, Pointer ptr) { + if (imageHeapInfo.isInReadOnlyPrimitivePartition(ptr)) { + log.string("points into the image heap (read-only primitives)"); + return true; + } else if (imageHeapInfo.isInReadOnlyReferencePartition(ptr)) { + log.string("points into the image heap (read-only references)"); + return true; + } else if (imageHeapInfo.isInReadOnlyRelocatablePartition(ptr)) { + log.string("points into the image heap (read-only relocatables)"); + return true; + } else if (imageHeapInfo.isInWritablePrimitivePartition(ptr)) { + log.string("points into the image heap (writable primitives)"); + return true; + } else if (imageHeapInfo.isInWritableReferencePartition(ptr)) { + log.string("points into the image heap (writable references)"); + return true; + } else if (imageHeapInfo.isInWritableHugePartition(ptr)) { + log.string("points into the image heap (writable huge)"); + return true; + } else if (imageHeapInfo.isInReadOnlyHugePartition(ptr)) { + log.string("points into the image heap (read-only huge)"); + return true; + } else if (AuxiliaryImageHeap.isPresent() && AuxiliaryImageHeap.singleton().containsObject(ptr)) { + log.string("points into the auxiliary image heap"); + return true; + } else if (isInYoungGen(ptr)) { + log.string("points into the young generation"); + return true; + } else if (isInOldGen(ptr)) { + log.string("points into the old generation"); + return true; + } else { + return printTlabInfo(log, ptr); + } + } + + boolean isInHeap(Pointer ptr) { + return isInImageHeap(ptr) || isInYoungGen(ptr) || isInOldGen(ptr); + } + + private boolean isInYoungGen(Pointer ptr) { + if (findPointerInSpace(youngGeneration.getEden(), ptr)) { + return true; + } + + for (int i = 0; i < youngGeneration.getMaxSurvivorSpaces(); i++) { + if (findPointerInSpace(youngGeneration.getSurvivorFromSpaceAt(i), ptr)) { + return true; + } + if (findPointerInSpace(youngGeneration.getSurvivorToSpaceAt(i), ptr)) { + return true; + } + } + return false; + } + + private boolean isInOldGen(Pointer ptr) { + return findPointerInSpace(oldGeneration.getFromSpace(), ptr) || findPointerInSpace(oldGeneration.getToSpace(), ptr); + } + + private static boolean findPointerInSpace(Space space, Pointer p) { + AlignedHeapChunk.AlignedHeader aChunk = space.getFirstAlignedHeapChunk(); + while (aChunk.isNonNull()) { + Pointer start = AlignedHeapChunk.getObjectsStart(aChunk); + if (start.belowOrEqual(p) && p.belowThan(HeapChunk.getTopPointer(aChunk))) { + return true; + } + aChunk = HeapChunk.getNext(aChunk); + } + + UnalignedHeapChunk.UnalignedHeader uChunk = space.getFirstUnalignedHeapChunk(); + while (uChunk.isNonNull()) { + Pointer start = UnalignedHeapChunk.getObjectStart(uChunk); + if (start.belowOrEqual(p) && p.belowThan(HeapChunk.getTopPointer(uChunk))) { + return true; + } + uChunk = HeapChunk.getNext(uChunk); + } + return false; + } + + /** + * Accessing the TLAB of other threads is a highly unsafe operation and can cause crashes. So, + * this only makes sense for printing diagnostics as it is very likely that register values + * point to TLABs. + */ + private static boolean printTlabInfo(Log log, Pointer ptr) { + assert SubstrateDiagnostics.isInProgressByCurrentThread() : "can cause crashes, so it may only be used while printing diagnostics"; + for (IsolateThread thread = VMThreads.firstThreadUnsafe(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) { + ThreadLocalAllocation.Descriptor tlab = getTlabUnsafe(thread); + AlignedHeader aChunk = tlab.getAlignedChunk(); + while (aChunk.isNonNull()) { + Pointer dataStart = AlignedHeapChunk.getObjectsStart(aChunk); + Pointer dataEnd = AlignedHeapChunk.getObjectsEnd(aChunk); + if (ptr.aboveOrEqual(dataStart) && ptr.belowThan(dataEnd)) { + log.string("points into an aligned TLAB chunk of thread ").zhex(thread); + return true; + } + aChunk = HeapChunk.getNext(aChunk); + } + + UnalignedHeader uChunk = tlab.getUnalignedChunk(); + while (uChunk.isNonNull()) { + Pointer dataStart = UnalignedHeapChunk.getObjectStart(uChunk); + Pointer dataEnd = UnalignedHeapChunk.getObjectEnd(uChunk); + if (ptr.aboveOrEqual(dataStart) && ptr.belowThan(dataEnd)) { + log.string("points into an unaligned TLAB chunk of thread ").zhex(thread); + return true; + } + } + } + return false; + } + + @Uninterruptible(reason = "This whole method is unsafe, so it is only uninterruptible to satisfy the checks.") + private static Descriptor getTlabUnsafe(IsolateThread thread) { + assert SubstrateDiagnostics.isInProgressByCurrentThread() : "can cause crashes, so it may only be used while printing diagnostics"; + return ThreadLocalAllocation.getTlab(thread); + } + + private static class DumpHeapSettingsAndStatistics extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void invokeWithoutAllocation(Log log) { - HeapImpl heap = HeapImpl.getHeapImpl(); + public void printDiagnostics(Log log, int invocationCount) { GCImpl gc = GCImpl.getGCImpl(); - log.string("[Heap settings and statistics: ").indent(true); + log.string("Heap settings and statistics:").indent(true); log.string("Supports isolates: ").bool(SubstrateOptions.SpawnIsolates.getValue()).newline(); + if (SubstrateOptions.SpawnIsolates.getValue()) { + log.string("Heap base: ").zhex(KnownIntrinsics.heapBase()).newline(); + } log.string("Object reference size: ").signed(ConfigurationValues.getObjectLayout().getReferenceSize()).newline(); + log.string("Aligned chunk size: ").unsigned(HeapPolicy.getAlignedHeapChunkSize()).newline(); GCAccounting accounting = gc.getAccounting(); log.string("Incremental collections: ").unsigned(accounting.getIncrementalCollectionCount()).newline(); - log.string("Complete collections: ").unsigned(accounting.getCompleteCollectionCount()); - log.redent(false).string("]").newline(); - log.newline(); + log.string("Complete collections: ").unsigned(accounting.getCompleteCollectionCount()).newline(); + log.indent(false); + } + } + + private static class DumpChunkInformation extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } - heap.logImageHeapPartitionBoundaries(log).newline(); + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + HeapImpl heap = HeapImpl.getHeapImpl(); + heap.logImageHeapPartitionBoundaries(log); zapValuesToLog(log).newline(); - heap.report(log, true).newline(); + heap.report(log, true); log.newline(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java index 57d06343857c..9790e7f69fe4 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java @@ -94,8 +94,8 @@ private static boolean verifyYoungGeneration(Occasion occasion) { if (occasion == HeapVerifier.Occasion.AFTER_COLLECTION) { Space eden = youngGeneration.getEden(); if (!eden.isEmpty()) { - Log.log().string("Eden contains chunks after a collection: firstAlignedChunk: ").hex(eden.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") - .hex(eden.getFirstUnalignedHeapChunk()).newline(); + Log.log().string("Eden contains chunks after a collection: firstAlignedChunk: ").zhex(eden.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") + .zhex(eden.getFirstUnalignedHeapChunk()).newline(); success = false; } } @@ -107,8 +107,8 @@ private static boolean verifyYoungGeneration(Occasion occasion) { Space toSpace = youngGeneration.getSurvivorToSpaceAt(i); if (!toSpace.isEmpty()) { - Log.log().string("Survivor to-space ").signed(i).string(" contains chunks: firstAlignedChunk: ").hex(toSpace.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") - .hex(toSpace.getFirstUnalignedHeapChunk()).newline(); + Log.log().string("Survivor to-space ").signed(i).string(" contains chunks: firstAlignedChunk: ").zhex(toSpace.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") + .zhex(toSpace.getFirstUnalignedHeapChunk()).newline(); success = false; } @@ -126,8 +126,8 @@ private static boolean verifyOldGeneration() { Space toSpace = oldGeneration.getToSpace(); if (!toSpace.isEmpty()) { - Log.log().string("Old generation to-space contains chunks: firstAlignedChunk: ").hex(toSpace.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") - .hex(toSpace.getFirstUnalignedHeapChunk()).newline(); + Log.log().string("Old generation to-space contains chunks: firstAlignedChunk: ").zhex(toSpace.getFirstAlignedHeapChunk()).string(", firstUnalignedChunk: ") + .zhex(toSpace.getFirstUnalignedHeapChunk()).newline(); success = false; } @@ -192,8 +192,8 @@ private static boolean verifyChunkList(Space space, String kind, HeapChunk.Heade while (current.isNonNull()) { HeapChunk.Header previousOfCurrent = HeapChunk.getPrevious(current); if (previousOfCurrent.notEqual(previous)) { - Log.log().string("Verification failed for the doubly-linked list that holds ").string(kind).string(" chunks: space: ").string(space.getName()).string(", current: ").hex(current) - .string(", current.previous: ").hex(previousOfCurrent).string(", previous: ").hex(previous).newline(); + Log.log().string("Verification failed for the doubly-linked list that holds ").string(kind).string(" chunks: space: ").string(space.getName()).string(", current: ").zhex(current) + .string(", current.previous: ").zhex(previousOfCurrent).string(", previous: ").zhex(previous).newline(); result = false; } previous = current; @@ -201,8 +201,8 @@ private static boolean verifyChunkList(Space space, String kind, HeapChunk.Heade } if (previous.notEqual(lastChunk)) { - Log.log().string("Verification failed for the doubly-linked list that holds ").string(kind).string(" chunks: space: ").string(space.getName()).string(", previous: ").hex(previous) - .string(", lastChunk: ").hex(lastChunk).newline(); + Log.log().string("Verification failed for the doubly-linked list that holds ").string(kind).string(" chunks: space: ").string(space.getName()).string(", previous: ").zhex(previous) + .string(", lastChunk: ").zhex(lastChunk).newline(); result = false; } return result; @@ -213,8 +213,8 @@ private static boolean verifyAlignedChunks(Space space, AlignedHeader firstAlign AlignedHeader aChunk = firstAlignedHeapChunk; while (aChunk.isNonNull()) { if (space != aChunk.getSpace()) { - Log.log().string("Space ").string(space.getName()).string(" contains aligned chunk ").hex(aChunk).string(" but the chunk does not reference the correct space: ") - .hex(Word.objectToUntrackedPointer(aChunk.getSpace())).newline(); + Log.log().string("Space ").string(space.getName()).string(" contains aligned chunk ").zhex(aChunk).string(" but the chunk does not reference the correct space: ") + .zhex(Word.objectToUntrackedPointer(aChunk.getSpace())).newline(); success = false; } @@ -231,8 +231,8 @@ private static boolean verifyUnalignedChunks(Space space, UnalignedHeader firstU UnalignedHeader uChunk = firstUnalignedHeapChunk; while (uChunk.isNonNull()) { if (space != uChunk.getSpace()) { - Log.log().string("Space ").string(space.getName()).string(" contains unaligned chunk ").hex(uChunk).string(" but the chunk does not reference the correct space: ") - .hex(Word.objectToUntrackedPointer(uChunk.getSpace())).newline(); + Log.log().string("Space ").string(space.getName()).string(" contains unaligned chunk ").zhex(uChunk).string(" but the chunk does not reference the correct space: ") + .zhex(Word.objectToUntrackedPointer(uChunk.getSpace())).newline(); success = false; } @@ -254,18 +254,18 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH int objectAlignment = ConfigurationValues.getObjectLayout().getAlignment(); if (ptr.unsignedRemainder(objectAlignment).notEqual(0)) { - Log.log().string("Object ").hex(ptr).string(" is not properly aligned to ").signed(objectAlignment).string(" bytes.").newline(); + Log.log().string("Object ").zhex(ptr).string(" is not properly aligned to ").signed(objectAlignment).string(" bytes.").newline(); return false; } UnsignedWord header = ObjectHeaderImpl.readHeaderFromPointer(ptr); if (ObjectHeaderImpl.isProducedHeapChunkZapped(header) || ObjectHeaderImpl.isConsumedHeapChunkZapped(header)) { - Log.log().string("Object ").hex(ptr).string(" has a zapped header: ").hex(header).newline(); + Log.log().string("Object ").zhex(ptr).string(" has a zapped header: ").zhex(header).newline(); return false; } if (ObjectHeaderImpl.isForwardedHeader(header)) { - Log.log().string("Object ").hex(ptr).string(" has a forwarded header: ").hex(header).newline(); + Log.log().string("Object ").zhex(ptr).string(" has a forwarded header: ").zhex(header).newline(); return false; } @@ -274,26 +274,26 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH HeapChunk.Header expectedChunk = aChunk.isNonNull() ? aChunk : uChunk; HeapChunk.Header chunk = HeapChunk.getEnclosingHeapChunk(obj); if (chunk.notEqual(expectedChunk)) { - Log.log().string("Object ").hex(ptr).string(" should have ").hex(expectedChunk).string(" as its enclosing chunk but getEnclosingHeapChunk returned ").hex(chunk).newline(); + Log.log().string("Object ").zhex(ptr).string(" should have ").zhex(expectedChunk).string(" as its enclosing chunk but getEnclosingHeapChunk returned ").zhex(chunk).newline(); return false; } Pointer chunkStart = HeapChunk.asPointer(chunk); Pointer chunkTop = HeapChunk.getTopPointer(chunk); if (chunkStart.aboveOrEqual(ptr) || chunkTop.belowOrEqual(ptr)) { - Log.log().string("Object ").hex(ptr).string(" is not within the allocated part of the chunk: ").hex(chunkStart).string(" - ").hex(chunkTop).string("").newline(); + Log.log().string("Object ").zhex(ptr).string(" is not within the allocated part of the chunk: ").zhex(chunkStart).string(" - ").zhex(chunkTop).string("").newline(); return false; } if (aChunk.isNonNull()) { if (!ObjectHeaderImpl.isAlignedHeader(header)) { - Log.log().string("Header of object ").hex(ptr).string(" is not marked as aligned: ").hex(header).newline(); + Log.log().string("Header of object ").zhex(ptr).string(" is not marked as aligned: ").zhex(header).newline(); return false; } } else { assert uChunk.isNonNull(); if (!ObjectHeaderImpl.isUnalignedHeader(header)) { - Log.log().string("Header of object ").hex(ptr).string(" is not marked as unaligned: ").hex(header).newline(); + Log.log().string("Header of object ").zhex(ptr).string(" is not marked as unaligned: ").zhex(header).newline(); return false; } } @@ -301,7 +301,7 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH Space space = chunk.getSpace(); if (space == null) { if (!HeapImpl.getHeapImpl().isInImageHeap(obj)) { - Log.log().string("Object ").hex(ptr).string(" is not an image heap object even though the space of the parent chunk ").hex(chunk).string(" is null.").newline(); + Log.log().string("Object ").zhex(ptr).string(" is not an image heap object even though the space of the parent chunk ").zhex(chunk).string(" is null.").newline(); return false; } // Not all objects in the image heap have the remembered set bit in the header, so @@ -309,7 +309,7 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH } else if (space.isOldSpace()) { if (SubstrateOptions.useRememberedSet() && !RememberedSet.get().hasRememberedSet(header)) { - Log.log().string("Object ").hex(ptr).string(" is in old generation chunk ").hex(chunk).string(" but does not have a remembered set.").newline(); + Log.log().string("Object ").zhex(ptr).string(" is in old generation chunk ").zhex(chunk).string(" but does not have a remembered set.").newline(); return false; } } @@ -317,7 +317,7 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH DynamicHub hub = KnownIntrinsics.readHub(obj); if (!HeapImpl.getHeapImpl().isInImageHeap(hub)) { - Log.log().string("Object ").hex(ptr).string(" references a hub that is not in the image heap: ").hex(Word.objectToUntrackedPointer(hub)).newline(); + Log.log().string("Object ").zhex(ptr).string(" references a hub that is not in the image heap: ").zhex(Word.objectToUntrackedPointer(hub)).newline(); return false; } @@ -359,8 +359,8 @@ private static boolean verifyReference(Object parentObject, Pointer reference, P return true; } - if (!isInHeap(referencedObject)) { - Log.log().string("Object reference at ").hex(reference).string(" points outside the Java heap: ").hex(referencedObject).string(". "); + if (!HeapImpl.getHeapImpl().isInHeap(referencedObject)) { + Log.log().string("Object reference at ").zhex(reference).string(" points outside the Java heap: ").zhex(referencedObject).string(". "); if (parentObject != null) { Log.log().string("The object that contains the invalid reference is of type ").string(parentObject.getClass().getName()).newline(); } else { @@ -372,54 +372,6 @@ private static boolean verifyReference(Object parentObject, Pointer reference, P return true; } - private static boolean isInHeap(Pointer ptr) { - HeapImpl heap = HeapImpl.getHeapImpl(); - return heap.isInImageHeap(ptr) || isInYoungGen(ptr) || isInOldGen(ptr); - } - - private static boolean isInYoungGen(Pointer ptr) { - YoungGeneration youngGen = HeapImpl.getHeapImpl().getYoungGeneration(); - if (findPointerInSpace(youngGen.getEden(), ptr)) { - return true; - } - - for (int i = 0; i < youngGen.getMaxSurvivorSpaces(); i++) { - if (findPointerInSpace(youngGen.getSurvivorFromSpaceAt(i), ptr)) { - return true; - } - if (findPointerInSpace(youngGen.getSurvivorToSpaceAt(i), ptr)) { - return true; - } - } - return false; - } - - private static boolean isInOldGen(Pointer ptr) { - OldGeneration oldGen = HeapImpl.getHeapImpl().getOldGeneration(); - return findPointerInSpace(oldGen.getFromSpace(), ptr) || findPointerInSpace(oldGen.getToSpace(), ptr); - } - - private static boolean findPointerInSpace(Space space, Pointer p) { - AlignedHeapChunk.AlignedHeader aChunk = space.getFirstAlignedHeapChunk(); - while (aChunk.isNonNull()) { - Pointer start = AlignedHeapChunk.getObjectsStart(aChunk); - if (start.belowOrEqual(p) && p.belowThan(HeapChunk.getTopPointer(aChunk))) { - return true; - } - aChunk = HeapChunk.getNext(aChunk); - } - - UnalignedHeapChunk.UnalignedHeader uChunk = space.getFirstUnalignedHeapChunk(); - while (uChunk.isNonNull()) { - Pointer start = UnalignedHeapChunk.getObjectStart(uChunk); - if (start.belowOrEqual(p) && p.belowThan(HeapChunk.getTopPointer(uChunk))) { - return true; - } - uChunk = HeapChunk.getNext(uChunk); - } - return false; - } - private static class ImageHeapRegionVerifier implements MemoryWalker.ImageHeapRegionVisitor { private final ImageHeapObjectVerifier objectVerifier; @@ -475,7 +427,7 @@ private static class ImageHeapObjectVerifier extends ObjectVerifier { public boolean visitObject(Object object) { Word pointer = Word.objectToUntrackedPointer(object); if (!HeapImpl.getHeapImpl().isInImageHeap(object)) { - Log.log().string("Image heap object ").hex(pointer).string(" is not considered as part of the image heap.").newline(); + Log.log().string("Image heap object ").zhex(pointer).string(" is not considered as part of the image heap.").newline(); result = false; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java index 9bb0430f8942..d9a0c1586a8f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java @@ -34,6 +34,8 @@ import com.oracle.svm.core.annotate.UnknownPrimitiveField; import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.snippets.KnownIntrinsics; /** @@ -129,49 +131,49 @@ public void initialize(Object firstReadOnlyPrimitiveObject, Object lastReadOnlyP @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInReadOnlyPrimitivePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstReadOnlyPrimitiveObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastReadOnlyPrimitiveObject)); + return Word.objectToUntrackedPointer(firstReadOnlyPrimitiveObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastReadOnlyPrimitiveObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInReadOnlyReferencePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstReadOnlyReferenceObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastReadOnlyReferenceObject)); + return Word.objectToUntrackedPointer(firstReadOnlyReferenceObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastReadOnlyReferenceObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInReadOnlyRelocatablePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstReadOnlyRelocatableObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastReadOnlyRelocatableObject)); + return Word.objectToUntrackedPointer(firstReadOnlyRelocatableObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastReadOnlyRelocatableObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInWritablePrimitivePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstWritablePrimitiveObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastWritablePrimitiveObject)); + return Word.objectToUntrackedPointer(firstWritablePrimitiveObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastWritablePrimitiveObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInWritableReferencePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstWritableReferenceObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastWritableReferenceObject)); + return Word.objectToUntrackedPointer(firstWritableReferenceObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastWritableReferenceObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInWritableHugePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstWritableHugeObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastWritableHugeObject)); + return Word.objectToUntrackedPointer(firstWritableHugeObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastWritableHugeObject)); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInReadOnlyHugePartition(Pointer ptr) { assert ptr.isNonNull(); - return Word.objectToUntrackedPointer(firstReadOnlyHugeObject).belowOrEqual(ptr) && ptr.belowOrEqual(Word.objectToUntrackedPointer(lastReadOnlyHugeObject)); + return Word.objectToUntrackedPointer(firstReadOnlyHugeObject).belowOrEqual(ptr) && ptr.belowThan(getObjectEnd(lastReadOnlyHugeObject)); } /** * This method only returns the correct result for pointers that point to the the start of an * object. This is sufficient for all our current use cases. This code must be as fast as - * possible at the GC uses it for every visited reference. + * possible as the GC uses it for every visited reference. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean isInImageHeap(Pointer objectPointer) { @@ -192,6 +194,14 @@ public UnalignedHeader getFirstWritableUnalignedChunk() { return asImageHeapChunk(offsetOfFirstWritableUnalignedChunk); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + private static Word getObjectEnd(Object obj) { + if (obj == null) { + return WordFactory.nullPointer(); + } + return Word.objectToUntrackedPointer(obj).add(LayoutEncoding.getSizeFromObject(obj)); + } + @SuppressWarnings("unchecked") private static > T asImageHeapChunk(long offsetInImageHeap) { if (offsetInImageHeap < 0) { @@ -200,4 +210,14 @@ private static > T asImageHeapChunk(long offsetInI UnsignedWord offset = WordFactory.unsigned(offsetInImageHeap); return (T) KnownIntrinsics.heapBase().add(offset); } + + public void print(Log log) { + log.string("ReadOnly Primitives: ").zhex(Word.objectToUntrackedPointer(firstReadOnlyPrimitiveObject)).string(" - ").zhex(getObjectEnd(lastReadOnlyPrimitiveObject)).newline(); + log.string("ReadOnly References: ").zhex(Word.objectToUntrackedPointer(firstReadOnlyReferenceObject)).string(" - ").zhex(getObjectEnd(lastReadOnlyReferenceObject)).newline(); + log.string("ReadOnly Relocatables: ").zhex(Word.objectToUntrackedPointer(firstReadOnlyRelocatableObject)).string(" - ").zhex(getObjectEnd(lastReadOnlyRelocatableObject)).newline(); + log.string("Writable Primitives: ").zhex(Word.objectToUntrackedPointer(firstWritablePrimitiveObject)).string(" - ").zhex(getObjectEnd(lastWritablePrimitiveObject)).newline(); + log.string("Writable References: ").zhex(Word.objectToUntrackedPointer(firstWritableReferenceObject)).string(" - ").zhex(getObjectEnd(lastWritableReferenceObject)).newline(); + log.string("Writable Huge: ").zhex(Word.objectToUntrackedPointer(firstWritableHugeObject)).string(" - ").zhex(getObjectEnd(lastWritableHugeObject)).newline(); + log.string("ReadOnly Huge: ").zhex(Word.objectToUntrackedPointer(firstReadOnlyHugeObject)).string(" - ").zhex(getObjectEnd(lastReadOnlyHugeObject)); + } } 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 cf9a5ed24ab3..1533fb4404a8 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 @@ -37,7 +37,6 @@ import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.heap.ObjectVisitor; import com.oracle.svm.core.hub.LayoutEncoding; -import com.oracle.svm.core.log.Log; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.util.UnsignedUtils; @@ -133,23 +132,6 @@ private static boolean walkPartitionInline(Object firstObject, Object lastObject } while (current.belowOrEqual(lastPointer)); return true; } - - static void logPartitionBoundaries(Log log, ImageHeapInfo imageHeapInfo) { - log.string("ReadOnly Primitives: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstReadOnlyPrimitiveObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastReadOnlyPrimitiveObject)).newline(); - log.string("ReadOnly References: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstReadOnlyReferenceObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastReadOnlyReferenceObject)).newline(); - log.string("ReadOnly Relocatables: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstReadOnlyRelocatableObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastReadOnlyRelocatableObject)).newline(); - log.string("Writable Primitives: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstWritablePrimitiveObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastWritablePrimitiveObject)).newline(); - log.string("Writable References: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstWritableReferenceObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastWritableReferenceObject)).newline(); - log.string("Writable Huge: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstWritableHugeObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastWritableHugeObject)).newline(); - log.string("ReadOnly Huge: ").hex(Word.objectToUntrackedPointer(imageHeapInfo.firstReadOnlyHugeObject)).string(" .. ").hex( - Word.objectToUntrackedPointer(imageHeapInfo.lastReadOnlyHugeObject)); - } } abstract class MemoryWalkerAccessBase implements MemoryWalker.NativeImageHeapRegionAccess { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java index 5ed74754ac35..41d4dcbd8e9a 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java @@ -144,6 +144,18 @@ public DynamicHub dynamicHubFromObjectHeader(UnsignedWord header) { return (DynamicHub) objectValue; } + @Override + public Pointer readPotentialDynamicHubFromPointer(Pointer ptr) { + UnsignedWord potentialHeader = ObjectHeaderImpl.readHeaderFromPointer(ptr); + UnsignedWord pointerBits = clearBits(potentialHeader); + if (ReferenceAccess.singleton().haveCompressedReferences()) { + UnsignedWord compressedBits = pointerBits.unsignedShiftRight(getCompressionShift()); + return KnownIntrinsics.heapBase().add(compressedBits.shiftLeft(getCompressionShift())); + } else { + return (Pointer) pointerBits; + } + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override public Word encodeAsUnmanagedObjectHeader(DynamicHub hub) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/OldGeneration.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/OldGeneration.java index dd04afcb90c8..82220acd97e0 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/OldGeneration.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/OldGeneration.java @@ -118,10 +118,10 @@ boolean scanGreyObjects() { @Override public Log report(Log log, boolean traceHeapChunks) { - log.string("[Old generation: ").indent(true); + log.string("Old generation: ").indent(true); getFromSpace().report(log, traceHeapChunks).newline(); getToSpace().report(log, traceHeapChunks).newline(); - log.redent(false).string("]"); + log.redent(false); return log; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java index ce1e98f62a5a..2f8f1f26cb08 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/PathExhibitor.java @@ -299,7 +299,7 @@ public boolean visitObjectReference(Pointer stackSlot, boolean compressed) { return true; } Pointer referentPointer = ReferenceAccess.singleton().readObjectAsUntrackedPointer(stackSlot, compressed); - trace.string(" referentPointer: ").hex(referentPointer); + trace.string(" referentPointer: ").zhex(referentPointer); if (target.matches(referentPointer.toObject())) { result.fill(new StackElement(stackSlot, ip, deoptFrame), new LeafElement(referentPointer.toObject())); return false; @@ -425,7 +425,7 @@ public Log toLog(Log log) { Pointer objPointer = Word.objectToUntrackedPointer(base); Pointer fieldObjRef = objPointer.add(offset); Pointer fieldPointer = fieldObjRef.readWord(0); - log.string(" field: ").hex(fieldPointer); + log.string(" field: ").zhex(fieldPointer); log.string("]"); return log; } @@ -454,10 +454,10 @@ public Object getObject() { @Override public Log toLog(Log log) { log.string("[stack:"); - log.string(" slot: ").hex(stackSlot); - log.string(" deoptSourcePC: ").hex(deoptSourcePC); - log.string(" ip: ").hex(ip); - log.string(" value: ").hex(slotValue); + log.string(" slot: ").zhex(stackSlot); + log.string(" deoptSourcePC: ").zhex(deoptSourcePC); + log.string(" ip: ").zhex(ip); + log.string(" value: ").zhex(slotValue); log.string("]"); return log; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java index c20d2c2db748..0c0a8da445af 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java @@ -148,25 +148,25 @@ public boolean walkObjects(ObjectVisitor visitor) { /** Report some statistics about this Space. */ public Log report(Log log, boolean traceHeapChunks) { - log.string("[").string(getName()).string(":").indent(true); + log.string(getName()).string(":").indent(true); accounting.report(log); if (traceHeapChunks) { if (getFirstAlignedHeapChunk().isNonNull()) { log.newline().string("aligned chunks:").redent(true); for (AlignedHeapChunk.AlignedHeader aChunk = getFirstAlignedHeapChunk(); aChunk.isNonNull(); aChunk = HeapChunk.getNext(aChunk)) { - log.newline().hex(aChunk).string(" (").hex(AlignedHeapChunk.getObjectsStart(aChunk)).string("-").hex(HeapChunk.getTopPointer(aChunk)).string(")"); + log.newline().zhex(aChunk).string(" (").zhex(AlignedHeapChunk.getObjectsStart(aChunk)).string("-").zhex(HeapChunk.getTopPointer(aChunk)).string(")"); } log.redent(false); } if (getFirstUnalignedHeapChunk().isNonNull()) { log.newline().string("unaligned chunks:").redent(true); for (UnalignedHeapChunk.UnalignedHeader uChunk = getFirstUnalignedHeapChunk(); uChunk.isNonNull(); uChunk = HeapChunk.getNext(uChunk)) { - log.newline().hex(uChunk).string(" (").hex(UnalignedHeapChunk.getObjectStart(uChunk)).string("-").hex(HeapChunk.getTopPointer(uChunk)).string(")"); + log.newline().zhex(uChunk).string(" (").zhex(UnalignedHeapChunk.getObjectStart(uChunk)).string("-").zhex(HeapChunk.getTopPointer(uChunk)).string(")"); } log.redent(false); } } - log.redent(false).string("]"); + log.redent(false); return log; } @@ -176,30 +176,20 @@ public Log report(Log log, boolean traceHeapChunks) { * This is "slow-path" memory allocation. */ private Pointer allocateMemory(UnsignedWord objectSize) { - Log trace = Log.noopLog().string("[Space.allocateMemory:").string(" space: ").string(getName()).string(" size: ").unsigned(objectSize).newline(); Pointer result = WordFactory.nullPointer(); /* First try allocating in the last chunk. */ AlignedHeapChunk.AlignedHeader oldChunk = getLastAlignedHeapChunk(); - trace.string(" oldChunk: ").hex(oldChunk); if (oldChunk.isNonNull()) { result = AlignedHeapChunk.allocateMemory(oldChunk, objectSize); - trace.string(" oldChunk provides: ").hex(result); } /* If oldChunk did not provide, try allocating a new chunk for the requested memory. */ if (result.isNull()) { AlignedHeapChunk.AlignedHeader newChunk = requestAlignedHeapChunk(); - trace.string(" newChunk: ").hex(newChunk); if (newChunk.isNonNull()) { /* Allocate the Object within the new chunk. */ result = AlignedHeapChunk.allocateMemory(newChunk, objectSize); - if (isSurvivorSpace()) { - trace.string(" newSurvivorChunk provides: ").hex(result); - } else { - trace.string(" newChunk provides: ").hex(result); - } } } - trace.string(" returns: ").hex(result).string("]").newline(); return result; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java index d51baef577c8..85bd719faedf 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java @@ -131,7 +131,7 @@ public static Word getTlabAddress() { } @Uninterruptible(reason = "Accesses TLAB", callerMustBe = true) - private static Descriptor getTlab(IsolateThread vmThread) { + public static Descriptor getTlab(IsolateThread vmThread) { return regularTLAB.getAddress(vmThread); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UnalignedHeapChunk.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UnalignedHeapChunk.java index b7df6d07e0ee..1d6b7ec92734 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UnalignedHeapChunk.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UnalignedHeapChunk.java @@ -102,6 +102,10 @@ public static Pointer getObjectStart(UnalignedHeader that) { return HeapChunk.asPointer(that).add(getObjectStartOffset()); } + public static Pointer getObjectEnd(UnalignedHeader that) { + return HeapChunk.getEndPointer(that); + } + public static UnsignedWord getOverhead() { return getObjectStartOffset(); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/YoungGeneration.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/YoungGeneration.java index 26446b351f21..76ab70d84263 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/YoungGeneration.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/YoungGeneration.java @@ -93,11 +93,11 @@ public boolean walkObjects(ObjectVisitor visitor) { @Override public Log report(Log log, boolean traceHeapChunks) { - log.string("[Young generation: ").indent(true); - log.string("[Eden: ").indent(true); + log.string("Young generation: ").indent(true); + log.string("Eden: ").indent(true); getEden().report(log, traceHeapChunks); - log.redent(false).string("]").newline(); - log.string("[Survivors: ").indent(true); + log.redent(false).newline(); + log.string("Survivors: ").indent(true); for (int i = 0; i < maxSurvivorSpaces; i++) { this.survivorFromSpaces[i].report(log, traceHeapChunks).newline(); this.survivorToSpaces[i].report(log, traceHeapChunks); @@ -105,7 +105,7 @@ public Log report(Log log, boolean traceHeapChunks) { log.newline(); } } - log.redent(false).string("]").redent(false).string("]"); + log.redent(false).redent(false); return log; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/CardTable.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/CardTable.java index 63ed94347e07..c26ae959cda8 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/CardTable.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/CardTable.java @@ -172,12 +172,12 @@ private static boolean verifyReference(Object parentObject, Pointer cardTableSta if (fromImageHeap || HeapChunk.getSpace(objChunk).isYoungSpace()) { UnsignedWord cardTableIndex = memoryOffsetToIndex(Word.objectToUntrackedPointer(parentObject).subtract(objectsStart)); Pointer cardTableAddress = cardTableStart.add(indexToTableOffset(cardTableIndex)); - Log.log().string("Object ").hex(Word.objectToUntrackedPointer(parentObject)).string(" (").string(parentObject.getClass().getName()).character(')') + Log.log().string("Object ").zhex(Word.objectToUntrackedPointer(parentObject)).string(" (").string(parentObject.getClass().getName()).character(')') .string(fromImageHeap ? ", which is in the image heap, " : " ") .string("has an object reference at ") - .hex(reference).string(" that points to ").hex(referencedObject).string(" (").string(obj.getClass().getName()).string("), ") + .zhex(reference).string(" that points to ").zhex(referencedObject).string(" (").string(obj.getClass().getName()).string("), ") .string("which is in the ").string(fromImageHeap ? "runtime heap" : "young generation").string(". ") - .string("However, the card table at ").hex(cardTableAddress).string(" is clean.").newline(); + .string("However, the card table at ").zhex(cardTableAddress).string(" is clean.").newline(); return false; } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/FirstObjectTable.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/FirstObjectTable.java index 4b995e4673c2..42130392da61 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/FirstObjectTable.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/FirstObjectTable.java @@ -290,23 +290,24 @@ public static boolean verify(Pointer tableStart, Pointer objectsStart, Pointer o for (UnsignedWord index = WordFactory.unsigned(0); index.belowThan(indexLimit); index = index.add(1)) { Pointer objStart = getFirstObject(tableStart, objectsStart, objectsLimit, index); if (objStart.belowThan(objectsStart) || objectsLimit.belowOrEqual(objStart)) { - Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object that is outside of the current chunk: obj: ").hex(objStart).string(", chunk: ") - .hex(objectsStart).string(" - ").hex(objectsLimit).newline(); + Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object that is outside of the current chunk: obj: ").zhex(objStart) + .string(", chunk: ") + .zhex(objectsStart).string(" - ").zhex(objectsLimit).newline(); return false; } Pointer entryStart = objectsStart.add(indexToMemoryOffset(index)); if (!objStart.belowOrEqual(entryStart)) { - Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object is not crossing nor starting at a card boundary: obj: ").hex(objStart) - .string(", chunk: ").hex(objectsStart).string(" - ").hex(objectsLimit).newline(); + Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object is not crossing nor starting at a card boundary: obj: ").zhex(objStart) + .string(", chunk: ").zhex(objectsStart).string(" - ").zhex(objectsLimit).newline(); return false; } Object obj = objStart.toObject(); Pointer objEnd = LayoutEncoding.getObjectEnd(obj); if (!entryStart.belowThan(objEnd)) { - Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object is not crossing nor starting at a card boundary: obj: ").hex(objStart) - .string(" - ").hex(objEnd).string(", chunk: ").hex(objectsStart).string(" - ").hex(objectsLimit).newline(); + Log.log().string("The first object table entry at index ").unsigned(index).string(" points to an object is not crossing nor starting at a card boundary: obj: ").zhex(objStart) + .string(" - ").zhex(objEnd).string(", chunk: ").zhex(objectsStart).string(" - ").zhex(objectsLimit).newline(); return false; } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateSegfaultHandler.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateSegfaultHandler.java index 3f05415babf9..6127d6806836 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateSegfaultHandler.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateSegfaultHandler.java @@ -30,6 +30,7 @@ import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateSegfaultHandler; @@ -40,6 +41,7 @@ import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue; import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; import com.oracle.svm.core.c.function.CEntryPointOptions.Publish; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.os.MemoryProtectionKeyProvider; import com.oracle.svm.core.posix.headers.LibC; import com.oracle.svm.core.posix.headers.Signal; @@ -68,15 +70,26 @@ private static void dispatch(@SuppressWarnings("unused") int signalNumber, @Supp } if (tryEnterIsolate(uContext)) { - if (MemoryProtectionKeyProvider.isAvailable()) { - MemoryProtectionKeyProvider.singleton().printSignalInfo(sigInfo); - } - - dump(uContext); + dump(sigInfo, uContext); throw VMError.shouldNotReachHere(); } } + @Override + protected void printSignalInfo(Log log, PointerBase signalInfo) { + if (MemoryProtectionKeyProvider.isAvailable()) { + MemoryProtectionKeyProvider.singleton().printSignalInfo(signalInfo); + } else { + siginfo_t sigInfo = (siginfo_t) signalInfo; + log.string("siginfo: si_signo: ").signed(sigInfo.si_signo()).string(", si_code: ").signed(sigInfo.si_code()); + if (sigInfo.si_errno() != 0) { + log.string(", si_errno: ").signed(sigInfo.si_errno()); + } + log.string(", si_addr: ").signed(sigInfo.si_addr()); + log.newline(); + } + } + /** The address of the signal handler for signals handled by Java code, above. */ private static final CEntryPointLiteral advancedSignalDispatcher = CEntryPointLiteral.create(PosixSubstrateSegfaultHandler.class, "dispatch", int.class, siginfo_t.class, ucontext_t.class); @@ -87,7 +100,7 @@ protected void install() { sigaction structSigAction = StackValue.get(structSigActionSize); LibC.memset(structSigAction, WordFactory.signed(0), WordFactory.unsigned(structSigActionSize)); /* Register sa_sigaction signal handler */ - structSigAction.sa_flags(Signal.SA_SIGINFO()); + structSigAction.sa_flags(Signal.SA_SIGINFO() | Signal.SA_NODEFER()); structSigAction.sa_sigaction(advancedSignalDispatcher.getFunctionPointer()); Signal.sigaction(Signal.SignalEnum.SIGSEGV, structSigAction, WordFactory.nullPointer()); Signal.sigaction(Signal.SignalEnum.SIGBUS, structSigAction, WordFactory.nullPointer()); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java index 751ac2f90749..7eee53fb5a20 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UContextRegisterDumper.java @@ -32,7 +32,7 @@ import com.oracle.svm.core.posix.headers.Signal.ucontext_t; public interface UContextRegisterDumper extends RegisterDumper { - void dumpRegisters(Log log, ucontext_t uContext); + void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess); PointerBase getHeapBase(ucontext_t uContext); @@ -43,8 +43,8 @@ public interface UContextRegisterDumper extends RegisterDumper { PointerBase getIP(ucontext_t uContext); @Override - default void dumpRegisters(Log log, Context context) { - dumpRegisters(log, (ucontext_t) context); + default void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess) { + dumpRegisters(log, (ucontext_t) context, printLocationInfo, allowJavaHeapAccess); } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/aarch64/AArch64UContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/aarch64/AArch64UContextRegisterDumper.java index ca2586afeb1c..89ec977460e0 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/aarch64/AArch64UContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/aarch64/AArch64UContextRegisterDumper.java @@ -59,43 +59,43 @@ public void afterRegistration(AfterRegistrationAccess access) { class AArch64UContextRegisterDumper implements UContextRegisterDumper { @Override - public void dumpRegisters(Log log, ucontext_t uContext) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess) { mcontext_t sigcontext = uContext.uc_mcontext(); GregsPointer regs = sigcontext.regs(); - dumpReg(log, "R0 ", regs.read(0)); - dumpReg(log, "R1 ", regs.read(1)); - dumpReg(log, "R2 ", regs.read(2)); - dumpReg(log, "R3 ", regs.read(3)); - dumpReg(log, "R4 ", regs.read(4)); - dumpReg(log, "R5 ", regs.read(5)); - dumpReg(log, "R6 ", regs.read(6)); - dumpReg(log, "R7 ", regs.read(7)); - dumpReg(log, "R8 ", regs.read(8)); - dumpReg(log, "R9 ", regs.read(9)); - dumpReg(log, "R10 ", regs.read(10)); - dumpReg(log, "R11 ", regs.read(11)); - dumpReg(log, "R12 ", regs.read(12)); - dumpReg(log, "R13 ", regs.read(13)); - dumpReg(log, "R14 ", regs.read(14)); - dumpReg(log, "R15 ", regs.read(15)); - dumpReg(log, "R16 ", regs.read(16)); - dumpReg(log, "R17 ", regs.read(17)); - dumpReg(log, "R18 ", regs.read(18)); - dumpReg(log, "R19 ", regs.read(19)); - dumpReg(log, "R20 ", regs.read(20)); - dumpReg(log, "R21 ", regs.read(21)); - dumpReg(log, "R22 ", regs.read(22)); - dumpReg(log, "R23 ", regs.read(23)); - dumpReg(log, "R24 ", regs.read(24)); - dumpReg(log, "R25 ", regs.read(25)); - dumpReg(log, "R26 ", regs.read(26)); - dumpReg(log, "R27 ", regs.read(27)); - dumpReg(log, "R28 ", regs.read(28)); - dumpReg(log, "R29 ", regs.read(29)); - dumpReg(log, "R30 ", regs.read(30)); - dumpReg(log, "R31 ", regs.read(31)); - dumpReg(log, "SP ", sigcontext.sp()); - dumpReg(log, "PC ", sigcontext.pc()); + dumpReg(log, "R0 ", regs.read(0), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R1 ", regs.read(1), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R2 ", regs.read(2), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R3 ", regs.read(3), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R4 ", regs.read(4), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R5 ", regs.read(5), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R6 ", regs.read(6), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R7 ", regs.read(7), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R8 ", regs.read(8), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R9 ", regs.read(9), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R10 ", regs.read(10), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R11 ", regs.read(11), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R12 ", regs.read(12), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R13 ", regs.read(13), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R14 ", regs.read(14), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R15 ", regs.read(15), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R16 ", regs.read(16), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R17 ", regs.read(17), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R18 ", regs.read(18), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R19 ", regs.read(19), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R20 ", regs.read(20), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R21 ", regs.read(21), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R22 ", regs.read(22), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R23 ", regs.read(23), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R24 ", regs.read(24), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R25 ", regs.read(25), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R26 ", regs.read(26), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R27 ", regs.read(27), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R28 ", regs.read(28), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R29 ", regs.read(29), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R30 ", regs.read(30), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R31 ", regs.read(31), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "SP ", sigcontext.sp(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "PC ", sigcontext.pc(), printLocationInfo, allowJavaHeapAccess); } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64UContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64UContextRegisterDumper.java index a1a2bc263504..f383405f72fb 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64UContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/amd64/AMD64UContextRegisterDumper.java @@ -59,26 +59,26 @@ public void afterRegistration(AfterRegistrationAccess access) { class AMD64UContextRegisterDumper implements UContextRegisterDumper { @Override - public void dumpRegisters(Log log, ucontext_t uContext) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess) { GregsPointer gregs = uContext.uc_mcontext_gregs(); - dumpReg(log, "RAX ", gregs.read(GregEnum.REG_RAX())); - dumpReg(log, "RBX ", gregs.read(GregEnum.REG_RBX())); - dumpReg(log, "RCX ", gregs.read(GregEnum.REG_RCX())); - dumpReg(log, "RDX ", gregs.read(GregEnum.REG_RDX())); - dumpReg(log, "RBP ", gregs.read(GregEnum.REG_RBP())); - dumpReg(log, "RSI ", gregs.read(GregEnum.REG_RSI())); - dumpReg(log, "RDI ", gregs.read(GregEnum.REG_RDI())); - dumpReg(log, "RSP ", gregs.read(GregEnum.REG_RSP())); - dumpReg(log, "R8 ", gregs.read(GregEnum.REG_R8())); - dumpReg(log, "R9 ", gregs.read(GregEnum.REG_R9())); - dumpReg(log, "R10 ", gregs.read(GregEnum.REG_R10())); - dumpReg(log, "R11 ", gregs.read(GregEnum.REG_R11())); - dumpReg(log, "R12 ", gregs.read(GregEnum.REG_R12())); - dumpReg(log, "R13 ", gregs.read(GregEnum.REG_R13())); - dumpReg(log, "R14 ", gregs.read(GregEnum.REG_R14())); - dumpReg(log, "R15 ", gregs.read(GregEnum.REG_R15())); - dumpReg(log, "EFL ", gregs.read(GregEnum.REG_EFL())); - dumpReg(log, "RIP ", gregs.read(GregEnum.REG_RIP())); + dumpReg(log, "RAX ", gregs.read(GregEnum.REG_RAX()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBX ", gregs.read(GregEnum.REG_RBX()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RCX ", gregs.read(GregEnum.REG_RCX()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDX ", gregs.read(GregEnum.REG_RDX()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBP ", gregs.read(GregEnum.REG_RBP()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSI ", gregs.read(GregEnum.REG_RSI()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDI ", gregs.read(GregEnum.REG_RDI()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSP ", gregs.read(GregEnum.REG_RSP()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R8 ", gregs.read(GregEnum.REG_R8()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R9 ", gregs.read(GregEnum.REG_R9()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R10 ", gregs.read(GregEnum.REG_R10()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R11 ", gregs.read(GregEnum.REG_R11()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R12 ", gregs.read(GregEnum.REG_R12()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R13 ", gregs.read(GregEnum.REG_R13()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R14 ", gregs.read(GregEnum.REG_R14()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R15 ", gregs.read(GregEnum.REG_R15()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "EFL ", gregs.read(GregEnum.REG_EFL()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RIP ", gregs.read(GregEnum.REG_RIP()), printLocationInfo, allowJavaHeapAccess); } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java index 92869f211b24..4de1a6cd04e0 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java @@ -35,8 +35,14 @@ import com.oracle.svm.core.stack.StackOverflowCheck; class DarwinStackOverflowSupport implements StackOverflowCheck.OSSupport { + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Override + public UnsignedWord lookupStackBase() { + Pthread.pthread_t self = Pthread.pthread_self(); + return DarwinPthread.pthread_get_stackaddr_np(self); + } - @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override public UnsignedWord lookupStackEnd() { Pthread.pthread_t self = Pthread.pthread_self(); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinUContextRegisterDumper.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinUContextRegisterDumper.java index 99d95156cadf..c56eb458e409 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinUContextRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinUContextRegisterDumper.java @@ -58,26 +58,26 @@ public void afterRegistration(AfterRegistrationAccess access) { class DarwinUContextRegisterDumper implements UContextRegisterDumper { @Override - public void dumpRegisters(Log log, ucontext_t uContext) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess) { Signal.MContext64 sigcontext = uContext.uc_mcontext64(); - dumpReg(log, "RAX ", ((Pointer) sigcontext).readLong(sigcontext.rax_offset())); - dumpReg(log, "RBX ", ((Pointer) sigcontext).readLong(sigcontext.rbx_offset())); - dumpReg(log, "RCX ", ((Pointer) sigcontext).readLong(sigcontext.rcx_offset())); - dumpReg(log, "RDX ", ((Pointer) sigcontext).readLong(sigcontext.rdx_offset())); - dumpReg(log, "RBP ", ((Pointer) sigcontext).readLong(sigcontext.rbp_offset())); - dumpReg(log, "RSI ", ((Pointer) sigcontext).readLong(sigcontext.rsi_offset())); - dumpReg(log, "RDI ", ((Pointer) sigcontext).readLong(sigcontext.rdi_offset())); - dumpReg(log, "RSP ", ((Pointer) sigcontext).readLong(sigcontext.rsp_offset())); - dumpReg(log, "R8 ", ((Pointer) sigcontext).readLong(sigcontext.r8_offset())); - dumpReg(log, "R9 ", ((Pointer) sigcontext).readLong(sigcontext.r9_offset())); - dumpReg(log, "R10 ", ((Pointer) sigcontext).readLong(sigcontext.r10_offset())); - dumpReg(log, "R11 ", ((Pointer) sigcontext).readLong(sigcontext.r11_offset())); - dumpReg(log, "R12 ", ((Pointer) sigcontext).readLong(sigcontext.r12_offset())); - dumpReg(log, "R13 ", ((Pointer) sigcontext).readLong(sigcontext.r13_offset())); - dumpReg(log, "R14 ", ((Pointer) sigcontext).readLong(sigcontext.r14_offset())); - dumpReg(log, "R15 ", ((Pointer) sigcontext).readLong(sigcontext.r15_offset())); - dumpReg(log, "EFL ", ((Pointer) sigcontext).readLong(sigcontext.efl_offset())); - dumpReg(log, "RIP ", ((Pointer) sigcontext).readLong(sigcontext.rip_offset())); + dumpReg(log, "RAX ", ((Pointer) sigcontext).readLong(sigcontext.rax_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBX ", ((Pointer) sigcontext).readLong(sigcontext.rbx_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RCX ", ((Pointer) sigcontext).readLong(sigcontext.rcx_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDX ", ((Pointer) sigcontext).readLong(sigcontext.rdx_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBP ", ((Pointer) sigcontext).readLong(sigcontext.rbp_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSI ", ((Pointer) sigcontext).readLong(sigcontext.rsi_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDI ", ((Pointer) sigcontext).readLong(sigcontext.rdi_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSP ", ((Pointer) sigcontext).readLong(sigcontext.rsp_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R8 ", ((Pointer) sigcontext).readLong(sigcontext.r8_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R9 ", ((Pointer) sigcontext).readLong(sigcontext.r9_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R10 ", ((Pointer) sigcontext).readLong(sigcontext.r10_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R11 ", ((Pointer) sigcontext).readLong(sigcontext.r11_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R12 ", ((Pointer) sigcontext).readLong(sigcontext.r12_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R13 ", ((Pointer) sigcontext).readLong(sigcontext.r13_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R14 ", ((Pointer) sigcontext).readLong(sigcontext.r14_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R15 ", ((Pointer) sigcontext).readLong(sigcontext.r15_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "EFL ", ((Pointer) sigcontext).readLong(sigcontext.efl_offset()), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RIP ", ((Pointer) sigcontext).readLong(sigcontext.rip_offset()), printLocationInfo, allowJavaHeapAccess); } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Signal.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Signal.java index 66a9f1fa8d65..dfd9302d5545 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Signal.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Signal.java @@ -38,6 +38,7 @@ import org.graalvm.nativeimage.c.struct.CFieldOffset; import org.graalvm.nativeimage.c.struct.CPointerTo; import org.graalvm.nativeimage.c.struct.CStruct; +import org.graalvm.nativeimage.c.type.VoidPointer; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.PointerBase; @@ -91,8 +92,19 @@ public interface SignalDispatcher extends CFunctionPointer { @CFunction public static native int raise(int signum); - @CStruct + @CStruct(isIncomplete = true) public interface siginfo_t extends PointerBase { + @CField + int si_signo(); + + @CField + int si_errno(); + + @CField + int si_code(); + + @CField + VoidPointer si_addr(); } @Platforms(Platform.LINUX.class) @@ -279,6 +291,9 @@ public interface AdvancedSignalDispatcher extends CFunctionPointer { @CConstant public static native int SA_SIGINFO(); + @CConstant + public static native int SA_NODEFER(); + @CStruct(addStructKeyword = true) public interface sigaction extends PointerBase { @CField diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java index d4b36f26f27e..5df1ad2fa37f 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java @@ -37,8 +37,23 @@ import com.oracle.svm.core.stack.StackOverflowCheck; class LinuxStackOverflowSupport implements StackOverflowCheck.OSSupport { + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Override + public UnsignedWord lookupStackBase() { + Pthread.pthread_attr_t attr = StackValue.get(Pthread.pthread_attr_t.class); + PosixUtils.checkStatusIs0(Pthread.pthread_getattr_np(Pthread.pthread_self(), attr), "LinuxStackOverflowSupport: pthread_getattr_np"); + + WordPointer stackaddrPtr = StackValue.get(WordPointer.class); + WordPointer stacksizePtr = StackValue.get(WordPointer.class); + PosixUtils.checkStatusIs0(Pthread.pthread_attr_getstack(attr, stackaddrPtr, stacksizePtr), "LinuxStackOverflowSupport: pthread_attr_getstack"); + PosixUtils.checkStatusIs0(Pthread.pthread_attr_destroy(attr), "LinuxStackOverflowSupport: pthread_attr_destroy"); + + UnsignedWord stackaddr = stackaddrPtr.read(); + UnsignedWord stacksize = stacksizePtr.read(); + return stackaddr.add(stacksize); + } - @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override public UnsignedWord lookupStackEnd() { Pthread.pthread_attr_t attr = StackValue.get(Pthread.pthread_attr_t.class); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java index 4f3a42050bde..8e10d8674795 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.posix.pthread; +import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; @@ -44,6 +45,7 @@ import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.locks.ClassInstanceReplacer; import com.oracle.svm.core.locks.VMCondition; +import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.posix.headers.Errno; @@ -63,7 +65,7 @@ final class PthreadVMLockFeature implements Feature { private final ClassInstanceReplacer mutexReplacer = new ClassInstanceReplacer(VMMutex.class) { @Override protected VMMutex createReplacement(VMMutex source) { - return new PthreadVMMutex(); + return new PthreadVMMutex(source.getName()); } }; @@ -81,7 +83,8 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void duringSetup(DuringSetupAccess access) { - ImageSingletons.add(PthreadVMLockSupport.class, new PthreadVMLockSupport()); + PthreadVMLockSupport support = new PthreadVMLockSupport(); + ImageSingletons.add(VMLockSupport.class, support); access.registerObjectReplacer(mutexReplacer); access.registerObjectReplacer(conditionReplacer); } @@ -105,14 +108,14 @@ public void beforeCompilation(BeforeCompilationAccess access) { nextIndex += conditionSize; } - PthreadVMLockSupport lockSupport = ImageSingletons.lookup(PthreadVMLockSupport.class); + PthreadVMLockSupport lockSupport = PthreadVMLockSupport.singleton(); lockSupport.mutexes = mutexes; lockSupport.conditions = conditions; lockSupport.pthreadStructs = new byte[nextIndex]; } } -public final class PthreadVMLockSupport { +public final class PthreadVMLockSupport extends VMLockSupport { /** All mutexes, so that we can initialize them at run time when the VM starts. */ @UnknownObjectField(types = PthreadVMMutex[].class)// protected PthreadVMMutex[] mutexes; @@ -130,18 +133,24 @@ public final class PthreadVMLockSupport { @UnknownObjectField(types = byte[].class)// protected byte[] pthreadStructs; + @Fold + public static PthreadVMLockSupport singleton() { + return (PthreadVMLockSupport) ImageSingletons.lookup(VMLockSupport.class); + } + /** * Must be called once early during startup, before any mutex or condition is used. */ @Uninterruptible(reason = "Called from uninterruptible code. Too early for safepoints.") public static boolean initialize() { - for (PthreadVMMutex mutex : ImageSingletons.lookup(PthreadVMLockSupport.class).mutexes) { + PthreadVMLockSupport support = PthreadVMLockSupport.singleton(); + for (PthreadVMMutex mutex : support.mutexes) { if (Pthread.pthread_mutex_init(mutex.getStructPointer(), WordFactory.nullPointer()) != 0) { return false; } } - for (PthreadVMCondition condition : ImageSingletons.lookup(PthreadVMLockSupport.class).conditions) { + for (PthreadVMCondition condition : support.conditions) { if (PthreadConditionUtils.initCondition(condition.getStructPointer()) != 0) { return false; } @@ -162,6 +171,16 @@ protected static void checkResult(int result, String functionName) { ImageSingletons.lookup(LogHandler.class).fatalError(); } } + + @Override + public PthreadVMMutex[] getMutexes() { + return mutexes; + } + + @Override + public PthreadVMCondition[] getConditions() { + return conditions; + } } final class PthreadVMMutex extends VMMutex { @@ -169,12 +188,13 @@ final class PthreadVMMutex extends VMMutex { protected UnsignedWord structOffset; @Platforms(Platform.HOSTED_ONLY.class) - protected PthreadVMMutex() { + protected PthreadVMMutex(String name) { + super(name); } @Uninterruptible(reason = "Called from uninterruptible code.") protected Pthread.pthread_mutex_t getStructPointer() { - return (Pthread.pthread_mutex_t) Word.objectToUntrackedPointer(ImageSingletons.lookup(PthreadVMLockSupport.class).pthreadStructs).add(structOffset); + return (Pthread.pthread_mutex_t) Word.objectToUntrackedPointer(PthreadVMLockSupport.singleton().pthreadStructs).add(structOffset); } @Override @@ -232,7 +252,7 @@ protected PthreadVMCondition(PthreadVMMutex mutex) { @Uninterruptible(reason = "Called from uninterruptible code.") protected Pthread.pthread_cond_t getStructPointer() { - return (Pthread.pthread_cond_t) Word.objectToUntrackedPointer(ImageSingletons.lookup(PthreadVMLockSupport.class).pthreadStructs).add(structOffset); + return (Pthread.pthread_cond_t) Word.objectToUntrackedPointer(PthreadVMLockSupport.singleton().pthreadStructs).add(structOffset); } @Override diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsRegisterDumper.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsRegisterDumper.java index e9d22fca35cf..cc24e53bf67d 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsRegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsRegisterDumper.java @@ -53,29 +53,29 @@ public void afterRegistration(AfterRegistrationAccess access) { public class WindowsRegisterDumper implements RegisterDumper { @Override - public void dumpRegisters(Log log, Context context) { - dumpRegisters(log, (CONTEXT) context); + public void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess) { + dumpRegisters(log, (CONTEXT) context, printLocationInfo, allowJavaHeapAccess); } - private static void dumpRegisters(Log log, CONTEXT context) { - dumpReg(log, "RAX ", context.Rax()); - dumpReg(log, "RBX ", context.Rbx()); - dumpReg(log, "RCX ", context.Rcx()); - dumpReg(log, "RDX ", context.Rdx()); - dumpReg(log, "RBP ", context.Rbp()); - dumpReg(log, "RSI ", context.Rsi()); - dumpReg(log, "RDI ", context.Rdi()); - dumpReg(log, "RSP ", context.Rsp()); - dumpReg(log, "R8 ", context.R8()); - dumpReg(log, "R9 ", context.R9()); - dumpReg(log, "R10 ", context.R10()); - dumpReg(log, "R11 ", context.R11()); - dumpReg(log, "R12 ", context.R12()); - dumpReg(log, "R13 ", context.R13()); - dumpReg(log, "R14 ", context.R14()); - dumpReg(log, "R15 ", context.R15()); - dumpReg(log, "EFL ", context.EFlags()); - dumpReg(log, "RIP ", context.Rip()); + private static void dumpRegisters(Log log, CONTEXT context, boolean printLocationInfo, boolean allowJavaHeapAccess) { + dumpReg(log, "RAX ", context.Rax(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBX ", context.Rbx(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RCX ", context.Rcx(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDX ", context.Rdx(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RBP ", context.Rbp(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSI ", context.Rsi(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RDI ", context.Rdi(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RSP ", context.Rsp(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R8 ", context.R8(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R9 ", context.R9(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R10 ", context.R10(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R11 ", context.R11(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R12 ", context.R12(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R13 ", context.R13(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R14 ", context.R14(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R15 ", context.R15(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "EFL ", context.EFlags(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RIP ", context.Rip(), printLocationInfo, allowJavaHeapAccess); } @Override diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsStackOverflowSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsStackOverflowSupport.java index 3ef8834b04b9..7659fb357eeb 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsStackOverflowSupport.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsStackOverflowSupport.java @@ -32,6 +32,7 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; @@ -40,8 +41,28 @@ @Platforms({Platform.WINDOWS.class}) class WindowsStackOverflowSupport implements StackOverflowCheck.OSSupport { + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Override + public UnsignedWord lookupStackBase() { + int sizeOfMInfo = SizeOf.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class); + MemoryAPI.MEMORY_BASIC_INFORMATION minfo = StackValue.get(sizeOfMInfo); + MemoryAPI.VirtualQuery(minfo, minfo, WordFactory.unsigned(sizeOfMInfo)); + Pointer stackBottom = (Pointer) minfo.AllocationBase(); + UnsignedWord stackSize = minfo.RegionSize(); + + // Add up the sizes of all the regions with the same AllocationBase. + while (true) { + MemoryAPI.VirtualQuery(stackBottom.add(stackSize), minfo, WordFactory.unsigned(sizeOfMInfo)); + if (stackBottom.equal(minfo.AllocationBase())) { + stackSize = stackSize.add(minfo.RegionSize()); + } else { + break; + } + } + return stackBottom.add(stackSize); + } - @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override public UnsignedWord lookupStackEnd() { MemoryAPI.MEMORY_BASIC_INFORMATION minfo = StackValue.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class); diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSubstrateSegfaultHandler.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSubstrateSegfaultHandler.java index e38b22c83f5f..b885e251fc6c 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSubstrateSegfaultHandler.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSubstrateSegfaultHandler.java @@ -26,12 +26,16 @@ import static com.oracle.svm.core.annotate.RestrictHeapAccess.Access.NO_ALLOCATION; import static com.oracle.svm.core.annotate.RestrictHeapAccess.Access.NO_HEAP_ACCESS; +import static com.oracle.svm.core.windows.headers.ErrHandlingAPI.EXCEPTION_ACCESS_VIOLATION; +import static com.oracle.svm.core.windows.headers.ErrHandlingAPI.EXCEPTION_IN_PAGE_ERROR; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.c.type.CLongPointer; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.word.PointerBase; import com.oracle.svm.core.SubstrateSegfaultHandler; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -42,6 +46,7 @@ import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; import com.oracle.svm.core.c.function.CEntryPointOptions.NotIncludedAutomatically; import com.oracle.svm.core.c.function.CEntryPointOptions.Publish; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.util.VMError; import com.oracle.svm.core.windows.headers.ErrHandlingAPI; @@ -54,6 +59,10 @@ public void afterRegistration(AfterRegistrationAccess access) { } class WindowsSubstrateSegfaultHandler extends SubstrateSegfaultHandler { + private static final int EX_READ = 0; + private static final int EX_WRITE = 1; + private static final int EX_EXECUTE = 8; + @Override protected void install() { /* @@ -93,13 +102,46 @@ private static int handler(ErrHandlingAPI.EXCEPTION_POINTERS exceptionInfo) { ErrHandlingAPI.CONTEXT context = exceptionInfo.ContextRecord(); if (tryEnterIsolate(context)) { - dump(context); + dump(exceptionInfo, context); throw shouldNotReachHere(); } /* Nothing we can do. */ return ErrHandlingAPI.EXCEPTION_CONTINUE_SEARCH(); } + @Override + protected void printSignalInfo(Log log, PointerBase signalInfo) { + ErrHandlingAPI.EXCEPTION_POINTERS exceptionInfo = (ErrHandlingAPI.EXCEPTION_POINTERS) signalInfo; + ErrHandlingAPI.EXCEPTION_RECORD exceptionRecord = exceptionInfo.ExceptionRecord(); + + int exceptionCode = exceptionRecord.ExceptionCode(); + log.string("siginfo: ExceptionCode: ").signed(exceptionCode); + + int numParameters = exceptionRecord.NumberParameters(); + if ((exceptionCode == EXCEPTION_ACCESS_VIOLATION() || exceptionCode == EXCEPTION_IN_PAGE_ERROR()) && numParameters >= 2) { + CLongPointer exInfo = exceptionRecord.ExceptionInformation(); + long operation = exInfo.addressOf(0).read(); + if (operation == EX_READ) { + log.string(", reading address"); + } else if (operation == EX_WRITE) { + log.string(", writing address"); + } else if (operation == EX_EXECUTE) { + log.string(", data execution prevention violation at address"); + } else { + log.string(", ExceptionInformation=").zhex(operation); + } + log.string(" ").zhex(exInfo.addressOf(1).read()); + } else { + if (numParameters > 0) { + log.string(", ExceptionInformation="); + CLongPointer exInfo = exceptionRecord.ExceptionInformation(); + for (int i = 0; i < numParameters; i++) { + log.string(" ").zhex(exInfo.addressOf(i).read()); + } + } + } + } + @Uninterruptible(reason = "Called from uninterruptible code.") @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true) private static RuntimeException shouldNotReachHere() { diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVMLockSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVMLockSupport.java index eb43885bd41f..49a555f2d534 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVMLockSupport.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVMLockSupport.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.windows; +import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; @@ -43,6 +44,7 @@ import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.locks.ClassInstanceReplacer; import com.oracle.svm.core.locks.VMCondition; +import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.thread.VMThreads; @@ -65,7 +67,7 @@ final class WindowsVMLockFeature implements Feature { private final ClassInstanceReplacer mutexReplacer = new ClassInstanceReplacer(VMMutex.class) { @Override protected WindowsVMMutex createReplacement(VMMutex source) { - return new WindowsVMMutex(); + return new WindowsVMMutex(source.getName()); } }; @@ -83,7 +85,7 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void duringSetup(DuringSetupAccess access) { - ImageSingletons.add(WindowsVMLockSupport.class, new WindowsVMLockSupport()); + ImageSingletons.add(VMLockSupport.class, new WindowsVMLockSupport()); access.registerObjectReplacer(mutexReplacer); access.registerObjectReplacer(conditionReplacer); } @@ -107,14 +109,14 @@ public void beforeCompilation(BeforeCompilationAccess access) { nextIndex += conditionSize; } - WindowsVMLockSupport lockSupport = ImageSingletons.lookup(WindowsVMLockSupport.class); + WindowsVMLockSupport lockSupport = WindowsVMLockSupport.singleton(); lockSupport.mutexes = mutexes; lockSupport.conditions = conditions; lockSupport.syncStructs = new byte[nextIndex]; } } -public final class WindowsVMLockSupport { +public final class WindowsVMLockSupport extends VMLockSupport { /** All mutexes, so that we can initialize them at run time when the VM starts. */ @UnknownObjectField(types = WindowsVMMutex[].class)// WindowsVMMutex[] mutexes; @@ -132,16 +134,22 @@ public final class WindowsVMLockSupport { @UnknownObjectField(types = byte[].class)// byte[] syncStructs; + @Fold + public static WindowsVMLockSupport singleton() { + return (WindowsVMLockSupport) ImageSingletons.lookup(VMLockSupport.class); + } + /** * Must be called once early during startup, before any mutex or condition is used. */ @Uninterruptible(reason = "Called from uninterruptible code. Too early for safepoints.") public static void initialize() { - for (WindowsVMMutex mutex : ImageSingletons.lookup(WindowsVMLockSupport.class).mutexes) { + WindowsVMLockSupport support = WindowsVMLockSupport.singleton(); + for (WindowsVMMutex mutex : support.mutexes) { // critical sections on windows always support recursive locking Process.InitializeCriticalSection(mutex.getStructPointer()); } - for (WindowsVMCondition condition : ImageSingletons.lookup(WindowsVMLockSupport.class).conditions) { + for (WindowsVMCondition condition : support.conditions) { Process.InitializeConditionVariable(condition.getStructPointer()); } } @@ -159,6 +167,16 @@ static void checkResult(int result, String functionName) { ImageSingletons.lookup(LogHandler.class).fatalError(); } } + + @Override + public VMMutex[] getMutexes() { + return mutexes; + } + + @Override + public VMCondition[] getConditions() { + return conditions; + } } final class WindowsVMMutex extends VMMutex { @@ -166,12 +184,13 @@ final class WindowsVMMutex extends VMMutex { UnsignedWord structOffset; @Platforms(Platform.HOSTED_ONLY.class) - protected WindowsVMMutex() { + protected WindowsVMMutex(String name) { + super(name); } @Uninterruptible(reason = "Called from uninterruptible code.") Process.PCRITICAL_SECTION getStructPointer() { - return (Process.PCRITICAL_SECTION) Word.objectToUntrackedPointer(ImageSingletons.lookup(WindowsVMLockSupport.class).syncStructs).add(structOffset); + return (Process.PCRITICAL_SECTION) Word.objectToUntrackedPointer(WindowsVMLockSupport.singleton().syncStructs).add(structOffset); } @Override @@ -229,7 +248,7 @@ final class WindowsVMCondition extends VMCondition { @Uninterruptible(reason = "Called from uninterruptible code.") Process.PCONDITION_VARIABLE getStructPointer() { - return (Process.PCONDITION_VARIABLE) Word.objectToUntrackedPointer(ImageSingletons.lookup(WindowsVMLockSupport.class).syncStructs).add(structOffset); + return (Process.PCONDITION_VARIABLE) Word.objectToUntrackedPointer(WindowsVMLockSupport.singleton().syncStructs).add(structOffset); } @Override diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/ErrHandlingAPI.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/ErrHandlingAPI.java index 1e12e0cf5219..2f819ac8123b 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/ErrHandlingAPI.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/ErrHandlingAPI.java @@ -31,7 +31,10 @@ import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.struct.CField; +import org.graalvm.nativeimage.c.struct.CFieldAddress; import org.graalvm.nativeimage.c.struct.CStruct; +import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.nativeimage.c.type.VoidPointer; import org.graalvm.word.PointerBase; import com.oracle.svm.core.RegisterDumper; @@ -62,15 +65,27 @@ public interface EXCEPTION_POINTERS extends PointerBase { } /** Contains a description of the exception. */ - @CStruct + @CStruct(isIncomplete = true) public interface EXCEPTION_RECORD extends PointerBase { @CField int ExceptionCode(); + + @CField + VoidPointer ExceptionAddress(); + + @CField + int NumberParameters(); + + @CFieldAddress + CLongPointer ExceptionInformation(); } @CConstant public static native int EXCEPTION_ACCESS_VIOLATION(); + @CConstant + public static native int EXCEPTION_IN_PAGE_ERROR(); + /** Contains processor-specific register data. */ @CStruct public interface CONTEXT extends RegisterDumper.Context { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java index 3d985e0c8fec..053ab9692857 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RegisterDumper.java @@ -26,6 +26,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.word.PointerBase; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.log.Log; @@ -40,14 +41,19 @@ static RegisterDumper singleton() { return ImageSingletons.lookup(RegisterDumper.class); } - static void dumpReg(Log log, String label, long value) { - log.string(label).zhex(value).newline(); + static void dumpReg(Log log, String label, long value, boolean printLocationInfo, boolean allowJavaHeapAccess) { + log.string(label).zhex(value); + if (printLocationInfo) { + log.spaces(1); + SubstrateDiagnostics.printLocationInfo(log, WordFactory.unsigned(value), allowJavaHeapAccess); + } + log.newline(); } interface Context extends PointerBase { } - void dumpRegisters(Log log, Context context); + void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess); PointerBase getHeapBase(Context context); 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 b9df27c46fbd..087470aa568d 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 @@ -27,6 +27,8 @@ import java.util.Arrays; import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; @@ -35,6 +37,7 @@ import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.RestrictHeapAccess; @@ -42,11 +45,16 @@ import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.code.RuntimeCodeInfoHistory; +import com.oracle.svm.core.code.RuntimeCodeInfoMemory; import com.oracle.svm.core.code.UntetheredCodeInfo; +import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.deopt.DeoptimizedFrame; import com.oracle.svm.core.deopt.Deoptimizer; +import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicWord; +import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.stack.JavaFrameAnchor; import com.oracle.svm.core.stack.JavaFrameAnchors; @@ -65,27 +73,8 @@ import com.oracle.svm.core.util.Counter; public class SubstrateDiagnostics { - private static final int REGISTERS = 1; - private static final int FRAME_ANCHORS = REGISTERS << 1; - private static final int DEOPT_STUB_POINTERS = FRAME_ANCHORS << 1; - private static final int TOP_FRAME = DEOPT_STUB_POINTERS << 1; - private static final int THREADS = TOP_FRAME << 1; - private static final int THREAD_STATES = THREADS << 1; - private static final int VM_OPERATIONS = THREAD_STATES << 1; - private static final int RUNTIME_COMPILATIONS = VM_OPERATIONS << 1; - private static final int COUNTERS = RUNTIME_COMPILATIONS << 1; - private static final int CURRENT_THREAD_RAW_STACKTRACE = COUNTERS << 1; - private static final int CURRENT_THREAD_DECODED_STACKTRACE = CURRENT_THREAD_RAW_STACKTRACE << 1; - private static final int OTHER_STACK_TRACES = CURRENT_THREAD_DECODED_STACKTRACE << 1; - - private static final Stage0StackFramePrintVisitor[] PRINT_VISITORS = new Stage0StackFramePrintVisitor[]{Stage0StackFramePrintVisitor.SINGLETON, Stage1StackFramePrintVisitor.SINGLETON, - StackFramePrintVisitor.SINGLETON}; - - private static final AtomicWord diagnosticThread = new AtomicWord<>(); private static final FastThreadLocalBytes threadOnlyAttachedForCrashHandler = FastThreadLocalFactory.createBytes(() -> 1); - - private static volatile int diagnosticSections = 0; - private static volatile int diagnosticThunkIndex = 0; + private static final PrintDiagnosticsState state = new PrintDiagnosticsState(); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void setOnlyAttachedForCrashHandler(IsolateThread thread) { @@ -97,12 +86,33 @@ private static boolean isThreadOnlyAttachedForCrashHandler(IsolateThread thread) } public static boolean isInProgress() { - return diagnosticThread.get().isNonNull(); + return state.diagnosticThread.get().isNonNull(); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static boolean isInProgressByCurrentThread() { + return state.diagnosticThread.get() == CurrentIsolate.getCurrentThread(); + } + + public static int maxInvocations() { + int result = 0; + DiagnosticThunkRegister thunks = DiagnosticThunkRegister.getSingleton(); + for (int i = 0; i < thunks.size(); i++) { + result += thunks.getThunk(i).maxInvocations(); + } + return result; + } + + public static void printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess) { + if (value.notEqual(0) && !RuntimeCodeInfoMemory.singleton().printLocationInfo(log, value, allowJavaHeapAccess) && !VMThreads.printLocationInfo(log, value) && + !Heap.getHeap().printLocationInfo(log, value, allowJavaHeapAccess)) { + log.string("is an unknown value"); + } } /** Prints extensive diagnostic information to the given Log. */ - public static void print(Log log, Pointer sp, CodePointer ip) { - print(log, sp, ip, WordFactory.nullPointer()); + public static boolean print(Log log, Pointer sp, CodePointer ip) { + return print(log, sp, ip, WordFactory.nullPointer()); } /** @@ -110,348 +120,541 @@ public static void print(Log log, Pointer sp, CodePointer ip) { * diagnostics, it can happen that the same thread enters this method multiple times. */ @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") - static void print(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context) { + static boolean print(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context) { log.newline(); - IsolateThread currentThread = CurrentIsolate.getCurrentThread(); - if (!diagnosticThread.compareAndSet(WordFactory.nullPointer(), currentThread) && diagnosticThread.get().notEqual(currentThread)) { + // Save the state of the initial error so that this state is consistently used, even if + // further errors occur while printing diagnostics. + if (!state.trySet(log, sp, ip, context) && !isInProgressByCurrentThread()) { log.string("Error: printDiagnostics already in progress by another thread.").newline(); log.newline(); - return; + return false; } - if (diagnosticSections > 0) { - log.newline(); - log.string("An error occurred while printing diagnostics. The remaining part of this section will be skipped.").newline(); + printDiagnosticsForCurrentState(); + return true; + } + + @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "This method is single threaded. The fields 'diagnosticThunkIndex' and 'invocationCount' are only volatile to ensure that the updated field values are written right away.") + private static void printDiagnosticsForCurrentState() { + assert isInProgressByCurrentThread(); + + Log log = state.log; + if (state.diagnosticThunkIndex > 0) { + // An error must have happened earlier as the code for printing diagnostics was invoked + // recursively. log.resetIndentation(); } // Print the various sections of the diagnostics and skip all sections that were already // printed earlier. - if (shouldPrint(REGISTERS)) { - try { - dumpRegisters(log, context); - } catch (Exception e) { - dumpException(log, "dumpRegisters", e); + int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + while (state.diagnosticThunkIndex < numDiagnosticThunks) { + DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(state.diagnosticThunkIndex); + while (++state.invocationCount <= thunk.maxInvocations()) { + try { + thunk.printDiagnostics(log, state.invocationCount); + break; + } catch (Throwable e) { + dumpException(log, thunk, e); + } } + + state.diagnosticThunkIndex++; + state.invocationCount = 0; } - if (shouldPrint(FRAME_ANCHORS)) { - try { - dumpJavaFrameAnchors(log); - } catch (Exception e) { - dumpException(log, "dumpJavaFrameAnchors", e); - } + // Reset the state so that another thread can print diagnostics. + state.clear(); + } + + static void dumpRuntimeCompilation(Log log) { + assert VMOperation.isInProgressAtSafepoint(); + try { + RuntimeCodeInfoHistory.singleton().printRecentOperations(log, true); + } catch (Exception e) { + dumpException(log, "DumpCodeCacheHistory", e); } - if (shouldPrint(DEOPT_STUB_POINTERS)) { - try { - dumpDeoptStubPointer(log); - } catch (Exception e) { - dumpException(log, "dumpDeoptStubPointer", e); - } + log.newline(); + try { + RuntimeCodeInfoMemory.singleton().printTable(log, true); + } catch (Exception e) { + dumpException(log, "DumpRuntimeCodeInfoMemory", e); } - if (shouldPrint(TOP_FRAME)) { - try { - dumpTopFrame(log, sp, ip); - } catch (Exception e) { - dumpException(log, "dumpTopFrame", e); - } + log.newline(); + try { + Deoptimizer.logRecentDeoptimizationEvents(log); + } catch (Exception e) { + dumpException(log, "DumpRecentDeoptimizations", e); } + } - if (shouldPrint(THREADS)) { - try { - dumpVMThreads(log); - } catch (Exception e) { - dumpException(log, "dumpVMThreads", e); - } + private static void dumpException(Log log, DiagnosticThunk thunk, Throwable e) { + dumpException(log, thunk.getClass().getName(), e); + } + + private static void dumpException(Log log, String currentDumper, Throwable e) { + log.newline().string("[!!! Exception while executing ").string(currentDumper).string(": ").string(e.getClass().getName()).string("]"); + log.resetIndentation().newline(); + } + + @Uninterruptible(reason = "Prevent deoptimization of stack frames while in this method.") + private static long getTotalFrameSize(Pointer sp, CodePointer ip) { + DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(sp); + if (deoptFrame != null) { + return deoptFrame.getSourceTotalFrameSize(); } - if (shouldPrint(THREAD_STATES)) { + UntetheredCodeInfo untetheredInfo = CodeInfoTable.lookupCodeInfo(ip); + if (untetheredInfo.isNonNull()) { + Object tether = CodeInfoAccess.acquireTether(untetheredInfo); try { - dumpVMThreadState(log, currentThread); - } catch (Exception e) { - dumpException(log, "dumpVMThreadState", e); + CodeInfo codeInfo = CodeInfoAccess.convert(untetheredInfo, tether); + return getTotalFrameSize0(ip, codeInfo); + } finally { + CodeInfoAccess.releaseTether(untetheredInfo, tether); } } + return -1; + } - if (shouldPrint(VM_OPERATIONS)) { - try { - dumpRecentVMOperations(log); - } catch (Exception e) { - dumpException(log, "dumpRecentVMOperations", e); + @Uninterruptible(reason = "Wrap the now safe call to interruptibly look up the frame size.", calleeMustBe = false) + private static long getTotalFrameSize0(CodePointer ip, CodeInfo codeInfo) { + return CodeInfoAccess.lookupTotalFrameSize(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip)); + } + + private static void logFrameAnchors(Log log, IsolateThread thread) { + JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor(thread); + if (anchor.isNull()) { + log.string("No anchors").newline(); + } + while (anchor.isNonNull()) { + log.string("Anchor ").zhex(anchor.rawValue()).string(" LastJavaSP ").zhex(anchor.getLastJavaSP().rawValue()).string(" LastJavaIP ").zhex(anchor.getLastJavaIP().rawValue()).newline(); + anchor = anchor.getPreviousAnchor(); + } + } + + private static class PrintDiagnosticsState { + AtomicWord diagnosticThread = new AtomicWord<>(); + volatile int diagnosticThunkIndex; + volatile int invocationCount; + + Log log; + Pointer sp; + CodePointer ip; + RegisterDumper.Context context; + + @SuppressWarnings("hiding") + public boolean trySet(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context) { + if (diagnosticThread.compareAndSet(WordFactory.nullPointer(), CurrentIsolate.getCurrentThread())) { + assert diagnosticThunkIndex == 0; + assert invocationCount == 0; + this.log = log; + this.sp = sp; + this.ip = ip; + this.context = context; + return true; } + return false; } - if (shouldPrint(RUNTIME_COMPILATIONS)) { - dumpRuntimeCompilation(log); + public void clear() { + log = null; + sp = WordFactory.nullPointer(); + ip = WordFactory.nullPointer(); + context = WordFactory.nullPointer(); + + diagnosticThunkIndex = 0; + invocationCount = 0; + + diagnosticThread.set(WordFactory.nullPointer()); } + } - if (shouldPrint(COUNTERS)) { - try { - dumpCounters(log); - } catch (Exception e) { - dumpException(log, "dumpCounters", e); + private static class DumpRegisters extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 3; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + RegisterDumper.Context context = state.context; + if (context.isNonNull()) { + log.string("General purpose register values:").indent(true); + RegisterDumper.singleton().dumpRegisters(log, context, invocationCount <= 2, invocationCount == 1); + log.indent(false); } } + } - if (shouldPrint(CURRENT_THREAD_RAW_STACKTRACE)) { - try { - dumpStacktraceRaw(log, sp); - } catch (Exception e) { - dumpException(log, "dumpStacktraceRaw", e); + private static class DumpInstructions extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 3; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (invocationCount < 3) { + printBytesBeforeAndAfterIp(log, invocationCount); + } else if (invocationCount == 3) { + printWord(log); } } - if (shouldPrint(CURRENT_THREAD_DECODED_STACKTRACE)) { - dumpStacktrace(log, sp, ip); + private static void printBytesBeforeAndAfterIp(Log log, int invocationCount) { + // print 64 or 32 instruction bytes. + int bytesToPrint = 64 >> invocationCount; + hexDump(log, state.ip, bytesToPrint, bytesToPrint); } - if (shouldPrint(OTHER_STACK_TRACES)) { - if (VMOperation.isInProgressAtSafepoint()) { + private static void printWord(Log log) { + // just print one word starting at the ip + hexDump(log, state.ip, 0, ConfigurationValues.getTarget().wordSize); + } + + private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesAfter) { + log.string("Printing Instructions (ip=").zhex(ip).string("):").indent(true); + log.hexdump(((Pointer) ip).subtract(bytesBefore), 1, bytesBefore + bytesAfter); + log.indent(false).newline(); + } + } + + private static class DumpTopOfCurrentThreadStack extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + Pointer sp = state.sp; + log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); + + UnsignedWord stackBase = VMThreads.StackBase.get(); + if (stackBase.equal(0)) { /* - * Only used for diagnostics - iterate all threads without locking the threads - * mutex. + * We have to be careful here and not dump too much of the stack: if there are not + * many frames on the stack, we segfault when going past the beginning of the stack. */ - for (IsolateThread vmThread = VMThreads.firstThreadUnsafe(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) { - if (vmThread == currentThread) { - continue; - } - try { - dumpStacktrace(log, vmThread); - } catch (Exception e) { - dumpException(log, "dumpStacktrace", e); - } + log.hexdump(state.sp, 8, 16); + } else { + int bytesToPrint = 512; + UnsignedWord availableBytes = stackBase.subtract(sp); + if (availableBytes.belowThan(bytesToPrint)) { + bytesToPrint = NumUtil.safeToInt(availableBytes.rawValue()); } - } - } - int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); - while (diagnosticThunkIndex < numDiagnosticThunks) { - try { - int index = diagnosticThunkIndex++; - DiagnosticThunkRegister.getSingleton().callDiagnosticThunk(log, index); - } catch (Exception e) { - dumpException(log, "callThunks", e); + log.hexdump(sp, 8, bytesToPrint / 8); } + log.indent(false).newline(); } - - diagnosticThunkIndex = 0; - diagnosticSections = 0; - diagnosticThread.set(WordFactory.nullPointer()); } - private static boolean shouldPrint(int sectionBit) { - if ((diagnosticSections & sectionBit) == 0) { - diagnosticSections |= sectionBit; - return true; + private static class DumpDeoptStubPointer extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; } - return false; - } - private static void dumpException(Log log, String context, Exception e) { - log.newline().string("[!!! Exception during ").string(context).string(": ").string(e.getClass().getName()).string("]").newline(); + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (DeoptimizationSupport.enabled()) { + log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline(); + } + } } - private static void dumpRegisters(Log log, RegisterDumper.Context context) { - if (context.isNonNull()) { - log.string("General Purpose Register Set values:").newline(); - log.indent(true); - RegisterDumper.singleton().dumpRegisters(log, context); + private static class DumpTopFrame extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + // We already dump all safe values first, so there is nothing we could retry if an error + // occurs. + Pointer sp = state.sp; + CodePointer ip = state.ip; + + log.string("Top frame info:").indent(true); + if (sp.isNonNull() && ip.isNonNull()) { + long totalFrameSize = getTotalFrameSize(sp, ip); + DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(sp); + if (deoptFrame != null) { + log.string("RSP ").zhex(sp).string(" frame was deoptimized:").newline(); + log.string("SourcePC ").zhex(deoptFrame.getSourcePC()).newline(); + log.string("SourceTotalFrameSize ").signed(totalFrameSize).newline(); + } else if (totalFrameSize != -1) { + log.string("TotalFrameSize in CodeInfoTable ").signed(totalFrameSize).newline(); + } + + if (totalFrameSize == -1) { + log.string("Does not look like a Java Frame. Use JavaFrameAnchors to find LastJavaSP:").newline(); + JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor(); + while (anchor.isNonNull() && anchor.getLastJavaSP().belowOrEqual(sp)) { + anchor = anchor.getPreviousAnchor(); + } + + if (anchor.isNonNull()) { + log.string("Found matching Anchor:").zhex(anchor).newline(); + Pointer lastSp = anchor.getLastJavaSP(); + log.string("LastJavaSP ").zhex(lastSp).newline(); + CodePointer lastIp = anchor.getLastJavaIP(); + log.string("LastJavaIP ").zhex(lastIp).newline(); + } + } + } log.indent(false); } } - private static void dumpJavaFrameAnchors(Log log) { - log.string("JavaFrameAnchor dump:").newline(); - log.indent(true); - JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor(); - if (anchor.isNull()) { - log.string("No anchors").newline(); + private static class DumpThreads extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; } - while (anchor.isNonNull()) { - log.string("Anchor ").zhex(anchor.rawValue()).string(" LastJavaSP ").zhex(anchor.getLastJavaSP().rawValue()).string(" LastJavaIP ").zhex(anchor.getLastJavaIP().rawValue()).newline(); - anchor = anchor.getPreviousAnchor(); + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + dumpThreads(log, invocationCount == 1); } - log.indent(false); - } - private static void dumpDeoptStubPointer(Log log) { - if (DeoptimizationSupport.enabled()) { - log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer().rawValue()).newline().newline(); + private static void dumpThreads(Log log, boolean accessThreadObject) { + log.string("Threads:").indent(true); + // Only used for diagnostics - iterate all threads without locking the thread mutex. + for (IsolateThread thread = VMThreads.firstThreadUnsafe(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) { + log.zhex(thread).spaces(1).string(VMThreads.StatusSupport.getStatusString(thread)); + if (accessThreadObject) { + Thread threadObj = JavaThreads.fromVMThread(thread); + log.string(" \"").string(threadObj.getName()).string("\" - ").object(threadObj); + if (threadObj.isDaemon()) { + log.string(", daemon"); + } + } + log.string(", stack(").zhex(VMThreads.StackEnd.get(thread)).string(",").zhex(VMThreads.StackBase.get(thread)).string(")"); + log.newline(); + } + log.indent(false); } } - private static void dumpTopFrame(Log log, Pointer sp, CodePointer ip) { - log.string("TopFrame info:").newline(); - log.indent(true); - if (sp.isNonNull() && ip.isNonNull()) { - long totalFrameSize = getTotalFrameSize(sp, ip); - DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(sp); - if (deoptFrame != null) { - log.string("RSP ").zhex(sp.rawValue()).string(" frame was deoptimized:").newline(); - log.string("SourcePC ").zhex(deoptFrame.getSourcePC().rawValue()).newline(); - log.string("SourceTotalFrameSize ").signed(totalFrameSize).newline(); - } else if (totalFrameSize != -1) { - log.string("TotalFrameSize in CodeInfoTable ").signed(totalFrameSize).newline(); - } + private static class DumpCurrentThreadLocals extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; + } - if (totalFrameSize == -1) { - log.string("Does not look like a Java Frame. Use JavaFrameAnchors to find LastJavaSP:").newline(); - JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor(); - while (anchor.isNonNull() && anchor.getLastJavaSP().belowOrEqual(sp)) { - anchor = anchor.getPreviousAnchor(); - } + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + printThreadLocals(log, invocationCount); + } - if (anchor.isNonNull()) { - log.string("Found matching Anchor:").zhex(anchor.rawValue()).newline(); - Pointer lastSp = anchor.getLastJavaSP(); - log.string("LastJavaSP ").zhex(lastSp.rawValue()).newline(); - CodePointer lastIp = anchor.getLastJavaIP(); - log.string("LastJavaIP ").zhex(lastIp.rawValue()).newline(); + private static void printThreadLocals(Log log, int invocationCount) { + IsolateThread currentThread = CurrentIsolate.getCurrentThread(); + if (isThreadOnlyAttachedForCrashHandler(currentThread)) { + if (invocationCount == 1) { + log.string("The failing thread ").zhex(currentThread).string(" does not have a full set of VM thread locals as it is an unattached thread.").newline(); + log.newline(); } + } else { + log.string("VM thread locals for the failing thread ").zhex(currentThread).string(":").indent(true); + VMThreadLocalInfos.dumpToLog(log, currentThread, invocationCount == 1); + log.indent(false); } } - log.indent(false); } - @Uninterruptible(reason = "Prevent deoptimization of stack frames while in this method.") - private static long getTotalFrameSize(Pointer sp, CodePointer ip) { - DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(sp); - if (deoptFrame != null) { - return deoptFrame.getSourceTotalFrameSize(); + private static class DumpCurrentVMOperation extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; } - UntetheredCodeInfo untetheredInfo = CodeInfoTable.lookupCodeInfo(ip); - if (untetheredInfo.isNonNull()) { - Object tether = CodeInfoAccess.acquireTether(untetheredInfo); - try { - CodeInfo codeInfo = CodeInfoAccess.convert(untetheredInfo, tether); - return getTotalFrameSize0(ip, codeInfo); - } finally { - CodeInfoAccess.releaseTether(untetheredInfo, tether); - } + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + VMOperationControl.printCurrentVMOperation(log, invocationCount == 1); + log.newline(); } - return -1; } - @Uninterruptible(reason = "Wrap the now safe call to interruptibly look up the frame size.", calleeMustBe = false) - private static long getTotalFrameSize0(CodePointer ip, CodeInfo codeInfo) { - return CodeInfoAccess.lookupTotalFrameSize(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip)); - } + private static class DumpVMOperationHistory extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; + } - private static void dumpVMThreads(Log log) { - log.string("VMThreads info:").newline(); - log.indent(true); - /* Only used for diagnostics - iterate all threads without locking the threads mutex. */ - for (IsolateThread vmThread = VMThreads.firstThreadUnsafe(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) { - log.string("VMThread ").zhex(vmThread.rawValue()).spaces(2).string(VMThreads.StatusSupport.getStatusString(vmThread)) - .spaces(2).object(JavaThreads.fromVMThread(vmThread)).newline(); + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + VMOperationControl.printRecentEvents(log, invocationCount == 1); } - log.indent(false); } - private static void dumpVMThreadState(Log log, IsolateThread currentThread) { - if (isThreadOnlyAttachedForCrashHandler(currentThread)) { - log.string("The current thread ").zhex(currentThread.rawValue()).string(" does not have a VM Thread State as it is an unattached thread.").newline(); - log.newline(); - } else { - log.string("VM Thread State for current thread ").zhex(currentThread.rawValue()).string(":").newline(); - log.indent(true); - VMThreadLocalInfos.dumpToLog(log, currentThread); - log.indent(false); + private static class DumpCodeCacheHistory extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; } - } - private static void dumpRecentVMOperations(Log log) { - log.string("VMOperation dump:").newline(); - log.indent(true); - VMOperationControl.logRecentEvents(log); - log.indent(false); + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (DeoptimizationSupport.enabled()) { + RuntimeCodeInfoHistory.singleton().printRecentOperations(log, invocationCount == 1); + } + } } - static void dumpRuntimeCompilation(Log log) { - if (DeoptimizationSupport.enabled()) { - log.newline().string("RuntimeCodeCache dump:").newline(); - log.indent(true); - try { - CodeInfoTable.getRuntimeCodeCache().logRecentOperations(log); - } catch (Exception e) { - dumpException(log, "dumpRecentRuntimeCodeCacheOperations", e); - } - log.newline(); - try { - CodeInfoTable.getRuntimeCodeCache().logTable(log); - } catch (Exception e) { - dumpException(log, "dumpRuntimeCodeCacheTable", e); + private static class DumpRuntimeCodeInfoMemory extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 2; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (DeoptimizationSupport.enabled()) { + RuntimeCodeInfoMemory.singleton().printTable(log, invocationCount == 1); } - log.indent(false); + } + } - try { - dumpRecentDeopts(log); - } catch (Exception e) { - dumpException(log, "dumpRecentDeopts", e); + private static class DumpRecentDeoptimizations extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (DeoptimizationSupport.enabled()) { + Deoptimizer.logRecentDeoptimizationEvents(log); } } } - private static void dumpRecentDeopts(Log log) { - log.string("Deoptimizer dump:").newline(); - log.indent(true); - Deoptimizer.logRecentDeoptimizationEvents(log); - log.indent(false); + private static class DumpCounters extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + log.string("Counters:").indent(true); + Counter.logValues(); + log.indent(false); + } } - private static void dumpCounters(Log log) { - log.string("Dump Counters:").newline(); - log.indent(true); - Counter.logValues(); - log.indent(false); + private static class DumpCurrentThreadFrameAnchors extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + IsolateThread currentThread = CurrentIsolate.getCurrentThread(); + log.string("Java frame anchors for the failing thread ").zhex(currentThread).string(":").indent(true); + logFrameAnchors(log, currentThread); + log.indent(false); + } } - private static void dumpStacktraceRaw(Log log, Pointer sp) { - log.string("Raw Stacktrace:").newline(); - log.indent(true); - /* - * We have to be careful here and not dump too much of the stack: if there are not many - * frames on the stack, we segfault when going past the beginning of the stack. - */ - log.hexdump(sp, 8, 16); - log.indent(false); + private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk { + private static final Stage0StackFramePrintVisitor[] PRINT_VISITORS = new Stage0StackFramePrintVisitor[]{StackFramePrintVisitor.SINGLETON, Stage1StackFramePrintVisitor.SINGLETON, + Stage0StackFramePrintVisitor.SINGLETON}; + + @Override + public int maxInvocations() { + return 3; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + Pointer sp = state.sp; + CodePointer ip = state.ip; + log.string("Stacktrace for the failing thread ").zhex(CurrentIsolate.getCurrentThread()).string(":").indent(true); + ThreadStackPrinter.printStacktrace(sp, ip, PRINT_VISITORS[invocationCount - 1], log); + log.indent(false); + } } - private static void dumpStacktrace(Log log, Pointer sp, CodePointer ip) { - for (int i = 0; i < PRINT_VISITORS.length; i++) { - try { - log.string("Stacktrace Stage ").signed(i).string(":").newline(); - log.indent(true); - ThreadStackPrinter.printStacktrace(sp, ip, PRINT_VISITORS[i], log); - log.indent(false); - } catch (Exception e) { - dumpException(log, "dumpStacktrace", e); + private static class DumpOtherStackTraces extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + if (VMOperation.isInProgressAtSafepoint()) { + // Iterate all threads without checking if the thread mutex is locked (it should + // be locked by this thread though because we are at a safepoint). + for (IsolateThread vmThread = VMThreads.firstThreadUnsafe(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) { + if (vmThread == CurrentIsolate.getCurrentThread()) { + continue; + } + try { + log.string("Thread ").zhex(vmThread).string(":").indent(true); + printFrameAnchors(log, vmThread); + printStackTrace(log, vmThread); + log.indent(false); + } catch (Exception e) { + dumpException(log, this, e); + } + } } } - } - private static void dumpStacktrace(Log log, IsolateThread vmThread) { - log.string("Full Stacktrace for VMThread ").zhex(vmThread.rawValue()).string(":").newline(); - log.indent(true); - JavaStackWalker.walkThread(vmThread, StackFramePrintVisitor.SINGLETON, log); - log.indent(false); - } + private static void printFrameAnchors(Log log, IsolateThread vmThread) { + log.string("Frame anchors:").indent(true); + logFrameAnchors(log, vmThread); + log.indent(false); + } - /** The functional interface for a "thunk" that does not allocate. */ - @FunctionalInterface - public interface DiagnosticThunk { + private static void printStackTrace(Log log, IsolateThread vmThread) { + log.string("Stacktrace:").indent(true); + JavaStackWalker.walkThread(vmThread, StackFramePrintVisitor.SINGLETON, log); + log.redent(false); + } + } - /** The method to be supplied by the implementor. */ + public abstract static class DiagnosticThunk { + /** + * Prints diagnostic information. This method may be invoked multiple times if an error + * (e.g., exception or segfault) occurred during execution. However, the method will only be + * invoked at most {@link #maxInvocations()} times. When the method is invoked for the first + * time, the argument invocationCount is 1. + */ @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") - void invokeWithoutAllocation(Log log); + public abstract void printDiagnostics(Log log, int invocationCount); + + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public abstract int maxInvocations(); } public static class DiagnosticThunkRegister { - - DiagnosticThunk[] diagnosticThunkRegistry; + DiagnosticThunk[] diagnosticThunks; /** * Get the register. @@ -470,27 +673,29 @@ public static synchronized DiagnosticThunkRegister getSingleton() { @Platforms(Platform.HOSTED_ONLY.class) DiagnosticThunkRegister() { - this.diagnosticThunkRegistry = new DiagnosticThunk[0]; + this.diagnosticThunks = new DiagnosticThunk[]{new DumpRegisters(), new DumpInstructions(), new DumpTopOfCurrentThreadStack(), new DumpDeoptStubPointer(), new DumpTopFrame(), + new DumpThreads(), new DumpCurrentThreadLocals(), new DumpCurrentVMOperation(), new DumpVMOperationHistory(), new DumpCodeCacheHistory(), + new DumpRuntimeCodeInfoMemory(), new DumpRecentDeoptimizations(), new DumpCounters(), new DumpCurrentThreadFrameAnchors(), new DumpCurrentThreadDecodedStackTrace(), + new DumpOtherStackTraces(), new VMLockSupport.DumpVMMutexes()}; } /** Register a diagnostic thunk to be called after a segfault. */ @Platforms(Platform.HOSTED_ONLY.class) /* { Checkstyle: allow synchronization. */ public synchronized void register(DiagnosticThunk diagnosticThunk) { - final DiagnosticThunk[] newArray = Arrays.copyOf(diagnosticThunkRegistry, diagnosticThunkRegistry.length + 1); + final DiagnosticThunk[] newArray = Arrays.copyOf(diagnosticThunks, diagnosticThunks.length + 1); newArray[newArray.length - 1] = diagnosticThunk; - diagnosticThunkRegistry = newArray; + diagnosticThunks = newArray; } /* } Checkstyle: disallow synchronization. */ @Fold int size() { - return diagnosticThunkRegistry.length; + return diagnosticThunks.length; } - /** Call each registered diagnostic thunk. */ - void callDiagnosticThunk(Log log, int index) { - diagnosticThunkRegistry[index].invokeWithoutAllocation(log); + DiagnosticThunk getThunk(int index) { + return diagnosticThunks[index]; } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateSegfaultHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateSegfaultHandler.java index 90a983d12f3d..033b01998a40 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateSegfaultHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateSegfaultHandler.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.options.Option; +import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Isolate; @@ -97,6 +98,8 @@ public static class Options { /** Installs the platform dependent segfault handler. */ protected abstract void install(); + protected abstract void printSignalInfo(Log log, PointerBase signalInfo); + /** Called from the platform dependent segfault handler to enter the isolate. */ @Uninterruptible(reason = "Called from uninterruptible code.") @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true) @@ -134,28 +137,29 @@ protected static boolean tryEnterIsolate(RegisterDumper.Context context) { /** Called from the platform dependent segfault handler to print diagnostics. */ @Uninterruptible(reason = "Must be uninterruptible until we get immune to safepoints.", calleeMustBe = false) @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true) - protected static void dump(RegisterDumper.Context context) { + protected static void dump(PointerBase signalInfo, RegisterDumper.Context context) { VMThreads.StatusSupport.setStatusIgnoreSafepoints(); StackOverflowCheck.singleton().disableStackOverflowChecksForFatalError(); - dumpInterruptibly(context); + dumpInterruptibly(signalInfo, context); } - private static void dumpInterruptibly(RegisterDumper.Context context) { + private static void dumpInterruptibly(PointerBase signalInfo, RegisterDumper.Context context) { PointerBase callerIP = RegisterDumper.singleton().getIP(context); LogHandler logHandler = ImageSingletons.lookup(LogHandler.class); - String msg = "[ [ SubstrateSegfaultHandler caught a segfault. ] ]"; - Log log = Log.enterFatalContext(logHandler, (CodePointer) callerIP, msg, null); + Log log = Log.enterFatalContext(logHandler, (CodePointer) callerIP, "[ [ SubstrateSegfaultHandler caught a segfault. ] ]", null); if (log != null) { log.newline(); - log.string(msg).newline(); + log.string("[ [ SubstrateSegfaultHandler caught a segfault in thread ").zhex(CurrentIsolate.getCurrentThread()).string(" ] ]").newline(); + ImageSingletons.lookup(SubstrateSegfaultHandler.class).printSignalInfo(log, signalInfo); PointerBase sp = RegisterDumper.singleton().getSP(context); PointerBase ip = RegisterDumper.singleton().getIP(context); - SubstrateDiagnostics.print(log, (Pointer) sp, (CodePointer) ip, context); - - log.string("Segfault detected, aborting process. Use runtime option -R:-InstallSegfaultHandler if you don't want to use SubstrateSegfaultHandler.").newline(); - log.newline(); + boolean printedDiagnostics = SubstrateDiagnostics.print(log, (Pointer) sp, (CodePointer) ip, context); + if (printedDiagnostics) { + log.string("Segfault detected, aborting process. Use runtime option -R:-InstallSegfaultHandler if you don't want to use SubstrateSegfaultHandler.").newline(); + log.newline(); + } } logHandler.fatalError(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java index 407b104765a6..3f7a2ba8bd5c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java @@ -253,8 +253,8 @@ public interface Thunk { } /** Prints extensive diagnostic information to the given Log. */ - public static void printDiagnostics(Log log, Pointer sp, CodePointer ip) { - SubstrateDiagnostics.print(log, sp, ip, WordFactory.nullPointer()); + public static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip) { + return SubstrateDiagnostics.print(log, sp, ip, WordFactory.nullPointer()); } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfo.java index c9f8eb1f61ab..be7179b38640 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfo.java @@ -73,5 +73,4 @@ public interface CodeInfo extends UntetheredCodeInfo { */ @DuplicatedInNativeCode // int STATE_UNREACHABLE = STATE_PARTIALLY_FREED + 1; - } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java index 0ba6f44f1948..e439cb9d6f0e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java @@ -142,6 +142,25 @@ public static int getState(CodeInfo info) { return cast(info).getState(); } + public static String stateToString(int codeInfoState) { + switch (codeInfoState) { + case CodeInfo.STATE_CREATED: + return "created"; + case CodeInfo.STATE_CODE_CONSTANTS_LIVE: + return "code constants live"; + case CodeInfo.STATE_NON_ENTRANT: + return "non-entrant"; + case CodeInfo.STATE_READY_FOR_INVALIDATION: + return "ready for invalidation"; + case CodeInfo.STATE_PARTIALLY_FREED: + return "partially freed"; + case CodeInfo.STATE_UNREACHABLE: + return "unreachable"; + default: + return "invalid state"; + } + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isAlive(CodeInfo info) { return isAliveState(cast(info).getState()); @@ -355,4 +374,24 @@ private static CodeInfoImpl cast(UntetheredCodeInfo info) { assert isValid(info); return (CodeInfoImpl) info; } + + public static void printCodeInfo(Log log, CodeInfo info, boolean allowJavaHeapAccess) { + String name = allowJavaHeapAccess ? CodeInfoAccess.getName(info) : null; + printCodeInfo(log, info, CodeInfoAccess.getState(info), name, CodeInfoAccess.getCodeStart(info), CodeInfoAccess.getCodeEnd(info)); + } + + public static void printCodeInfo(Log log, UntetheredCodeInfo codeInfo, int state, String name, CodePointer codeStart, CodePointer codeEnd) { + log.string("CodeInfo (").zhex(codeInfo).string(" - ").zhex(((UnsignedWord) codeInfo).add(RuntimeCodeInfoAccess.getSizeOfCodeInfo()).subtract(1)).string("), ") + .string(CodeInfoAccess.stateToString(state)); + if (name != null) { + log.string(" - ").string(name); + } + log.string(", ip: (").zhex(codeStart).string(" - ").zhex(codeEnd).string(")"); + log.newline(); + /* + * Note that we are not trying to output the InstalledCode object. It is not a pinned + * object, so when log printing (for, e.g., a fatal error) occurs during a GC, then the VM + * could segfault. + */ + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 17d54ca21a0e..7cdbc0c1d959 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -222,18 +222,14 @@ private static void invalidateCodeAtSafepoint0(CodeInfo info) { private static void invalidateCodeAtSafepoint(CodeInfo info) { VMOperation.guaranteeInProgressAtSafepoint("Must be at a safepoint"); RuntimeCodeCache codeCache = getRuntimeCodeCache(); - long num = codeCache.logMethodOperation(info, RuntimeCodeCache.INFO_INVALIDATE); codeCache.invalidateMethod(info); - codeCache.logMethodOperationEnd(num); } @RestrictHeapAccess(access = Access.NO_ALLOCATION, reason = "Called by the GC") public static void invalidateNonStackCodeAtSafepoint(CodeInfo info) { VMOperation.guaranteeGCInProgress("Must only be called during a GC."); RuntimeCodeCache codeCache = getRuntimeCodeCache(); - long num = codeCache.logMethodOperation(info, RuntimeCodeCache.INFO_INVALIDATE); codeCache.invalidateNonStackMethod(info); - codeCache.logMethodOperationEnd(num); } @Uninterruptible(reason = "Prevent the GC from freeing the CodeInfo.", callerMustBe = true) @@ -279,7 +275,9 @@ public void duringSetup(DuringSetupAccess access) { ImageSingletons.add(CodeInfoDecoderCounters.class, new CodeInfoDecoderCounters()); ImageSingletons.add(CodeInfoEncoder.Counters.class, new CodeInfoEncoder.Counters()); ImageSingletons.add(ImageCodeInfo.class, new ImageCodeInfo()); + ImageSingletons.add(RuntimeCodeInfoHistory.class, new RuntimeCodeInfoHistory()); ImageSingletons.add(RuntimeCodeCache.class, new RuntimeCodeCache()); + ImageSingletons.add(RuntimeCodeInfoMemory.class, new RuntimeCodeInfoMemory()); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java index 7cd89e6b92d6..703a1c402bb1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java @@ -48,14 +48,12 @@ import com.oracle.svm.core.deopt.DeoptimizedFrame; import com.oracle.svm.core.deopt.Deoptimizer; import com.oracle.svm.core.deopt.SubstrateInstalledCode; -import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.StackFrameVisitor; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.util.Counter; -import com.oracle.svm.core.util.RingBuffer; public class RuntimeCodeCache { @@ -67,18 +65,12 @@ public static class Options { public static final RuntimeOptionKey WriteableCodeCache = new RuntimeOptionKey<>(false); } - private final RingBuffer recentCodeCacheOperations = new RingBuffer<>(30, CodeCacheLogEntry::new); - private long codeCacheOperationSequenceNumber; - private final Counter.Group counters = new Counter.Group(CodeInfoTable.Options.CodeCacheCounters, "RuntimeCodeInfo"); private final Counter lookupMethodCount = new Counter(counters, "lookupMethod", ""); private final Counter addMethodCount = new Counter(counters, "addMethod", ""); private final Counter invalidateMethodCount = new Counter(counters, "invalidateMethod", ""); private final CodeNotOnStackVerifier codeNotOnStackVerifier = new CodeNotOnStackVerifier(); - static final String INFO_ADD = "Add"; - static final String INFO_INVALIDATE = "Invalidate"; - private static final int INITIAL_TABLE_SIZE = 100; private NonmovableArray codeInfos; @@ -88,7 +80,6 @@ public static class Options { public RuntimeCodeCache() { } - /** Tear down the heap, return all allocated virtual memory chunks to VirtualMemoryProvider. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public final void tearDown() { NonmovableArrays.releaseUnmanagedArray(codeInfos); @@ -159,22 +150,14 @@ private static int binarySearch(NonmovableArray a, int fromI } public void addMethod(CodeInfo info) { - VMOperation.guaranteeInProgressAtSafepoint("Modifying code tables that are used by the GC"); + assert VMOperation.isInProgressAtSafepoint() : "Modifying code tables that are used by the GC"; InstalledCodeObserverSupport.activateObservers(RuntimeCodeInfoAccess.getCodeObserverHandles(info)); - long num = logMethodOperation(info, INFO_ADD); addMethodOperation(info); - logMethodOperationEnd(num); } private void addMethodOperation(CodeInfo info) { addMethodCount.inc(); assert verifyTable(); - if (Options.TraceCodeCache.getValue()) { - Log.log().string("[" + INFO_ADD + " method: "); - logCodeInfo(Log.log(), info); - Log.log().string("]").newline(); - } - if (codeInfos.isNull() || numCodeInfos >= NonmovableArrays.lengthOf(codeInfos)) { enlargeTable(); assert verifyTable(); @@ -188,9 +171,7 @@ private void addMethodOperation(CodeInfo info) { numCodeInfos++; NonmovableArrays.setWord(codeInfos, insertionPoint, info); - if (Options.TraceCodeCache.getValue()) { - logTable(); - } + RuntimeCodeInfoHistory.singleton().logAdd(info); assert verifyTable(); } @@ -230,11 +211,6 @@ private void prepareInvalidation(CodeInfo info) { VMOperation.guaranteeInProgressAtSafepoint("Modifying code tables that are used by the GC"); invalidateMethodCount.inc(); assert verifyTable(); - if (Options.TraceCodeCache.getValue()) { - Log.log().string("[").string(INFO_INVALIDATE).string(" method: "); - logCodeInfo(Log.log(), info); - Log.log().string("]").newline(); - } SubstrateInstalledCode installedCode = RuntimeCodeInfoAccess.getInstalledCode(info); if (installedCode != null) { @@ -262,10 +238,7 @@ private void finishInvalidation(CodeInfo info, boolean notifyGC) { NonmovableArrays.setWord(codeInfos, numCodeInfos, WordFactory.nullPointer()); RuntimeCodeInfoAccess.partialReleaseAfterInvalidate(info, notifyGC); - - if (Options.TraceCodeCache.getValue()) { - logTable(); - } + RuntimeCodeInfoHistory.singleton().logInvalidate(info); assert verifyTable(); } @@ -293,69 +266,6 @@ private boolean verifyTable() { return true; } - public void logTable() { - logTable(Log.log()); - } - - private static final RingBuffer.Consumer consumer = (context, e) -> e.log(Log.log()); - - public void logRecentOperations(Log log) { - log.string("== [Recent RuntimeCodeCache operations: "); - recentCodeCacheOperations.foreach(consumer); - log.string("]").newline(); - } - - public void logTable(Log log) { - log.string("== [RuntimeCodeCache: ").signed(numCodeInfos).string(" methods"); - for (int i = 0; i < numCodeInfos; i++) { - logCodeInfo(log, i); - } - log.string("]").newline(); - } - - @Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.") - private void logCodeInfo(Log log, int i) { - UntetheredCodeInfo untetheredInfo = NonmovableArrays.getWord(codeInfos, i); - Object tether = CodeInfoAccess.acquireTether(untetheredInfo); - try { - CodeInfo info = CodeInfoAccess.convert(untetheredInfo, tether); - logCodeInfo0(log, info); - } finally { - CodeInfoAccess.releaseTether(untetheredInfo, tether); - } - } - - @Uninterruptible(reason = "Pass the now protected CodeInfo to interruptible code.", calleeMustBe = false) - private static void logCodeInfo0(Log log, CodeInfo info) { - log.newline().hex(CodeInfoAccess.getCodeStart(info)).string(" "); - logCodeInfo(log, info); - } - - private static void logCodeInfo(Log log, CodeInfo info) { - logCodeInfo(log, CodeInfoAccess.getName(info), CodeInfoAccess.getCodeStart(info), CodeInfoAccess.getCodeEnd(info), CodeInfoAccess.getCodeSize(info)); - } - - private static void logCodeInfo(Log log, String codeName, CodePointer codeStart, CodePointer codeEnd, UnsignedWord codeSize) { - log.string(codeName); - log.string(" ip: ").hex(codeStart).string(" - ").hex(codeEnd); - log.string(" size: ").unsigned(codeSize); - /* - * Note that we are not trying to output the InstalledCode object. It is not a pinned - * object, so when log printing (for, e.g., a fatal error) occurs during a GC, then the VM - * could segfault. - */ - } - - long logMethodOperation(CodeInfo info, String kind) { - long current = ++codeCacheOperationSequenceNumber; - recentCodeCacheOperations.next().setValues(current, kind, CodeInfoAccess.getName(info), CodeInfoAccess.getCodeStart(info), CodeInfoAccess.getCodeEnd(info), CodeInfoAccess.getCodeSize(info)); - return current; - } - - void logMethodOperationEnd(long operationNumber) { - recentCodeCacheOperations.next().setValues(operationNumber, null, null, WordFactory.nullPointer(), WordFactory.nullPointer(), WordFactory.unsigned(0)); - } - public boolean walkRuntimeMethods(MemoryWalker.Visitor visitor) { VMOperation.guaranteeInProgress("Modifying code tables that are used by the GC"); boolean continueVisiting = true; @@ -384,39 +294,6 @@ private static boolean visitRuntimeMethod0(MemoryWalker.Visitor visitor, CodeInf return visitor.visitCode(codeInfo, ImageSingletons.lookup(CodeInfoMemoryWalker.class)); } - private static class CodeCacheLogEntry { - private long sequenceNumber; - private String kind; - private String codeName; - private CodePointer codeStart; - private CodePointer codeEnd; - private UnsignedWord codeSize; - - @Platforms(Platform.HOSTED_ONLY.class) - CodeCacheLogEntry() { - } - - public void setValues(long sequenceNumber, String kind, String codeName, CodePointer codeStart, CodePointer codeEnd, UnsignedWord codeSize) { - this.sequenceNumber = sequenceNumber; - this.kind = kind; - this.codeName = codeName; - this.codeStart = codeStart; - this.codeEnd = codeEnd; - this.codeSize = codeSize; - } - - public void log(Log log) { - log.newline(); - if (kind != null) { - log.string(kind).string(": "); - logCodeInfo(log, codeName, codeStart, codeEnd, codeSize); - log.string(" ").unsigned(sequenceNumber).string(":{"); - } else { - log.string("}:").unsigned(sequenceNumber); - } - } - } - private static final class CodeNotOnStackVerifier extends StackFrameVisitor { private CodeInfo codeInfoToCheck; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java index 22f2fd0cbe66..bc6322ac101e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java @@ -26,6 +26,7 @@ import java.util.EnumSet; +import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CodePointer; @@ -191,7 +192,7 @@ public static CodeInfo allocateMethodInfo() { } public static CodeInfo allocateMethodInfo(NonmovableObjectArray objectData) { - CodeInfoImpl info = UnmanagedMemory.calloc(SizeOf.unsigned(CodeInfoImpl.class)); + CodeInfoImpl info = UnmanagedMemory.calloc(getSizeOfCodeInfo()); assert objectData.isNonNull() && NonmovableArrays.lengthOf(objectData) == CodeInfoImpl.OBJFIELDS_COUNT; info.setObjectFields(objectData); @@ -201,6 +202,11 @@ public static CodeInfo allocateMethodInfo(NonmovableObjectArray objectDa return info; } + @Fold + public static UnsignedWord getSizeOfCodeInfo() { + return SizeOf.unsigned(CodeInfoImpl.class); + } + static void partialReleaseAfterInvalidate(CodeInfo info, boolean notifyGC) { InstalledCodeObserverSupport.removeObservers(RuntimeCodeInfoAccess.getCodeObserverHandles(info)); releaseMemory(info, notifyGC); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java new file mode 100644 index 000000000000..880563ed6ffe --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.code; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.function.CodePointer; + +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.VMThreads; +import com.oracle.svm.core.util.RingBuffer; + +public class RuntimeCodeInfoHistory { + private static final RingBuffer.Consumer PRINT_WITH_JAVA_HEAP_DATA = RuntimeCodeInfoHistory::printEntryWithJavaHeapData; + private static final RingBuffer.Consumer PRINT_WITHOUT_JAVA_HEAP_DATA = RuntimeCodeInfoHistory::printEntryWithoutJavaHeapData; + + private final RingBuffer recentOperations; + + @Platforms(Platform.HOSTED_ONLY.class) + RuntimeCodeInfoHistory() { + recentOperations = new RingBuffer<>(20, CodeCacheLogEntry::new); + } + + @Fold + public static RuntimeCodeInfoHistory singleton() { + return ImageSingletons.lookup(RuntimeCodeInfoHistory.class); + } + + public void logAdd(CodeInfo info) { + logOperation("Added", info); + } + + public void logMakeNonEntrant(CodeInfo info) { + logOperation("Made non-entrant", info); + } + + public void logInvalidate(CodeInfo info) { + logOperation("Invalidated", info); + } + + private void logOperation(String kind, CodeInfo info) { + assert VMOperation.isInProgressAtSafepoint(); + + traceCodeCache(kind, info, true); + recentOperations.next().setValues(kind, info, CodeInfoAccess.getState(info), CodeInfoAccess.getName(info), CodeInfoAccess.getCodeStart(info), CodeInfoAccess.getCodeEnd(info)); + } + + public void logFree(CodeInfo info) { + assert VMOperation.isInProgressAtSafepoint() || VMThreads.isTearingDown(); + + traceCodeCache("Freed", info, false); + recentOperations.next().setValues("Freed", info, CodeInfoAccess.getState(info), null, CodeInfoAccess.getCodeStart(info), CodeInfoAccess.getCodeEnd(info)); + } + + private static void traceCodeCache(String kind, CodeInfo info, boolean allowJavaHeapAccess) { + if (RuntimeCodeCache.Options.TraceCodeCache.getValue()) { + Log.log().string(kind).string(" method: "); + CodeInfoAccess.printCodeInfo(Log.log(), info, allowJavaHeapAccess); + } + } + + public void printRecentOperations(Log log, boolean allowJavaHeapAccess) { + log.string("The ").signed(recentOperations.size()).string(" most recent RuntimeCodeInfo operations (oldest first): ").indent(true); + recentOperations.foreach(log, allowJavaHeapAccess ? PRINT_WITH_JAVA_HEAP_DATA : PRINT_WITHOUT_JAVA_HEAP_DATA); + log.indent(false); + } + + private static void printEntryWithJavaHeapData(Object context, CodeCacheLogEntry entry) { + printEntry(context, entry, true); + } + + private static void printEntryWithoutJavaHeapData(Object context, CodeCacheLogEntry entry) { + printEntry(context, entry, false); + } + + private static void printEntry(Object context, CodeCacheLogEntry entry, boolean allowJavaHeapAccess) { + Log log = (Log) context; + entry.print(log, allowJavaHeapAccess); + } + + private static class CodeCacheLogEntry { + private long timestamp; + private String kind; + private String codeName; + private CodeInfo codeInfo; + private int codeInfoState; + private CodePointer codeStart; + private CodePointer codeEnd; + + @Platforms(Platform.HOSTED_ONLY.class) + CodeCacheLogEntry() { + } + + public void setValues(String kind, CodeInfo codeInfo, int codeInfoState, String codeName, CodePointer codeStart, CodePointer codeEnd) { + assert Heap.getHeap().isInImageHeap(kind); + this.timestamp = System.currentTimeMillis(); + this.kind = kind; + this.codeInfo = codeInfo; + this.codeInfoState = codeInfoState; + this.codeName = codeName; + this.codeStart = codeStart; + this.codeEnd = codeEnd; + } + + public void print(Log log, boolean allowJavaHeapAccess) { + if (kind != null) { + log.unsigned(timestamp).string(" - ").string(kind).spaces(1); + String name = allowJavaHeapAccess ? codeName : null; + CodeInfoAccess.printCodeInfo(log, codeInfo, codeInfoState, name, codeStart, codeEnd); + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java index 071aec06be27..ef6d37702c6a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java @@ -30,16 +30,18 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.c.function.CodePointer; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.RuntimeCodeCache.CodeInfoVisitor; import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.VMError; @@ -125,6 +127,7 @@ private void add0(CodeInfo info) { } while (resized); NonmovableArrays.setWord(table, index, info); count++; + assert count > 0 : "invalid counter value"; } @@ -243,6 +246,38 @@ private static int nextIndex(int index, int length) { return (index + 1 < length) ? (index + 1) : 0; } + public void printTable(Log log, boolean allowJavaHeapAccess) { + assert VMOperation.isInProgressAtSafepoint() || + SubstrateDiagnostics.isInProgressByCurrentThread() : "outside of a safepoint, this may only be used for printing diagnostics as the table could be freed at any time"; + log.string("RuntimeCodeInfoMemory contains ").signed(count).string(" methods:").indent(true); + if (table.isNonNull()) { + for (int i = 0; i < NonmovableArrays.lengthOf(table); i++) { + printCodeInfo(log, i, allowJavaHeapAccess); + } + } + log.indent(false); + } + + @Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.") + private void printCodeInfo(Log log, int i, boolean allowJavaHeapAccess) { + UntetheredCodeInfo info = NonmovableArrays.getWord(table, i); + if (info.isNonNull()) { + /* + * Newly created CodeInfo objects do not have a tether yet. So, we can't use tethering + * to keep the CodeInfo object alive. Instead, we read all relevant values in + * uninterruptible code and pass those values to interruptible code that does the + * printing. + */ + String name = allowJavaHeapAccess ? UntetheredCodeInfoAccess.getName(info) : null; + printCodeInfo0(log, info, UntetheredCodeInfoAccess.getState(info), name, UntetheredCodeInfoAccess.getCodeStart(info), UntetheredCodeInfoAccess.getCodeEnd(info)); + } + } + + @Uninterruptible(reason = "CodeInfo no longer needs to be protected from the GC.", calleeMustBe = false) + private static void printCodeInfo0(Log log, UntetheredCodeInfo codeInfo, int state, String name, CodePointer codeStart, CodePointer codeEnd) { + CodeInfoAccess.printCodeInfo(log, codeInfo, state, name, codeStart, codeEnd); + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void tearDown() { if (table.isNonNull()) { @@ -263,12 +298,61 @@ public void tearDown() { table = NonmovableArrays.nullArray(); } } -} -@AutomaticFeature -class RuntimeMethodInfoMemoryFeature implements Feature { - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(RuntimeCodeInfoMemory.class, new RuntimeCodeInfoMemory()); + @Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.") + public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess) { + assert SubstrateDiagnostics.isInProgressByCurrentThread() : "may only be used for printing diagnostics as the table could be freed at any time"; + if (table.isNonNull()) { + for (int i = 0; i < NonmovableArrays.lengthOf(table); i++) { + UntetheredCodeInfo info = NonmovableArrays.getWord(table, i); + if (info.isNonNull()) { + if (info.equal(value)) { + String name = allowJavaHeapAccess ? UntetheredCodeInfoAccess.getName(info) : null; + printIsCodeInfoObject(log, name); + return true; + } + + UnsignedWord codeInfoEnd = ((UnsignedWord) info).add(RuntimeCodeInfoAccess.getSizeOfCodeInfo()); + if (value.aboveOrEqual((UnsignedWord) info) && value.belowThan(codeInfoEnd)) { + String name = allowJavaHeapAccess ? UntetheredCodeInfoAccess.getName(info) : null; + printInsideCodeInfo(log, info, name); + return true; + } + + UnsignedWord codeStart = (UnsignedWord) UntetheredCodeInfoAccess.getCodeStart(info); + UnsignedWord codeEnd = (UnsignedWord) UntetheredCodeInfoAccess.getCodeEnd(info); + if (value.aboveOrEqual(codeStart) && value.belowOrEqual(codeEnd)) { + String name = allowJavaHeapAccess ? UntetheredCodeInfoAccess.getName(info) : null; + printInsideInstructions(log, value, info, codeStart, name); + return true; + } + } + } + } + return false; + } + + @Uninterruptible(reason = "CodeInfo no longer needs to be protected from the GC.", calleeMustBe = false) + private static void printIsCodeInfoObject(Log log, String name) { + log.string("is a CodeInfo object"); + if (name != null) { + log.string(" (").string(name).string(")"); + } + } + + @Uninterruptible(reason = "CodeInfo no longer needs to be protected from the GC.", calleeMustBe = false) + private static void printInsideCodeInfo(Log log, UntetheredCodeInfo info, String name) { + log.string("points inside the CodeInfo object ").zhex(info); + if (name != null) { + log.string(" (").string(name).string(")"); + } + } + + @Uninterruptible(reason = "CodeInfo no longer needs to be protected from the GC.", calleeMustBe = false) + private static void printInsideInstructions(Log log, UnsignedWord value, UntetheredCodeInfo info, UnsignedWord codeStart, String name) { + log.string("is at codeStart+").unsigned(value.subtract(codeStart)).string(" of CodeInfo ").zhex(info); + if (name != null) { + log.string(" (").string(name).string(")"); + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index 831262fe3014..de32926e9376 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -406,7 +406,6 @@ public static void invalidateMethodOfFrame(Pointer sourceSp, SpeculationReason s } } else { if (installedCode == null) { - CodeInfoTable.getRuntimeCodeCache().logTable(); throw VMError.shouldNotReachHere( "Only runtime compiled methods can be invalidated. sp = " + Long.toHexString(sourceSp.rawValue()) + ", returnAddress = " + Long.toHexString(returnAddress.rawValue())); } @@ -735,7 +734,7 @@ private DeoptimizedFrame deoptSourceFrameOperation(CodePointer pc, boolean ignor private static void logDeoptSourceFrameOperation(Pointer sp, DeoptimizedFrame deoptimizedFrame, FrameInfoQueryResult frameInfo) { StringBuilderLog log = new StringBuilderLog(); PointerBase deoptimizedFrameAddress = deoptimizedFrame.getPin().addressOfObject(); - log.string("deoptSourceFrameOperation: DeoptimizedFrame at ").hex(deoptimizedFrameAddress).string(": "); + log.string("deoptSourceFrameOperation: DeoptimizedFrame at ").zhex(deoptimizedFrameAddress).string(": "); printDeoptimizedFrame(log, sp, deoptimizedFrame, frameInfo, true); recentDeoptimizationEvents.append(log.getResult().toCharArray()); } @@ -752,9 +751,9 @@ private static void logDeoptSourceFrameOperation(Pointer sp, DeoptimizedFrame de }; public static void logRecentDeoptimizationEvents(Log log) { - log.string("== [Recent Deoptimizer Events: ").newline(); + log.string("Recent deoptimization events:").indent(true); recentDeoptimizationEvents.foreach(log, deoptEventsConsumer); - log.string("]").newline(); + log.indent(false); } /** @@ -1066,7 +1065,7 @@ private static void printDeoptimizedFrame(Log log, Pointer sp, DeoptimizedFrame if (installedCode != null) { log.string(" name: ").string(installedCode.getName()).newline(); } - log.string(" sp: ").hex(sp).string(" ip: ").hex(deoptimizedFrame.getSourcePC()).newline(); + log.string(" sp: ").zhex(sp).string(" ip: ").zhex(deoptimizedFrame.getSourcePC()).newline(); if (sourceFrameInfo != null) { log.string(" stack trace where execution continues:").newline(); @@ -1085,11 +1084,11 @@ private static void printDeoptimizedFrame(Log log, Pointer sp, DeoptimizedFrame log.string(deoptMethod.format("%H.%n(%p)")); } } else { - log.string("method at ").hex(sourceFrame.getDeoptMethodAddress()); + log.string("method at ").zhex(sourceFrame.getDeoptMethodAddress()); } log.string(" bci "); FrameInfoDecoder.logReadableBci(log, sourceFrame.getEncodedBci()); - log.string(" return address ").hex(targetFrame.returnAddress.returnAddress).newline(); + log.string(" return address ").zhex(targetFrame.returnAddress.returnAddress).newline(); if (printOnlyTopFrames || Options.TraceDeoptimizationDetails.getValue()) { printVirtualFrame(log, targetFrame); @@ -1118,8 +1117,8 @@ private static void printVirtualFrame(Log log, VirtualFrame virtualFrame) { log.string(" bci: "); FrameInfoDecoder.logReadableBci(log, frameInfo.getEncodedBci()); log.string(" deoptMethodOffset: ").signed(frameInfo.getDeoptMethodOffset()); - log.string(" deoptMethod: ").hex(frameInfo.getDeoptMethodAddress()); - log.string(" return address: ").hex(virtualFrame.returnAddress.returnAddress).string(" offset: ").signed(virtualFrame.returnAddress.offset); + log.string(" deoptMethod: ").zhex(frameInfo.getDeoptMethodAddress()); + log.string(" return address: ").zhex(virtualFrame.returnAddress.returnAddress).string(" offset: ").signed(virtualFrame.returnAddress.offset); for (int i = 0; i < frameInfo.getValueInfos().length; i++) { JavaConstant con = virtualFrame.getConstant(i); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java index fe9c54efa999..70d5ec483e42 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java @@ -30,7 +30,6 @@ import java.util.Map; import java.util.function.Predicate; -import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -48,6 +47,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.UnreachableNode; import org.graalvm.compiler.nodes.extended.ForeignCallNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -74,7 +74,6 @@ import com.oracle.svm.core.graal.GraalFeature; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; -import org.graalvm.compiler.nodes.UnreachableNode; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.RestrictHeapAccessCallees; import com.oracle.svm.core.meta.SharedMethod; @@ -85,6 +84,7 @@ import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.thread.ThreadingSupportImpl; +import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.threadlocal.FastThreadLocal; import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.FastThreadLocalInt; @@ -94,7 +94,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; final class StackOverflowCheckImpl implements StackOverflowCheck { - + // The stack boundary for the stack overflow check static final FastThreadLocalWord stackBoundaryTL = FastThreadLocalFactory.createWord().setMaxOffset(FastThreadLocal.FIRST_CACHE_LINE); /** @@ -107,26 +107,19 @@ final class StackOverflowCheckImpl implements StackOverflowCheck { static final int STATE_UNINITIALIZED = 0; static final int STATE_YELLOW_ENABLED = 1; - /* - * Until all of our supported platforms provide the OSSupport, stack overflow checks are not - * mandatory. Eventually this check will go away, see GR-13274. - */ - @Fold - static boolean supportedByOS() { - return ImageSingletons.contains(StackOverflowCheck.OSSupport.class); - } - @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") @Override public void initialize(IsolateThread thread) { - if (!supportedByOS()) { - return; - } - /* * Get the real physical end of the stack. Everything past this point is memory-protected. */ - UnsignedWord stackEnd = ImageSingletons.lookup(StackOverflowCheck.OSSupport.class).lookupStackEnd(); + OSSupport osSupport = ImageSingletons.lookup(StackOverflowCheck.OSSupport.class); + UnsignedWord stackBase = osSupport.lookupStackBase(); + UnsignedWord stackEnd = osSupport.lookupStackEnd(); + + /* Initialize the stack base and the stack end thread locals. */ + VMThreads.StackBase.set(thread, stackBase); + VMThreads.StackEnd.set(thread, stackEnd); /* * Set up our yellow and red zones. That memory is not memory protected, it is a soft limit @@ -146,10 +139,6 @@ public void makeYellowZoneAvailable() { */ ThreadingSupportImpl.pauseRecurringCallback("Recurring callbacks are considered user code and must not run in yellow zone"); - if (!supportedByOS()) { - return; - } - int state = yellowZoneStateTL.get(); VMError.guarantee(state >= STATE_YELLOW_ENABLED, "StackOverflowSupport.disableYellowZone: Illegal state"); @@ -182,10 +171,6 @@ public boolean isYellowZoneAvailable() { public void protectYellowZone() { ThreadingSupportImpl.resumeRecurringCallbackAtNextSafepoint(); - if (!supportedByOS()) { - return; - } - int state = yellowZoneStateTL.get(); VMError.guarantee(state > STATE_YELLOW_ENABLED, "StackOverflowSupport.enableYellowZone: Illegal state"); @@ -198,10 +183,6 @@ public void protectYellowZone() { @Override public int yellowAndRedZoneSize() { - if (!supportedByOS()) { - return 0; - } - return Options.StackYellowZoneSize.getValue() + Options.StackRedZoneSize.getValue(); } @@ -218,7 +199,7 @@ public void disableStackOverflowChecksForFatalError() { * ensures that any future calls to protectYellowZone() do not modify the stack boundary * again. */ - yellowZoneStateTL.set(0xfefefefe); + yellowZoneStateTL.set(0x7EFEFEFE); } } @@ -440,18 +421,11 @@ public void afterRegistration(AfterRegistrationAccess access) { @Override public void registerGraalPhases(Providers providers, SnippetReflectionProvider snippetReflection, Suites suites, boolean hosted) { - if (!StackOverflowCheckImpl.supportedByOS()) { - return; - } - suites.getHighTier().prependPhase(new InsertStackOverflowCheckPhase()); } @Override public void registerForeignCalls(RuntimeConfiguration runtimeConfig, Providers providers, SnippetReflectionProvider snippetReflection, SubstrateForeignCallsProvider foreignCalls, boolean hosted) { - if (!StackOverflowCheckImpl.supportedByOS()) { - return; - } foreignCalls.register(providers, StackOverflowCheckSnippets.FOREIGN_CALLS); } @@ -459,10 +433,6 @@ public void registerForeignCalls(RuntimeConfiguration runtimeConfig, Providers p @SuppressWarnings("unused") public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Iterable factories, Providers providers, SnippetReflectionProvider snippetReflection, Map, NodeLoweringProvider> lowerings, boolean hosted) { - if (!StackOverflowCheckImpl.supportedByOS()) { - return; - } - Predicate mustNotAllocatePredicate = null; if (hosted) { mustNotAllocatePredicate = method -> ImageSingletons.lookup(RestrictHeapAccessCallees.class).mustNotAllocate(method); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java index 71f6a10733bb..cfd7218f1839 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java @@ -35,10 +35,12 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.PredefinedClassesSupport; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.os.CommittedMemoryProvider; import jdk.vm.ci.meta.MetaAccessProvider; @@ -166,7 +168,11 @@ public List> getLoadedClasses() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public abstract boolean isInImageHeap(Object object); - /** Returns true if the object at the given address is located in the image heap. */ + /** + * Returns true if the object at the given address is located in the image heap. Depending on + * the used GC, this method may only work reliably for pointers that point to the start of an + * object. + */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public abstract boolean isInImageHeap(Pointer objectPtr); @@ -187,4 +193,10 @@ public List> getLoadedClasses() { * May return {@code null}. */ public abstract Reference getAndClearReferencePendingList(); + + /** + * If the passed value is within the Java heap, this method prints some information about that + * value and returns true. Otherwise, the method returns false. + */ + public abstract boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java index 56cb5a73cc3c..56c5aaf511d8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java @@ -70,6 +70,18 @@ public static DynamicHub readDynamicHubFromObject(Object o) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public abstract DynamicHub readDynamicHubFromPointer(Pointer ptr); + public abstract Pointer readPotentialDynamicHubFromPointer(Pointer ptr); + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public abstract void initializeHeaderOfNewObject(Pointer objectPointer, Word objectHeader); + + public boolean pointsToObjectHeader(Pointer ptr) { + Pointer potentialDynamicHub = readPotentialDynamicHubFromPointer(ptr); + if (Heap.getHeap().isInImageHeap(potentialDynamicHub)) { + Pointer potentialHubOfDynamicHub = readPotentialDynamicHubFromPointer(potentialDynamicHub); + return potentialHubOfDynamicHub.equal(Word.objectToUntrackedPointer(DynamicHub.class)); + } + return false; + } + } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java index ad493ef178b3..747476cecd70 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/RuntimeCodeCacheCleaner.java @@ -36,6 +36,7 @@ import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; import com.oracle.svm.core.code.RuntimeCodeInfoAccess; +import com.oracle.svm.core.code.RuntimeCodeInfoHistory; import com.oracle.svm.core.code.RuntimeCodeInfoMemory; /** @@ -81,6 +82,7 @@ public boolean visitCode(T codeInfo) { private static void freeMemory(CodeInfo codeInfo) { boolean removed = RuntimeCodeInfoMemory.singleton().removeDuringGC(codeInfo); assert removed : "must have been present"; + RuntimeCodeInfoHistory.singleton().logFree(codeInfo); RuntimeCodeInfoAccess.releaseMethodInfoMemory(codeInfo, false); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java index 3e64f2965d4a..978e668a922a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java @@ -203,6 +203,7 @@ public static UnsignedWord getArraySize(int encoding, int length) { return getArrayElementOffset(encoding, length).add(alignmentMask).and(~alignmentMask); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static UnsignedWord getSizeFromObject(Object obj) { int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding(); if (isArray(encoding)) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleHashtable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleHashtable.java index 1f7c4f164467..90b336f40c74 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleHashtable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleHashtable.java @@ -44,14 +44,14 @@ public abstract class UninterruptibleHashtable private int size; @Platforms(Platform.HOSTED_ONLY.class) - public UninterruptibleHashtable() { - this(DEFAULT_TABLE_LENGTH); + public UninterruptibleHashtable(String name) { + this(name, DEFAULT_TABLE_LENGTH); } @Platforms(Platform.HOSTED_ONLY.class) - public UninterruptibleHashtable(int primeLength) { + public UninterruptibleHashtable(String name, int primeLength) { this.table = createTable(primeLength); - this.mutex = new VMMutex(); + this.mutex = new VMMutex(name); this.size = 0; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/SingleThreadedVMLockSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/SingleThreadedVMLockSupport.java index 66303d25e6ba..d1f7be48987e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/SingleThreadedVMLockSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/SingleThreadedVMLockSupport.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.locks; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; @@ -37,8 +38,16 @@ * Support of {@link VMMutex} and {@link VMCondition} in single-threaded environments. No real * locking is necessary. */ -final class SingleThreadedVMLockSupport { - // Empty class to have the same name as the source file. +final class SingleThreadedVMLockSupport extends VMLockSupport { + @Override + public VMMutex[] getMutexes() { + return null; + } + + @Override + public VMCondition[] getConditions() { + return null; + } } @AutomaticFeature @@ -47,7 +56,7 @@ final class SingleThreadedVMLockFeature implements Feature { private final ClassInstanceReplacer mutexReplacer = new ClassInstanceReplacer(VMMutex.class) { @Override protected VMMutex createReplacement(VMMutex source) { - return new SingleThreadedVMMutex(); + return new SingleThreadedVMMutex(source.getName()); } }; @@ -65,6 +74,7 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void duringSetup(DuringSetupAccess access) { + ImageSingletons.add(VMLockSupport.class, new SingleThreadedVMLockSupport()); access.registerObjectReplacer(mutexReplacer); access.registerObjectReplacer(conditionReplacer); } @@ -79,7 +89,8 @@ public void beforeCompilation(BeforeCompilationAccess access) { final class SingleThreadedVMMutex extends VMMutex { @Platforms(Platform.HOSTED_ONLY.class) - protected SingleThreadedVMMutex() { + protected SingleThreadedVMMutex(String name) { + super(name); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMLockSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMLockSupport.java new file mode 100644 index 000000000000..ea8700d81ce5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMLockSupport.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.locks; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.IsolateThread; + +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; +import com.oracle.svm.core.annotate.RestrictHeapAccess; +import com.oracle.svm.core.log.Log; + +public abstract class VMLockSupport { + /** + * Returns an array that contains all {@link VMMutex} objects that are present in the image or + * null if that information is not available. + */ + public abstract VMMutex[] getMutexes(); + + /** + * Returns an array that contains all {@link VMCondition} objects that are present in the image + * or null if that information is not available. + */ + public abstract VMCondition[] getConditions(); + + public static class DumpVMMutexes extends DiagnosticThunk { + @Override + public int maxInvocations() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, int invocationCount) { + log.string("VM mutexes:").indent(true); + + VMLockSupport support = null; + if (ImageSingletons.contains(VMLockSupport.class)) { + support = ImageSingletons.lookup(VMLockSupport.class); + } + + if (support == null || support.getMutexes() == null) { + log.string("No mutex information is available."); + } else { + VMMutex[] mutexes = support.getMutexes(); + for (int i = 0; i < mutexes.length; i++) { + VMMutex mutex = mutexes[i]; + IsolateThread owner = mutex.owner; + log.string("mutex \"").string(mutex.getName()).string("\" "); + if (owner.isNull()) { + log.string("is unlocked."); + } else { + log.string("is locked by "); + if (owner.equal(VMMutex.UNSPECIFIED_OWNER)) { + log.string("an unspecified thread."); + } else { + log.string("thread ").zhex(owner); + } + } + log.newline(); + } + } + + log.indent(false); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMMutex.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMMutex.java index 180e95d2d186..5a4789859ccb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMMutex.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMMutex.java @@ -50,12 +50,23 @@ * with platform-specific implementations. */ public class VMMutex { - private static final UnsignedWord UNSPECIFIED_OWNER = WordFactory.unsigned(-1); + static final UnsignedWord UNSPECIFIED_OWNER = WordFactory.unsigned(-1); - private IsolateThread owner; + private final String name; + IsolateThread owner; @Platforms(Platform.HOSTED_ONLY.class) public VMMutex() { + this.name = "unspecified"; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public VMMutex(String name) { + this.name = name; + } + + public String getName() { + return name; } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java index 2ad4e2c60d74..ec08a2f1791c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java @@ -271,6 +271,13 @@ public final Log string(byte[] value) { @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, mayBeInlined = true, reason = "Must not allocate when logging.") public abstract Log hex(long value); + /** + * Prints the value, treated as an unsigned value, in hexadecimal format zero filled to + * 16-digits. + */ + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, mayBeInlined = true, reason = "Must not allocate when logging.") + public abstract Log zhex(WordBase value); + /** * Prints the value, treated as an unsigned value, in hexadecimal format zero filled to * 16-digits. @@ -549,6 +556,11 @@ public Log autoflush(boolean onOrOff) { return this; } + @Override + public Log zhex(WordBase value) { + return this; + } + @Override public Log zhex(long value) { return this; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java index eff09c2e6f64..5fd54b5afcc9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java @@ -382,7 +382,7 @@ public Log object(Object value) { } else { string(value.getClass().getName()); string("@"); - hex(Word.objectToUntrackedPointer(value)); + zhex(Word.objectToUntrackedPointer(value)); } return this; } @@ -442,9 +442,16 @@ private void rawString(char[] value) { rawBytes(value, 0, value.length); } + @Override + public Log zhex(WordBase value) { + zhex(value.rawValue()); + return this; + } + @NeverInline("Logging is always slow-path code") @Override public Log zhex(long value) { + string("0x"); int zeros = Long.numberOfLeadingZeros(value); int hexZeros = zeros / 4; for (int i = 0; i < hexZeros; i += 1) { @@ -457,6 +464,7 @@ public Log zhex(long value) { } private Log zhex(int value, int wordSizeInBytes) { + string("0x"); int zeros = Integer.numberOfLeadingZeros(value) - 32 + (wordSizeInBytes * 8); int hexZeros = zeros / 4; for (int i = 0; i < hexZeros; i += 1) { @@ -513,7 +521,7 @@ public Log hexdump(PointerBase from, int wordSize, int numWords) { zhex(base.readLong(offset)); break; } - if ((offset + sanitizedWordsize) % 16 == 0) { + if ((offset + sanitizedWordsize) % 16 == 0 && (offset + sanitizedWordsize) < sanitizedWordsize * numWords) { newline(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java index ca5e2559aeb2..8867464e8ef4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java @@ -28,6 +28,7 @@ import java.lang.reflect.GenericSignatureFormatError; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.jdk.StackTraceUtils; @@ -128,7 +129,7 @@ public static void deactivateImplicitExceptionsAreFatal() { } private static void vmErrorIfImplicitExceptionsAreFatal() { - if (implicitExceptionsAreFatal.get() > 0 || ExceptionUnwind.exceptionsAreFatal()) { + if ((implicitExceptionsAreFatal.get() > 0 || ExceptionUnwind.exceptionsAreFatal()) && !SubstrateDiagnostics.isInProgressByCurrentThread()) { throw VMError.shouldNotReachHere("Implicit exception thrown in code where such exceptions are fatal errors"); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java index cac98ff4ec9e..2072cc9b5f56 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java @@ -29,6 +29,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.option.HostedOptionKey; @@ -87,7 +88,14 @@ class Options { * platforms use this direction. */ interface OSSupport { - @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") + /** The highest address of the stack or zero if not supported. */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + default UnsignedWord lookupStackBase() { + return WordFactory.zero(); + } + + /** The lowest address of the stack. */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) UnsignedWord lookupStackEnd(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java index ff0c6559df46..c5e77c20eab2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperationControl.java @@ -46,6 +46,7 @@ import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.RestrictHeapAccess.Access; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.locks.VMCondition; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; @@ -192,7 +193,7 @@ public static boolean mayExecuteVmOperations() { } } - public static void logRecentEvents(Log log) { + public static void printCurrentVMOperation(Log log, boolean allowJavaHeapAccess) { /* * All reads in this method are racy as the currently executed VM operation could finish and * a different VM operation could start. So, the read data is not necessarily consistent. @@ -201,15 +202,19 @@ public static void logRecentEvents(Log log) { VMOperation op = control.inProgress.operation; if (op == null) { log.string("No VMOperation in progress").newline(); + } else if (allowJavaHeapAccess) { + log.string("VMOperation in progress: ").string(op.getName()).indent(true); + log.string("Safepoint: ").bool(op.getCausesSafepoint()).newline(); + log.string("QueuingThread: ").zhex(control.inProgress.queueingThread.rawValue()).newline(); + log.string("ExecutingThread: ").zhex(control.inProgress.executingThread.rawValue()).newline(); + log.redent(false); } else { - log.string("VMOperation in progress: ").string(op.getName()).newline(); - log.string(" safepoint: ").bool(op.getCausesSafepoint()).newline(); - log.string(" queuingThread: ").zhex(control.inProgress.queueingThread.rawValue()).newline(); - log.string(" executingThread: ").zhex(control.inProgress.executingThread.rawValue()).newline(); + log.string("VMOperation in progress: ").zhex(Word.objectToUntrackedPointer(op)).newline(); } - log.newline(); + } - control.history.print(log); + public static void printRecentEvents(Log log, boolean allowJavaHeapAccess) { + get().history.print(log, allowJavaHeapAccess); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -440,7 +445,7 @@ private static final class WorkQueues { this.nativeSafepointOperations = new NativeVMOperationQueue(prefix + "NativeSafepointOperations"); this.javaNonSafepointOperations = new JavaVMOperationQueue(prefix + "JavaNonSafepointOperations"); this.javaSafepointOperations = new JavaVMOperationQueue(prefix + "JavaSafepointOperations"); - this.mutex = createMutex(needsLocking); + this.mutex = createMutex(prefix + "VMOperationControlWorkQueue", needsLocking); this.operationQueued = createCondition(); this.operationFinished = createCondition(); } @@ -670,9 +675,9 @@ private void assertIsLocked() { } @Platforms(value = Platform.HOSTED_ONLY.class) - private static VMMutex createMutex(boolean needsLocking) { + private static VMMutex createMutex(String mutexName, boolean needsLocking) { if (needsLocking) { - return new VMMutex(); + return new VMMutex(mutexName); } return null; } @@ -866,7 +871,8 @@ public IsolateThread getExecutingThread() { */ private static class VMOpHistory { private final RingBuffer history; - private static final RingBuffer.Consumer PRINT_ENTRY = VMOpHistory::printEntry; + private static final RingBuffer.Consumer PRINT_WITH_JAVA_HEAP_DATA = VMOpHistory::printEntryWithJavaHeapData; + private static final RingBuffer.Consumer PRINT_WITHOUT_JAVA_HEAP_DATA = VMOpHistory::printEntryWithoutJavaHeapData; @Platforms(Platform.HOSTED_ONLY.class) VMOpHistory() { @@ -875,6 +881,7 @@ private static class VMOpHistory { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void add(VMOpStatus status, VMOperation operation, IsolateThread queueingThread, IsolateThread executingThread) { + assert Heap.getHeap().isInImageHeap(status); VMOpStatusChange entry = history.next(); entry.timestamp = System.currentTimeMillis(); entry.status = status; @@ -884,15 +891,23 @@ public void add(VMOpStatus status, VMOperation operation, IsolateThread queueing entry.executingThread = executingThread; } - public void print(Log log) { + public void print(Log log, boolean allowJavaHeapAccess) { log.string("The ").signed(history.size()).string(" most recent VM operation status changes (oldest first):").indent(true); - history.foreach(log, PRINT_ENTRY); + history.foreach(log, allowJavaHeapAccess ? PRINT_WITH_JAVA_HEAP_DATA : PRINT_WITHOUT_JAVA_HEAP_DATA); log.indent(false); } - private static void printEntry(Object context, VMOpStatusChange entry) { + private static void printEntryWithJavaHeapData(Object context, VMOpStatusChange entry) { + printEntry(context, entry, true); + } + + private static void printEntryWithoutJavaHeapData(Object context, VMOpStatusChange entry) { + printEntry(context, entry, false); + } + + private static void printEntry(Object context, VMOpStatusChange entry, boolean allowJavaHeapAccess) { Log log = (Log) context; - entry.print(log); + entry.print(log, allowJavaHeapAccess); } } @@ -919,10 +934,14 @@ private static class VMOpStatusChange { VMOpStatusChange() { } - void print(Log log) { + void print(Log log, boolean allowJavaHeapAccess) { VMOpStatus localStatus = status; if (localStatus != null) { - log.unsigned(timestamp).string(" - ").string(localStatus.name()).string(" ").string(operation).string(" (safepoint: ").bool(causesSafepoint).string(", queueingThread: ") + log.unsigned(timestamp).string(" - ").string(localStatus.name()); + if (allowJavaHeapAccess) { + log.string(" ").string(operation); + } + log.string(" (safepoint: ").bool(causesSafepoint).string(", queueingThread: ") .zhex(queueingThread.rawValue()).string(", executingThread: ").zhex(executingThread.rawValue()).string(")").newline(); } } 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 d79be8c0208f..c2a16375a720 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 @@ -43,6 +43,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; @@ -53,10 +54,12 @@ import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicWord; import com.oracle.svm.core.locks.VMCondition; import com.oracle.svm.core.locks.VMMutex; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.threadlocal.FastThreadLocal; import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.FastThreadLocalInt; import com.oracle.svm.core.threadlocal.FastThreadLocalWord; +import com.oracle.svm.core.threadlocal.VMThreadLocalMTSupport; import com.oracle.svm.core.util.UnsignedUtils; import com.oracle.svm.core.util.VMError; @@ -109,7 +112,7 @@ public static VMThreads singleton() { * still holds that mutex. * */ - protected static final VMMutex THREAD_MUTEX = new VMMutex(); + protected static final VMMutex THREAD_MUTEX = new VMMutex("thread"); /** * A condition variable for waiting for and notifying on changes to the {@link IsolateThread} @@ -139,6 +142,13 @@ public static VMThreads singleton() { private static final FastThreadLocalWord OSThreadIdTL = FastThreadLocalFactory.createWord(); protected static final FastThreadLocalWord OSThreadHandleTL = FastThreadLocalFactory.createWord(); public static final FastThreadLocalWord IsolateTL = FastThreadLocalFactory.createWord(); + /** The highest stack address. */ + public static final FastThreadLocalWord StackBase = FastThreadLocalFactory.createWord(); + /** + * The lowest stack address. Note that this value does not necessarily match the value that is + * used for the stack overflow check. + */ + public static final FastThreadLocalWord StackEnd = FastThreadLocalFactory.createWord(); private static final int STATE_UNINITIALIZED = 1; private static final int STATE_INITIALIZING = 2; @@ -563,6 +573,32 @@ public static boolean ownsThreadMutex() { return THREAD_MUTEX.isOwner(); } + public static boolean printLocationInfo(Log log, UnsignedWord value) { + for (IsolateThread thread = firstThreadUnsafe(); thread.isNonNull(); thread = nextThread(thread)) { + if (thread.equal(value)) { + log.string("is a thread"); + return true; + } + + UnsignedWord stackBase = StackBase.get(thread); + UnsignedWord stackEnd = StackEnd.get(thread); + if (value.belowOrEqual(stackBase) && value.aboveOrEqual(stackEnd)) { + log.string("points into the stack for thread ").zhex(thread); + return true; + } + + if (SubstrateOptions.MultiThreaded.getValue()) { + int sizeOfThreadLocals = ImageSingletons.lookup(VMThreadLocalMTSupport.class).vmThreadSize; + UnsignedWord endOfThreadLocals = ((UnsignedWord) thread).add(sizeOfThreadLocals); + if (value.aboveOrEqual((UnsignedWord) thread) && value.belowThan(endOfThreadLocals)) { + log.string("points into the thread locals for thread ").zhex(thread); + return true; + } + } + } + return false; + } + /* * Access to platform-specific implementations. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalInfos.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalInfos.java index a27102187f5a..fc5404ee689a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalInfos.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalInfos.java @@ -29,11 +29,11 @@ import org.graalvm.compiler.word.ObjectAccess; import org.graalvm.compiler.word.Word; -import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.Pointer; import org.graalvm.word.WordBase; import org.graalvm.word.WordFactory; @@ -41,6 +41,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.log.Log; public class VMThreadLocalInfos { @@ -59,30 +60,35 @@ public static boolean setInfos(Collection infos) { } } - public static void dumpToLog(Log log, IsolateThread thread) { + public static void dumpToLog(Log log, IsolateThread thread, boolean allowJavaHeapAccess) { for (VMThreadLocalInfo info : ImageSingletons.lookup(VMThreadLocalInfos.class).infos) { log.signed(info.offset).string(" (").signed(info.sizeInBytes).string(" bytes): ").string(info.name).string(" = "); if (info.threadLocalClass == FastThreadLocalInt.class) { int value = primitiveData(thread).readInt(WordFactory.signed(info.offset)); - log.string("(int) ").signed(value).string(" ").zhex(value); + log.string("(int) ").signed(value).string(" (").zhex(value).string(")"); } else if (info.threadLocalClass == FastThreadLocalLong.class) { long value = primitiveData(thread).readLong(WordFactory.signed(info.offset)); - log.string("(long) ").signed(value).string(" ").zhex(value); + log.string("(long) ").signed(value).string(" (").zhex(value).string(")"); } else if (info.threadLocalClass == FastThreadLocalWord.class) { WordBase value = primitiveData(thread).readWord(WordFactory.signed(info.offset)); - log.string("(Word) ").signed(value).string(" ").zhex(value.rawValue()); + log.string("(Word) ").signed(value).string(" (").zhex(value.rawValue()).string(")"); } else if (info.threadLocalClass == FastThreadLocalObject.class) { - Object value = ObjectAccess.readObject(objectData(thread), WordFactory.signed(info.offset)); - log.string("(Object) "); - if (value == null) { - log.string("null"); + if (allowJavaHeapAccess) { + Object value = ObjectAccess.readObject(objectData(thread), WordFactory.signed(info.offset)); + log.string("(Object) "); + if (value == null) { + log.string("null"); + } else { + log.string(value.getClass().getName()).string(" (").zhex(Word.objectToUntrackedPointer(value).rawValue()).string(")"); + } } else { - log.string(value.getClass().getName()).string(" ").zhex(Word.objectToUntrackedPointer(value).rawValue()); + Word value = ReferenceAccess.singleton().readObjectAsUntrackedPointer(Word.objectToUntrackedPointer(objectData(thread)).add(info.offset), true); + log.string("(Object) ").zhex(value); } } else if (info.threadLocalClass == FastThreadLocalBytes.class) { log.string("(bytes) ").indent(true); log.hexdump(primitiveData(thread).add(WordFactory.signed(info.offset)), 8, info.sizeInBytes / 8); - log.indent(false); + log.redent(false); } else { log.string("unknown class ").string(info.threadLocalClass.getName()); } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrRecorderThread.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrRecorderThread.java index b48106c54815..179fa8e0782d 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrRecorderThread.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrRecorderThread.java @@ -51,7 +51,7 @@ public JfrRecorderThread(JfrGlobalMemory globalMemory, JfrUnlockedChunkWriter un super("JFR Recorder Thread"); this.globalMemory = globalMemory; this.unlockedChunkWriter = unlockedChunkWriter; - this.mutex = new VMMutex(); + this.mutex = new VMMutex("jfrRecorder"); this.condition = new VMCondition(mutex); setDaemon(true); } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrSymbolRepository.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrSymbolRepository.java index 1c13be2f4341..a1f82973ebc7 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrSymbolRepository.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrSymbolRepository.java @@ -162,6 +162,11 @@ private interface JfrSymbol extends UninterruptibleEntry { } private static class JfrSymbolHashtable extends UninterruptibleHashtable { + @Platforms(Platform.HOSTED_ONLY.class) + JfrSymbolHashtable() { + super("jfrSymbolHashtable"); + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override protected JfrSymbol[] createTable(int size) { diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTargetInstalledCode.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTargetInstalledCode.java index af42c977035f..f5ef0afbbe1c 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTargetInstalledCode.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTargetInstalledCode.java @@ -34,6 +34,7 @@ import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.code.RuntimeCodeInfoHistory; import com.oracle.svm.core.code.UntetheredCodeInfo; import com.oracle.svm.core.code.UntetheredCodeInfoAccess; import com.oracle.svm.core.deopt.SubstrateInstalledCode; @@ -147,12 +148,18 @@ private void invalidateWithoutDeoptimization0() { Object tether = CodeInfoAccess.acquireTether(untetheredInfo); try { // Indicates to GC that the code can be freed once there are no activations left CodeInfo codeInfo = CodeInfoAccess.convert(untetheredInfo, tether); - CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_NON_ENTRANT); + invalidateWithoutDeoptimization1(codeInfo); } finally { CodeInfoAccess.releaseTether(untetheredInfo, tether); } } + @Uninterruptible(reason = "Call interruptible code now that the CodeInfo is tethered.", calleeMustBe = false) + private static void invalidateWithoutDeoptimization1(CodeInfo codeInfo) { + CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_NON_ENTRANT); + RuntimeCodeInfoHistory.singleton().logMakeNonEntrant(codeInfo); + } + static Object doInvoke(SubstrateOptimizedCallTarget callTarget, Object[] args) { SubstrateOptimizedCallTarget.safepointBarrier(); /*