From cd0de3537c3530908701e622eae11641c8e9db6d Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Tue, 31 Aug 2021 11:24:01 +0200 Subject: [PATCH 1/6] Avoid unsafe operations when no segfault handler is present. --- .../core/genscavenge/AlignedHeapChunk.java | 1 + .../genscavenge/GreyToBlackObjectVisitor.java | 2 +- .../oracle/svm/core/genscavenge/HeapImpl.java | 93 +++--- .../core/genscavenge/UnalignedHeapChunk.java | 1 + .../svm/core/genscavenge/YoungGeneration.java | 2 + .../amd64/AMD64CalleeSavedRegisters.java | 38 +-- .../posix/PosixSubstrateSegfaultHandler.java | 2 +- .../core/windows/WindowsRegisterDumper.java | 42 +-- .../WindowsSubstrateSegfaultHandler.java | 2 +- .../oracle/svm/core/CalleeSavedRegisters.java | 2 +- .../svm/core/InvalidVTableEntryHandler.java | 2 +- .../com/oracle/svm/core/RegisterDumper.java | 6 +- .../oracle/svm/core/SubstrateDiagnostics.java | 310 +++++++++++++----- .../oracle/svm/core/SubstrateGCOptions.java | 6 +- .../com/oracle/svm/core/SubstrateOptions.java | 3 + .../svm/core/SubstrateSegfaultHandler.java | 21 +- .../com/oracle/svm/core/SubstrateUtil.java | 2 +- .../svm/core/code/RuntimeCodeInfoMemory.java | 68 ++-- .../src/com/oracle/svm/core/heap/Heap.java | 2 +- .../svm/core/jdk/VMErrorSubstitutions.java | 2 +- .../oracle/svm/core/locks/VMLockSupport.java | 2 +- .../svm/core/thread/VMOperationControl.java | 5 +- .../com/oracle/svm/core/thread/VMThreads.java | 18 +- .../core/threadlocal/VMThreadLocalInfos.java | 5 +- 24 files changed, 413 insertions(+), 224 deletions(-) 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 b84544c24e01..5432cfc3b8b7 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 @@ -93,6 +93,7 @@ public static void reset(AlignedHeader chunk) { HeapChunk.initialize(chunk, AlignedHeapChunk.getObjectsStart(chunk), HeapChunk.getEndOffset(chunk)); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static Pointer getObjectsStart(AlignedHeader that) { return HeapChunk.asPointer(that).add(getObjectsStartOffset()); } 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 a233a2b182e4..4fd2188137da 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 @@ -132,7 +132,7 @@ public void noteObject(Object o) { } @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } 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 ffeff4cc4233..3add5293446e 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 @@ -33,6 +33,7 @@ import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.nodes.gc.BarrierSet; import org.graalvm.compiler.word.Word; +import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; @@ -42,6 +43,7 @@ import com.oracle.svm.core.MemoryWalker; import com.oracle.svm.core.SubstrateDiagnostics; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.NeverInline; @@ -610,7 +612,7 @@ public Reference getAndClearReferencePendingList() { } @Override - public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess) { + public boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { if (SubstrateOptions.SpawnIsolates.getValue()) { Pointer heapBase = KnownIntrinsics.heapBase(); if (value.equal(heapBase)) { @@ -623,8 +625,8 @@ public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaH } Pointer ptr = (Pointer) value; - if (printLocationInfo(log, ptr)) { - if (allowJavaHeapAccess && objectHeaderImpl.pointsToObjectHeader(ptr)) { + if (printLocationInfo(log, ptr, diagnosticLevel)) { + if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel) && objectHeaderImpl.pointsToObjectHeader(ptr)) { DynamicHub hub = objectHeaderImpl.readDynamicHubFromPointer(ptr); log.indent(true); log.string("hub=").string(hub.getName()); @@ -645,7 +647,7 @@ static Pointer getImageHeapStart() { } } - private boolean printLocationInfo(Log log, Pointer ptr) { + private boolean printLocationInfo(Log log, Pointer ptr, int diagnosticLevel) { if (imageHeapInfo.isInReadOnlyPrimitivePartition(ptr)) { log.string("points into the image heap (read-only primitives)"); return true; @@ -670,21 +672,34 @@ private boolean printLocationInfo(Log log, Pointer ptr) { } 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"); + } else if (printTlabInfo(log, ptr, CurrentIsolate.getCurrentThread())) { return true; - } else if (isInOldGen(ptr)) { - log.string("points into the old generation"); - return true; - } else { + } + + if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel)) { + // Accessing spaces and chunks is safe if we prevent a GC. + 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; + } + } + + if (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + // If we are not at a safepoint, then it is unsafe to access thread locals of another + // thread as the IsolateThread could be freed at any time. return printTlabInfo(log, ptr); } + return false; } boolean isInHeap(Pointer ptr) { return isInImageHeap(ptr) || isInYoungGen(ptr) || isInOldGen(ptr); } + @Uninterruptible(reason = "Prevent that chunks are freed.") private boolean isInYoungGen(Pointer ptr) { if (findPointerInSpace(youngGeneration.getEden(), ptr)) { return true; @@ -701,10 +716,12 @@ private boolean isInYoungGen(Pointer ptr) { return false; } + @Uninterruptible(reason = "Prevent that chunks are freed.") private boolean isInOldGen(Pointer ptr) { return findPointerInSpace(oldGeneration.getFromSpace(), ptr) || findPointerInSpace(oldGeneration.getToSpace(), ptr); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) private static boolean findPointerInSpace(Space space, Pointer p) { AlignedHeapChunk.AlignedHeader aChunk = space.getFirstAlignedHeapChunk(); while (aChunk.isNonNull()) { @@ -726,37 +743,39 @@ private static boolean findPointerInSpace(Space space, Pointer p) { 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); + if (printTlabInfo(log, ptr, thread)) { + return true; } + } + return false; + } - 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; - } - uChunk = HeapChunk.getNext(uChunk); + private static boolean printTlabInfo(Log log, Pointer ptr, IsolateThread 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; } + uChunk = HeapChunk.getNext(uChunk); } + return false; } @@ -768,7 +787,7 @@ private static Descriptor getTlabUnsafe(IsolateThread thread) { private static class DumpHeapSettingsAndStatistics extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @@ -794,7 +813,7 @@ public void printDiagnostics(Log log, int invocationCount) { private static class DumpChunkInformation extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } 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 1d6b7ec92734..2de8d7887e4b 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 @@ -98,6 +98,7 @@ public static void initialize(UnalignedHeader chunk, UnsignedWord chunkSize) { HeapChunk.initialize(chunk, UnalignedHeapChunk.getObjectStart(chunk), chunkSize); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static Pointer getObjectStart(UnalignedHeader that) { return HeapChunk.asPointer(that).add(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 7d07ab5ae45f..5bb3ac7b2acf 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 @@ -118,11 +118,13 @@ Space getEden() { return eden; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) Space getSurvivorToSpaceAt(int index) { assert index >= 0 && index < maxSurvivorSpaces : "Survivor index should be between 0 and NumberOfSurvivorSpaces"; return survivorToSpaces[index]; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) Space getSurvivorFromSpaceAt(int index) { assert index >= 0 && index < maxSurvivorSpaces : "Survivor index should be between 0 and NumberOfSurvivorSpaces"; return survivorFromSpaces[index]; diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java index c95b6636bcee..ea160145b194 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java @@ -167,36 +167,36 @@ private AMD64Address calleeSaveAddress(AMD64MacroAssembler asm, int frameSize, R } @Override - public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess) { + public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { log.string("Callee saved registers (sp=").zhex(callerSP).string(")").indent(true); /* * The loop to print all registers is manually unrolled so that the register order is * defined, and also so that the lookup of the "offset in frame" can be constant folded at * image build time using a @Fold method. */ - dumpReg(log, "RAX ", callerSP, offsetInFrameOrNull(AMD64.rax), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RBX ", callerSP, offsetInFrameOrNull(AMD64.rbx), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RCX ", callerSP, offsetInFrameOrNull(AMD64.rcx), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RDX ", callerSP, offsetInFrameOrNull(AMD64.rdx), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RBP ", callerSP, offsetInFrameOrNull(AMD64.rbp), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RSI ", callerSP, offsetInFrameOrNull(AMD64.rsi), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RDI ", callerSP, offsetInFrameOrNull(AMD64.rdi), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "RSP ", callerSP, offsetInFrameOrNull(AMD64.rsp), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R8 ", callerSP, offsetInFrameOrNull(AMD64.r8), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R9 ", callerSP, offsetInFrameOrNull(AMD64.r9), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R10 ", callerSP, offsetInFrameOrNull(AMD64.r10), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R11 ", callerSP, offsetInFrameOrNull(AMD64.r11), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R12 ", callerSP, offsetInFrameOrNull(AMD64.r12), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R13 ", callerSP, offsetInFrameOrNull(AMD64.r13), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R14 ", callerSP, offsetInFrameOrNull(AMD64.r14), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "R15 ", callerSP, offsetInFrameOrNull(AMD64.r15), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "RAX ", callerSP, offsetInFrameOrNull(AMD64.rax), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RBX ", callerSP, offsetInFrameOrNull(AMD64.rbx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RCX ", callerSP, offsetInFrameOrNull(AMD64.rcx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RDX ", callerSP, offsetInFrameOrNull(AMD64.rdx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RBP ", callerSP, offsetInFrameOrNull(AMD64.rbp), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RSI ", callerSP, offsetInFrameOrNull(AMD64.rsi), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RDI ", callerSP, offsetInFrameOrNull(AMD64.rdi), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RSP ", callerSP, offsetInFrameOrNull(AMD64.rsp), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R8 ", callerSP, offsetInFrameOrNull(AMD64.r8), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R9 ", callerSP, offsetInFrameOrNull(AMD64.r9), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R10 ", callerSP, offsetInFrameOrNull(AMD64.r10), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R11 ", callerSP, offsetInFrameOrNull(AMD64.r11), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R12 ", callerSP, offsetInFrameOrNull(AMD64.r12), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R13 ", callerSP, offsetInFrameOrNull(AMD64.r13), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R14 ", callerSP, offsetInFrameOrNull(AMD64.r14), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R15 ", callerSP, offsetInFrameOrNull(AMD64.r15), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); log.indent(false); } - private static void dumpReg(Log log, String label, Pointer callerSP, int offsetInFrameOrNull, boolean printLocationInfo, boolean allowJavaHeapAccess) { + private static void dumpReg(Log log, String label, Pointer callerSP, int offsetInFrameOrNull, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { if (offsetInFrameOrNull != 0) { long value = callerSP.readLong(offsetInFrameOrNull); - RegisterDumper.dumpReg(log, label, value, printLocationInfo, allowJavaHeapAccess); + RegisterDumper.dumpReg(log, label, value, printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); } } 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 5eb65fbb5ddd..14edd6ac3147 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 @@ -95,7 +95,7 @@ protected void printSignalInfo(Log log, PointerBase signalInfo) { "dispatch", int.class, siginfo_t.class, ucontext_t.class); @Override - protected void install() { + protected void installInternal() { int structSigActionSize = SizeOf.get(sigaction.class); sigaction structSigAction = StackValue.get(structSigActionSize); LibC.memset(structSigAction, WordFactory.signed(0), WordFactory.unsigned(structSigActionSize)); 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 cc24e53bf67d..afeb1ad29977 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, boolean printLocationInfo, boolean allowJavaHeapAccess) { - dumpRegisters(log, (CONTEXT) context, printLocationInfo, allowJavaHeapAccess); + public void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { + dumpRegisters(log, (CONTEXT) context, printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); } - 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); + private static void dumpRegisters(Log log, CONTEXT context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { + dumpReg(log, "RAX ", context.Rax(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RBX ", context.Rbx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RCX ", context.Rcx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RDX ", context.Rdx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RBP ", context.Rbp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RSI ", context.Rsi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RDI ", context.Rdi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RSP ", context.Rsp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R8 ", context.R8(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R9 ", context.R9(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R10 ", context.R10(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R11 ", context.R11(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R12 ", context.R12(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R13 ", context.R13(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R14 ", context.R14(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "R15 ", context.R15(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "EFL ", context.EFlags(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RIP ", context.Rip(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); } @Override 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 b885e251fc6c..1d9788b83e81 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 @@ -64,7 +64,7 @@ class WindowsSubstrateSegfaultHandler extends SubstrateSegfaultHandler { private static final int EX_EXECUTE = 8; @Override - protected void install() { + protected void installInternal() { /* * Normally we would use SEH (Structured Exception Handling) for this. However, in order for * SEH to work, the OS must be able to perform stack walking. On x64, this requires the diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java index 5e97ed0146b8..be7745d00ac9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java @@ -116,6 +116,6 @@ public int getOffsetInFrame(Register register) { * provided frame really has callee saved registers, since that cannot be checked automatically. */ @SuppressWarnings("unused") - public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess) { + public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, int diagnosticLevel) { } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java index 6b5092c9adb2..b38ba57b6ede 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java @@ -68,7 +68,7 @@ private static void invalidVTableEntryHandler() { LogHandler logHandler = ImageSingletons.lookup(LogHandler.class); Log log = Log.enterFatalContext(logHandler, callerIP, MSG, null); if (log != null) { - SubstrateDiagnostics.print(log, callerSP, callerIP, WordFactory.nullPointer(), true); + SubstrateDiagnostics.printForFatalError(log, callerSP, callerIP, WordFactory.nullPointer(), true); log.string(MSG).newline(); } logHandler.fatalError(); 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 053ab9692857..7e71c73c9ae0 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 @@ -41,11 +41,11 @@ static RegisterDumper singleton() { return ImageSingletons.lookup(RegisterDumper.class); } - static void dumpReg(Log log, String label, long value, boolean printLocationInfo, boolean allowJavaHeapAccess) { + static void dumpReg(Log log, String label, long value, boolean printLocationInfo, int diagnosticLevel) { log.string(label).zhex(value); if (printLocationInfo) { log.spaces(1); - SubstrateDiagnostics.printLocationInfo(log, WordFactory.unsigned(value), allowJavaHeapAccess); + SubstrateDiagnostics.printLocationInfo(log, WordFactory.unsigned(value), diagnosticLevel); } log.newline(); } @@ -53,7 +53,7 @@ static void dumpReg(Log log, String label, long value, boolean printLocationInfo interface Context extends PointerBase { } - void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess); + void dumpRegisters(Log log, Context context, boolean printLocationInfo, int diagnosticLevel); 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 e6866121777c..03ecfd48b04b 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 @@ -40,6 +40,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.code.CodeInfo; @@ -98,29 +99,39 @@ public static int maxInvocations() { int result = 0; DiagnosticThunkRegister thunks = DiagnosticThunkRegister.getSingleton(); for (int i = 0; i < thunks.size(); i++) { - result += thunks.getThunk(i).maxInvocations(); + result += thunks.getThunk(i).baseInvocations(); } 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)) { + public static void printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { + if (value.notEqual(0) && + !RuntimeCodeInfoMemory.singleton().printLocationInfo(log, value, diagnosticLevel) && + !VMThreads.printLocationInfo(log, value, diagnosticLevel) && + !Heap.getHeap().printLocationInfo(log, value, diagnosticLevel)) { log.string("is an unknown value"); } } - /** Prints extensive diagnostic information to the given Log. */ - public static boolean print(Log log, Pointer sp, CodePointer ip) { - return print(log, sp, ip, WordFactory.nullPointer(), false); + /** + * See + * {@link #printForFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters)}. + */ + public static boolean printForFatalError(Log log, Pointer sp, CodePointer ip) { + return printForFatalError(log, sp, ip, WordFactory.nullPointer(), false); } /** - * Print diagnostics for the various subsystems. If a fatal error occurs while printing - * diagnostics, it can happen that the same thread enters this method multiple times. + * Called for fatal errors to print extensive diagnostic information to the given log. If a + * fatal error occurs while printing diagnostics, this method may get called recursively + * multiple times. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") - public static boolean print(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { + public static boolean printForFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { + return printDiagnostics(log, sp, ip, context, frameHasCalleeSavedRegisters); + } + + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + private static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { log.newline(); // Save the state of the initial error so that this state is consistently used, even if // further errors occur while printing diagnostics. @@ -130,12 +141,13 @@ public static boolean print(Log log, Pointer sp, CodePointer ip, RegisterDumper. return false; } - printDiagnosticsForCurrentState(); + int diagnosticLevel = SubstrateOptions.PrintDiagnosticDetails.getValue(); + printDiagnosticsForCurrentState(diagnosticLevel); 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() { + private static void printDiagnosticsForCurrentState(int diagnosticLevel) { assert isInProgressByCurrentThread(); Log log = state.log; @@ -150,9 +162,9 @@ private static void printDiagnosticsForCurrentState() { int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); while (state.diagnosticThunkIndex < numDiagnosticThunks) { DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(state.diagnosticThunkIndex); - while (++state.invocationCount <= thunk.maxInvocations()) { + while (++state.invocationCount <= thunk.baseInvocations()) { try { - thunk.printDiagnostics(log, state.invocationCount); + thunk.printDiagnostics(log, diagnosticLevel, state.invocationCount); break; } catch (Throwable e) { dumpException(log, thunk, e); @@ -177,7 +189,7 @@ static void dumpRuntimeCompilation(Log log) { log.newline(); try { - RuntimeCodeInfoMemory.singleton().printTable(log, true); + RuntimeCodeInfoMemory.singleton().printTable(log, DiagnosticLevel.MAX); } catch (Exception e) { dumpException(log, "DumpRuntimeCodeInfoMemory", e); } @@ -277,34 +289,40 @@ public void clear() { private static class DumpRegisters extends DiagnosticThunk { @Override - public int maxInvocations() { - return 3; + protected int baseInvocations() { + return 2; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { RegisterDumper.Context context = state.context; + boolean printLocationInfo = invocationCount < maxInvocations(diagnosticLevel); + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); if (context.isNonNull()) { log.string("General purpose register values:").indent(true); - RegisterDumper.singleton().dumpRegisters(log, context, invocationCount <= 2, invocationCount == 1); + RegisterDumper.singleton().dumpRegisters(log, context, printLocationInfo, adjustedDiagnosticLevel); log.indent(false); } else if (CalleeSavedRegisters.supportedByPlatform() && state.frameHasCalleeSavedRegisters) { - CalleeSavedRegisters.singleton().dumpRegisters(log, state.sp, invocationCount <= 2, invocationCount == 1); - + CalleeSavedRegisters.singleton().dumpRegisters(log, state.sp, printLocationInfo, adjustedDiagnosticLevel); } } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.JAVA_HEAP_ACCESS | DiagnosticLevel.UNSAFE_ACCESS; + } } private static class DumpInstructions extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 3; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { if (invocationCount < 3) { printBytesBeforeAndAfterIp(log, invocationCount); } else if (invocationCount == 3) { @@ -312,6 +330,11 @@ public void printDiagnostics(Log log, int invocationCount) { } } + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } + private static void printBytesBeforeAndAfterIp(Log log, int invocationCount) { // print 64 or 32 instruction bytes. int bytesToPrint = 64 >> invocationCount; @@ -332,13 +355,13 @@ private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesA private static class DumpTopOfCurrentThreadStack extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { Pointer sp = state.sp; log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); @@ -360,32 +383,42 @@ public void printDiagnostics(Log log, int invocationCount) { } log.indent(false).newline(); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpDeoptStubPointer extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline(); } } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpTopFrame extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { // We already dump all safe values first, so there is nothing we could retry if an error // occurs. Pointer sp = state.sp; @@ -421,52 +454,61 @@ public void printDiagnostics(Log log, int invocationCount) { } log.indent(false); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpThreads extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @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); - } - - 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"); + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + if (DiagnosticLevel.isUnsafeAccessAllowed(adjustedDiagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + // If we are not at a safepoint, then it is unsafe to access thread locals of + // another thread as the IsolateThread could be freed at any time. + log.string("Threads:").indent(true); + for (IsolateThread thread = VMThreads.firstThreadUnsafe(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) { + log.zhex(thread).spaces(1).string(VMThreads.StatusSupport.getStatusString(thread)); + if (DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel)) { + 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.string(", stack(").zhex(VMThreads.StackEnd.get(thread)).string(",").zhex(VMThreads.StackBase.get(thread)).string(")"); - log.newline(); + log.indent(false); } - log.indent(false); + } + + @Override + protected int usedFeatures() { + if (VMOperation.isInProgressAtSafepoint()) { + return DiagnosticLevel.JAVA_HEAP_ACCESS; + } + return DiagnosticLevel.UNSAFE_ACCESS | DiagnosticLevel.JAVA_HEAP_ACCESS; } } private static class DumpCurrentThreadLocals extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { - printThreadLocals(log, invocationCount); - } - - private static void printThreadLocals(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { IsolateThread currentThread = CurrentIsolate.getCurrentThread(); if (isThreadOnlyAttachedForCrashHandler(currentThread)) { if (invocationCount == 1) { @@ -474,114 +516,168 @@ private static void printThreadLocals(Log log, int invocationCount) { log.newline(); } } else { + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); log.string("VM thread locals for the failing thread ").zhex(currentThread).string(":").indent(true); - VMThreadLocalInfos.dumpToLog(log, currentThread, invocationCount == 1); + VMThreadLocalInfos.dumpToLog(log, currentThread, adjustedDiagnosticLevel); log.indent(false); } } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.JAVA_HEAP_ACCESS; + } } private static class DumpCurrentVMOperation extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @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); + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); + VMOperationControl.printCurrentVMOperation(log, isJavaHeapAccessAllowed); log.newline(); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.JAVA_HEAP_ACCESS; + } } private static class DumpVMOperationHistory extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @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); + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); + VMOperationControl.printRecentEvents(log, isJavaHeapAccessAllowed); + } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.JAVA_HEAP_ACCESS; } } private static class DumpCodeCacheHistory extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { - RuntimeCodeInfoHistory.singleton().printRecentOperations(log, invocationCount == 1); + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); + RuntimeCodeInfoHistory.singleton().printRecentOperations(log, isJavaHeapAccessAllowed); } } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.JAVA_HEAP_ACCESS; + } } private static class DumpRuntimeCodeInfoMemory extends DiagnosticThunk { @Override - public int maxInvocations() { - return 2; + public int baseInvocations() { + return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { - RuntimeCodeInfoMemory.singleton().printTable(log, invocationCount == 1); + int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); + boolean isUnsafeAccessAllowed = DiagnosticLevel.isUnsafeAccessAllowed(adjustedDiagnosticLevel); + RuntimeCodeInfoMemory.singleton().printTable(log, isJavaHeapAccessAllowed, isUnsafeAccessAllowed); + } + } + + @Override + protected int usedFeatures() { + // TEMP (chaeubl): completely decoupled from the use -> will get outdated soon! + if (VMOperation.isInProgressAtSafepoint()) { + return DiagnosticLevel.JAVA_HEAP_ACCESS; } + return DiagnosticLevel.UNSAFE_ACCESS | DiagnosticLevel.JAVA_HEAP_ACCESS; } } private static class DumpRecentDeoptimizations extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { Deoptimizer.logRecentDeoptimizationEvents(log); } } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpCounters extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { log.string("Counters:").indent(true); Counter.logValues(); log.indent(false); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpCurrentThreadFrameAnchors extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, 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); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk { @@ -589,30 +685,35 @@ private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk Stage0StackFramePrintVisitor.SINGLETON}; @Override - public int maxInvocations() { + public int baseInvocations() { return 3; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, 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); } + + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } } private static class DumpOtherStackTraces extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, int diagnosticLevel, 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). @@ -632,6 +733,11 @@ public void printDiagnostics(Log log, int invocationCount) { } } + @Override + protected int usedFeatures() { + return DiagnosticLevel.ZERO; + } + private static void printFrameAnchors(Log log, IsolateThread vmThread) { log.string("Frame anchors:").indent(true); logFrameAnchors(log, vmThread); @@ -649,14 +755,29 @@ 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. + * invoked at most {@link #baseInvocations()} 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.") - public abstract void printDiagnostics(Log log, int invocationCount); + public abstract void printDiagnostics(Log log, int diagnosticLevel, int invocationCount); @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public abstract int maxInvocations(); + public int maxInvocations(int diagnosticLevel) { + int relevantDiagnostics = diagnosticLevel & usedFeatures(); + return Integer.bitCount(relevantDiagnostics) + baseInvocations(); + } + + protected abstract int baseInvocations(); + + protected abstract int usedFeatures(); + + protected int adjustDiagnosticLevel(int diagnosticLevel, int invocationCount) { + int result = diagnosticLevel & usedFeatures(); + for (int i = 1; i < invocationCount; i++) { + result ^= Integer.highestOneBit(result); + } + return result; + } } public static class DiagnosticThunkRegister { @@ -704,4 +825,19 @@ DiagnosticThunk getThunk(int index) { return diagnosticThunks[index]; } } + + public static class DiagnosticLevel { + public static final int ZERO = 0; + public static final int JAVA_HEAP_ACCESS = 0b01; + public static final int UNSAFE_ACCESS = 0b10; + public static final int MAX = (UNSAFE_ACCESS << 1) - 1; + + public static boolean isJavaHeapAccessAllowed(int level) { + return level >= 1; + } + + public static boolean isUnsafeAccessAllowed(int level) { + return level >= 2; + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java index 8b1fd383a34a..9f585c3515bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java @@ -38,9 +38,6 @@ * Garbage collection-specific options that are supported by all garbage collectors. */ public class SubstrateGCOptions { - @Option(help = "Print summary GC information after each collection", type = OptionType.Expert)// - public static final RuntimeOptionKey PrintGC = new RuntimeOptionKey<>(false); - @Option(help = "Print more information about the heap before and after each collection", type = OptionType.Expert)// public static final RuntimeOptionKey VerboseGC = new RuntimeOptionKey<>(false); @@ -53,6 +50,9 @@ public class SubstrateGCOptions { @Option(help = "Ignore calls to System.gc()", type = OptionType.Expert)// public static final RuntimeOptionKey DisableExplicitGC = new RuntimeOptionKey<>(false); + @Option(help = "Print summary GC information after each collection", type = OptionType.Expert)// + public static final RuntimeOptionKey PrintGC = new RuntimeOptionKey<>(false); + @Option(help = "The minimum heap size at run-time, in bytes.", type = OptionType.User)// public static final RuntimeOptionKey MinHeapSize = new RuntimeOptionKey(0L) { @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index f3e35232d53a..93103cb58131 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -605,4 +605,7 @@ private static int getHostPageSize() { return 4096; } } + + @Option(help = "Determines how much diagnostic information is printed. Supported values range from 0 to 2, where 2 prints the largest amount of information.", type = Expert)// + public static final RuntimeOptionKey PrintDiagnosticDetails = new RuntimeOptionKey<>(2); } 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 663c761b47b2..960106732c0a 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 @@ -89,14 +89,29 @@ public void run() { } public abstract class SubstrateSegfaultHandler { - public static class Options { @Option(help = "Install segfault handler that prints register contents and full Java stacktrace. Default: enabled for an executable, disabled for a shared library.")// static final RuntimeOptionKey InstallSegfaultHandler = new RuntimeOptionKey<>(null); } + private boolean installed; + + @Fold + public static SubstrateSegfaultHandler singleton() { + return ImageSingletons.lookup(SubstrateSegfaultHandler.class); + } + + public static boolean isInstalled() { + return singleton().installed; + } + /** Installs the platform dependent segfault handler. */ - protected abstract void install(); + public void install() { + installInternal(); + installed = true; + } + + protected abstract void installInternal(); protected abstract void printSignalInfo(Log log, PointerBase signalInfo); @@ -155,7 +170,7 @@ private static void dumpInterruptibly(PointerBase signalInfo, RegisterDumper.Con PointerBase sp = RegisterDumper.singleton().getSP(context); PointerBase ip = RegisterDumper.singleton().getIP(context); - boolean printedDiagnostics = SubstrateDiagnostics.print(log, (Pointer) sp, (CodePointer) ip, context, false); + boolean printedDiagnostics = SubstrateDiagnostics.printForFatalError(log, (Pointer) sp, (CodePointer) ip, context, false); if (printedDiagnostics) { log.string("Segfault detected, aborting process. Use runtime option -R:-InstallSegfaultHandler if you don't want to use SubstrateSegfaultHandler.").newline(); log.newline(); 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 1fbce692c360..7259e2aed29d 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 @@ -254,7 +254,7 @@ public interface Thunk { /** Prints extensive diagnostic information to the given Log. */ public static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip) { - return SubstrateDiagnostics.print(log, sp, ip, WordFactory.nullPointer(), false); + return SubstrateDiagnostics.printForFatalError(log, sp, ip, WordFactory.nullPointer(), false); } /** 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 ef6d37702c6a..c37d0c19c29c 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 @@ -34,7 +34,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.SubstrateDiagnostics; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; @@ -246,16 +246,17 @@ 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); + public void printTable(Log log, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { + if (allowUnsafeAccess || VMOperation.isInProgressAtSafepoint()) { + // If we are not at a safepoint, then 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); } - log.indent(false); } @Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.") @@ -300,31 +301,34 @@ public void tearDown() { } @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; - } + public boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { + if (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + // If we are not at a safepoint, then 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()) { + boolean allowJavaHeapAccess = DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel); + 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 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; + 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; + } } } } 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 4deca35e1038..dcafe4dfb9d7 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 @@ -216,5 +216,5 @@ public List> getLoadedClasses() { * 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); + public abstract boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java index c80f1a272524..921111908dd1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java @@ -131,7 +131,7 @@ private static void doShutdown(CodePointer callerIP, String msg, Throwable ex) { log.newline(); } - SubstrateDiagnostics.print(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); + SubstrateDiagnostics.printForFatalError(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); /* * Print the error message again, so that the most important bit of information 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 index ea8700d81ce5..ffb2f2c6cba5 100644 --- 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 @@ -46,7 +46,7 @@ public abstract class VMLockSupport { public static class DumpVMMutexes extends DiagnosticThunk { @Override - public int maxInvocations() { + public int baseInvocations() { return 1; } 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 c1b7e49f5701..808da87ccd2e 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 @@ -40,6 +40,7 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.NeverInline; @@ -193,7 +194,7 @@ public static boolean mayExecuteVmOperations() { } } - public static void printCurrentVMOperation(Log log, boolean allowJavaHeapAccess) { + public static void printCurrentVMOperation(Log log, boolean isJavaHeapAccessAllowed) { /* * 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. @@ -202,7 +203,7 @@ public static void printCurrentVMOperation(Log log, boolean allowJavaHeapAccess) VMOperation op = control.inProgress.operation; if (op == null) { log.string("No VMOperation in progress").newline(); - } else if (allowJavaHeapAccess) { + } else if (isJavaHeapAccessAllowed) { 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(); 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 c2a16375a720..322e95625fc3 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 @@ -44,6 +44,8 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateSegfaultHandler; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; @@ -573,18 +575,22 @@ public static boolean ownsThreadMutex() { return THREAD_MUTEX.isOwner(); } - public static boolean printLocationInfo(Log log, UnsignedWord value) { + public static boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { 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 (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + // If we are not at a safepoint, then it is unsafe to access thread locals of + // another thread is unsafe as the IsolateThread could be freed at any time. + UnsignedWord stackBase = StackBase.get(thread); + UnsignedWord stackEnd = StackEnd.get(thread); + if (value.belowThan(stackBase) && value.aboveOrEqual(stackEnd)) { + log.string("points into the stack for thread ").zhex(thread); + return true; + } } if (SubstrateOptions.MultiThreaded.getValue()) { 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 b9bd9eace35c..9a0cab7e49b5 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 @@ -39,6 +39,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.heap.ReferenceAccess; @@ -60,7 +61,7 @@ public static boolean setInfos(Collection infos) { } } - public static void dumpToLog(Log log, IsolateThread thread, boolean allowJavaHeapAccess) { + public static void dumpToLog(Log log, IsolateThread thread, int diagnosticLevel) { 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) { @@ -73,7 +74,7 @@ public static void dumpToLog(Log log, IsolateThread thread, boolean allowJavaHea WordBase value = primitiveData(thread).readWord(WordFactory.signed(info.offset)); log.string("(Word) ").signed(value).string(" (").zhex(value.rawValue()).string(")"); } else if (info.threadLocalClass == FastThreadLocalObject.class) { - if (allowJavaHeapAccess) { + if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel)) { Object value = ObjectAccess.readObject(objectData(thread), WordFactory.signed(info.offset)); log.string("(Object) "); if (value == null) { From 6a1b7939665de49da868bfd1dbfadc6272ef82a5 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Tue, 14 Sep 2021 09:41:06 +0200 Subject: [PATCH 2/6] Simplifications and better control over diagnostic thunks. --- .../genscavenge/GreyToBlackObjectVisitor.java | 6 +- .../oracle/svm/core/genscavenge/HeapImpl.java | 24 +- .../amd64/AMD64CalleeSavedRegisters.java | 38 +- .../svm/core/posix/PosixLogHandler.java | 2 +- .../core/posix/UContextRegisterDumper.java | 6 +- .../AArch64UContextRegisterDumper.java | 68 +- .../amd64/AMD64UContextRegisterDumper.java | 38 +- .../darwin/DarwinUContextRegisterDumper.java | 38 +- .../svm/core/windows/WindowsLogHandler.java | 2 +- .../core/windows/WindowsRegisterDumper.java | 42 +- .../oracle/svm/core/CalleeSavedRegisters.java | 2 +- .../svm/core/InvalidVTableEntryHandler.java | 2 +- .../com/oracle/svm/core/RegisterDumper.java | 6 +- .../oracle/svm/core/SubstrateDiagnostics.java | 593 ++++++++++-------- .../com/oracle/svm/core/SubstrateOptions.java | 11 +- .../svm/core/SubstrateSegfaultHandler.java | 2 +- .../com/oracle/svm/core/SubstrateUtil.java | 2 +- .../svm/core/code/RuntimeCodeInfoMemory.java | 9 +- .../src/com/oracle/svm/core/heap/Heap.java | 2 +- .../svm/core/jdk/VMErrorSubstitutions.java | 2 +- .../oracle/svm/core/locks/VMLockSupport.java | 6 +- .../svm/core/snippets/ImplicitExceptions.java | 2 +- .../com/oracle/svm/core/thread/VMThreads.java | 4 +- .../core/threadlocal/VMThreadLocalInfos.java | 4 +- .../svm/jni/functions/JNIFunctions.java | 2 +- 25 files changed, 502 insertions(+), 411 deletions(-) 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 4fd2188137da..1055262f4659 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 @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.genscavenge; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.word.Word; @@ -33,6 +34,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; import com.oracle.svm.core.annotate.AlwaysInline; @@ -132,13 +134,13 @@ public void noteObject(Object o) { } @Override - public int baseInvocations() { + public int maxInvocations() { return 1; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") - public void printDiagnostics(Log log, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (historyCount > 0) { log.string("[GreyToBlackObjectVisitor.RealDiagnosticReporter.invoke:") .string(" history / count: ") 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 3add5293446e..cb39a375bdd7 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 @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; @@ -43,7 +44,6 @@ import com.oracle.svm.core.MemoryWalker; import com.oracle.svm.core.SubstrateDiagnostics; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.NeverInline; @@ -612,7 +612,7 @@ public Reference getAndClearReferencePendingList() { } @Override - public boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { + public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { if (SubstrateOptions.SpawnIsolates.getValue()) { Pointer heapBase = KnownIntrinsics.heapBase(); if (value.equal(heapBase)) { @@ -625,8 +625,8 @@ public boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLeve } Pointer ptr = (Pointer) value; - if (printLocationInfo(log, ptr, diagnosticLevel)) { - if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel) && objectHeaderImpl.pointsToObjectHeader(ptr)) { + if (printLocationInfo(log, ptr, allowJavaHeapAccess, allowUnsafeOperations)) { + if (allowJavaHeapAccess && objectHeaderImpl.pointsToObjectHeader(ptr)) { DynamicHub hub = objectHeaderImpl.readDynamicHubFromPointer(ptr); log.indent(true); log.string("hub=").string(hub.getName()); @@ -647,7 +647,7 @@ static Pointer getImageHeapStart() { } } - private boolean printLocationInfo(Log log, Pointer ptr, int diagnosticLevel) { + private boolean printLocationInfo(Log log, Pointer ptr, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { if (imageHeapInfo.isInReadOnlyPrimitivePartition(ptr)) { log.string("points into the image heap (read-only primitives)"); return true; @@ -676,7 +676,7 @@ private boolean printLocationInfo(Log log, Pointer ptr, int diagnosticLevel) { return true; } - if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel)) { + if (allowJavaHeapAccess) { // Accessing spaces and chunks is safe if we prevent a GC. if (isInYoungGen(ptr)) { log.string("points into the young generation"); @@ -687,7 +687,7 @@ private boolean printLocationInfo(Log log, Pointer ptr, int diagnosticLevel) { } } - if (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then it is unsafe to access thread locals of another // thread as the IsolateThread could be freed at any time. return printTlabInfo(log, ptr); @@ -781,19 +781,19 @@ private static boolean printTlabInfo(Log log, Pointer ptr, IsolateThread thread) @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"; + assert SubstrateDiagnostics.isFatalErrorHandlingThread() : "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 baseInvocations() { + 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) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { GCImpl gc = GCImpl.getGCImpl(); log.string("Heap settings and statistics:").indent(true); @@ -813,13 +813,13 @@ public void printDiagnostics(Log log, int invocationCount) { private static class DumpChunkInformation extends DiagnosticThunk { @Override - public int baseInvocations() { + 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) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { HeapImpl heap = HeapImpl.getHeapImpl(); heap.logImageHeapPartitionBoundaries(log); zapValuesToLog(log).newline(); diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java index ea160145b194..1247f3cace34 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64CalleeSavedRegisters.java @@ -167,36 +167,36 @@ private AMD64Address calleeSaveAddress(AMD64MacroAssembler asm, int frameSize, R } @Override - public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { + public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { log.string("Callee saved registers (sp=").zhex(callerSP).string(")").indent(true); /* * The loop to print all registers is manually unrolled so that the register order is * defined, and also so that the lookup of the "offset in frame" can be constant folded at * image build time using a @Fold method. */ - dumpReg(log, "RAX ", callerSP, offsetInFrameOrNull(AMD64.rax), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RBX ", callerSP, offsetInFrameOrNull(AMD64.rbx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RCX ", callerSP, offsetInFrameOrNull(AMD64.rcx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RDX ", callerSP, offsetInFrameOrNull(AMD64.rdx), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RBP ", callerSP, offsetInFrameOrNull(AMD64.rbp), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RSI ", callerSP, offsetInFrameOrNull(AMD64.rsi), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RDI ", callerSP, offsetInFrameOrNull(AMD64.rdi), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RSP ", callerSP, offsetInFrameOrNull(AMD64.rsp), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R8 ", callerSP, offsetInFrameOrNull(AMD64.r8), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R9 ", callerSP, offsetInFrameOrNull(AMD64.r9), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R10 ", callerSP, offsetInFrameOrNull(AMD64.r10), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R11 ", callerSP, offsetInFrameOrNull(AMD64.r11), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R12 ", callerSP, offsetInFrameOrNull(AMD64.r12), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R13 ", callerSP, offsetInFrameOrNull(AMD64.r13), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R14 ", callerSP, offsetInFrameOrNull(AMD64.r14), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R15 ", callerSP, offsetInFrameOrNull(AMD64.r15), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + dumpReg(log, "RAX ", callerSP, offsetInFrameOrNull(AMD64.rax), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBX ", callerSP, offsetInFrameOrNull(AMD64.rbx), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RCX ", callerSP, offsetInFrameOrNull(AMD64.rcx), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDX ", callerSP, offsetInFrameOrNull(AMD64.rdx), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBP ", callerSP, offsetInFrameOrNull(AMD64.rbp), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSI ", callerSP, offsetInFrameOrNull(AMD64.rsi), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDI ", callerSP, offsetInFrameOrNull(AMD64.rdi), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSP ", callerSP, offsetInFrameOrNull(AMD64.rsp), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R8 ", callerSP, offsetInFrameOrNull(AMD64.r8), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R9 ", callerSP, offsetInFrameOrNull(AMD64.r9), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R10 ", callerSP, offsetInFrameOrNull(AMD64.r10), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R11 ", callerSP, offsetInFrameOrNull(AMD64.r11), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R12 ", callerSP, offsetInFrameOrNull(AMD64.r12), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R13 ", callerSP, offsetInFrameOrNull(AMD64.r13), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R14 ", callerSP, offsetInFrameOrNull(AMD64.r14), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R15 ", callerSP, offsetInFrameOrNull(AMD64.r15), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); log.indent(false); } - private static void dumpReg(Log log, String label, Pointer callerSP, int offsetInFrameOrNull, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { + private static void dumpReg(Log log, String label, Pointer callerSP, int offsetInFrameOrNull, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { if (offsetInFrameOrNull != 0) { long value = callerSP.readLong(offsetInFrameOrNull); - RegisterDumper.dumpReg(log, label, value, printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + RegisterDumper.dumpReg(log, label, value, printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java index e5c4b743f1fd..43456a1c1304 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java @@ -79,7 +79,7 @@ public void flush() { @Override public void fatalError() { - if (SubstrateDiagnostics.isInProgress()) { + if (SubstrateDiagnostics.isFatalErrorHandlingInProgress()) { // Delay the shutdown a bit if another thread has something important to report. VMThreads.singleton().nativeSleep(3000); } 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 7eee53fb5a20..593c0307eb04 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, boolean printLocationInfo, boolean allowJavaHeapAccess); + void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations); 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, boolean printLocationInfo, boolean allowJavaHeapAccess) { - dumpRegisters(log, (ucontext_t) context, printLocationInfo, allowJavaHeapAccess); + default void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { + dumpRegisters(log, (ucontext_t) context, printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } @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 c9dc6b1754fd..b3c643e630b6 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,42 +59,42 @@ public void afterRegistration(AfterRegistrationAccess access) { class AArch64UContextRegisterDumper implements UContextRegisterDumper { @Override - public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { mcontext_t sigcontext = uContext.uc_mcontext(); GregsPointer regs = sigcontext.regs(); - 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, "SP ", sigcontext.sp(), printLocationInfo, allowJavaHeapAccess); - dumpReg(log, "PC ", sigcontext.pc(), printLocationInfo, allowJavaHeapAccess); + dumpReg(log, "R0 ", regs.read(0), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R1 ", regs.read(1), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R2 ", regs.read(2), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R3 ", regs.read(3), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R4 ", regs.read(4), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R5 ", regs.read(5), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R6 ", regs.read(6), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R7 ", regs.read(7), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R8 ", regs.read(8), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R9 ", regs.read(9), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R10 ", regs.read(10), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R11 ", regs.read(11), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R12 ", regs.read(12), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R13 ", regs.read(13), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R14 ", regs.read(14), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R15 ", regs.read(15), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R16 ", regs.read(16), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R17 ", regs.read(17), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R18 ", regs.read(18), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R19 ", regs.read(19), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R20 ", regs.read(20), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R21 ", regs.read(21), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R22 ", regs.read(22), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R23 ", regs.read(23), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R24 ", regs.read(24), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R25 ", regs.read(25), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R26 ", regs.read(26), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R27 ", regs.read(27), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R28 ", regs.read(28), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R29 ", regs.read(29), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R30 ", regs.read(30), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "SP ", sigcontext.sp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "PC ", sigcontext.pc(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } @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 f383405f72fb..a4c6a205dd14 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, boolean printLocationInfo, boolean allowJavaHeapAccess) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { GregsPointer gregs = uContext.uc_mcontext_gregs(); - 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); + dumpReg(log, "RAX ", gregs.read(GregEnum.REG_RAX()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBX ", gregs.read(GregEnum.REG_RBX()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RCX ", gregs.read(GregEnum.REG_RCX()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDX ", gregs.read(GregEnum.REG_RDX()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBP ", gregs.read(GregEnum.REG_RBP()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSI ", gregs.read(GregEnum.REG_RSI()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDI ", gregs.read(GregEnum.REG_RDI()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSP ", gregs.read(GregEnum.REG_RSP()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R8 ", gregs.read(GregEnum.REG_R8()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R9 ", gregs.read(GregEnum.REG_R9()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R10 ", gregs.read(GregEnum.REG_R10()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R11 ", gregs.read(GregEnum.REG_R11()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R12 ", gregs.read(GregEnum.REG_R12()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R13 ", gregs.read(GregEnum.REG_R13()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R14 ", gregs.read(GregEnum.REG_R14()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R15 ", gregs.read(GregEnum.REG_R15()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "EFL ", gregs.read(GregEnum.REG_EFL()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RIP ", gregs.read(GregEnum.REG_RIP()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } @Override 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 c56eb458e409..d3005a855363 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, boolean printLocationInfo, boolean allowJavaHeapAccess) { + public void dumpRegisters(Log log, ucontext_t uContext, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { Signal.MContext64 sigcontext = uContext.uc_mcontext64(); - 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); + dumpReg(log, "RAX ", ((Pointer) sigcontext).readLong(sigcontext.rax_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBX ", ((Pointer) sigcontext).readLong(sigcontext.rbx_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RCX ", ((Pointer) sigcontext).readLong(sigcontext.rcx_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDX ", ((Pointer) sigcontext).readLong(sigcontext.rdx_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBP ", ((Pointer) sigcontext).readLong(sigcontext.rbp_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSI ", ((Pointer) sigcontext).readLong(sigcontext.rsi_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDI ", ((Pointer) sigcontext).readLong(sigcontext.rdi_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSP ", ((Pointer) sigcontext).readLong(sigcontext.rsp_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R8 ", ((Pointer) sigcontext).readLong(sigcontext.r8_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R9 ", ((Pointer) sigcontext).readLong(sigcontext.r9_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R10 ", ((Pointer) sigcontext).readLong(sigcontext.r10_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R11 ", ((Pointer) sigcontext).readLong(sigcontext.r11_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R12 ", ((Pointer) sigcontext).readLong(sigcontext.r12_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R13 ", ((Pointer) sigcontext).readLong(sigcontext.r13_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R14 ", ((Pointer) sigcontext).readLong(sigcontext.r14_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R15 ", ((Pointer) sigcontext).readLong(sigcontext.r15_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "EFL ", ((Pointer) sigcontext).readLong(sigcontext.efl_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RIP ", ((Pointer) sigcontext).readLong(sigcontext.rip_offset()), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } @Override diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsLogHandler.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsLogHandler.java index 9ccfbba43598..0aebe9177fe6 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsLogHandler.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsLogHandler.java @@ -67,7 +67,7 @@ public void flush() { @Override public void fatalError() { - if (SubstrateDiagnostics.isInProgress()) { + if (SubstrateDiagnostics.isFatalErrorHandlingInProgress()) { // Delay the shutdown a bit if another thread has something important to report. VMThreads.singleton().nativeSleep(3000); } 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 afeb1ad29977..2b20cce32603 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, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { - dumpRegisters(log, (CONTEXT) context, printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + public void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { + dumpRegisters(log, (CONTEXT) context, printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } - private static void dumpRegisters(Log log, CONTEXT context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { - dumpReg(log, "RAX ", context.Rax(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RBX ", context.Rbx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RCX ", context.Rcx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RDX ", context.Rdx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RBP ", context.Rbp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RSI ", context.Rsi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RDI ", context.Rdi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RSP ", context.Rsp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R8 ", context.R8(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R9 ", context.R9(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R10 ", context.R10(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R11 ", context.R11(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R12 ", context.R12(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R13 ", context.R13(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R14 ", context.R14(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "R15 ", context.R15(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "EFL ", context.EFlags(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); - dumpReg(log, "RIP ", context.Rip(), printLocationInfo, allowJavaHeapAccess, allowUnsafeAccess); + private static void dumpRegisters(Log log, CONTEXT context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { + dumpReg(log, "RAX ", context.Rax(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBX ", context.Rbx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RCX ", context.Rcx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDX ", context.Rdx(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RBP ", context.Rbp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSI ", context.Rsi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RDI ", context.Rdi(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RSP ", context.Rsp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R8 ", context.R8(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R9 ", context.R9(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R10 ", context.R10(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R11 ", context.R11(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R12 ", context.R12(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R13 ", context.R13(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R14 ", context.R14(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "R15 ", context.R15(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "EFL ", context.EFlags(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); + dumpReg(log, "RIP ", context.Rip(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java index be7745d00ac9..043fce6c8eb2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CalleeSavedRegisters.java @@ -116,6 +116,6 @@ public int getOffsetInFrame(Register register) { * provided frame really has callee saved registers, since that cannot be checked automatically. */ @SuppressWarnings("unused") - public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, int diagnosticLevel) { + public void dumpRegisters(Log log, Pointer callerSP, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java index b38ba57b6ede..43b7f36d57f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidVTableEntryHandler.java @@ -68,7 +68,7 @@ private static void invalidVTableEntryHandler() { LogHandler logHandler = ImageSingletons.lookup(LogHandler.class); Log log = Log.enterFatalContext(logHandler, callerIP, MSG, null); if (log != null) { - SubstrateDiagnostics.printForFatalError(log, callerSP, callerIP, WordFactory.nullPointer(), true); + SubstrateDiagnostics.printFatalError(log, callerSP, callerIP, WordFactory.nullPointer(), true); log.string(MSG).newline(); } logHandler.fatalError(); 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 7e71c73c9ae0..c9774ee878da 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 @@ -41,11 +41,11 @@ static RegisterDumper singleton() { return ImageSingletons.lookup(RegisterDumper.class); } - static void dumpReg(Log log, String label, long value, boolean printLocationInfo, int diagnosticLevel) { + static void dumpReg(Log log, String label, long value, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { log.string(label).zhex(value); if (printLocationInfo) { log.spaces(1); - SubstrateDiagnostics.printLocationInfo(log, WordFactory.unsigned(value), diagnosticLevel); + SubstrateDiagnostics.printLocationInfo(log, WordFactory.unsigned(value), allowJavaHeapAccess, allowUnsafeOperations); } log.newline(); } @@ -53,7 +53,7 @@ static void dumpReg(Log log, String label, long value, boolean printLocationInfo interface Context extends PointerBase { } - void dumpRegisters(Log log, Context context, boolean printLocationInfo, int diagnosticLevel); + void dumpRegisters(Log log, Context context, boolean printLocationInfo, boolean allowJavaHeapAccess, boolean allowUnsafeOperations); 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 03ecfd48b04b..f2750b30568a 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 @@ -34,15 +34,20 @@ import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; +import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; @@ -72,10 +77,11 @@ import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.util.Counter; +import com.oracle.svm.core.util.VMError; public class SubstrateDiagnostics { private static final FastThreadLocalBytes threadOnlyAttachedForCrashHandler = FastThreadLocalFactory.createBytes(() -> 1); - private static final PrintDiagnosticsState state = new PrintDiagnosticsState(); + private static final PrintDiagnosticsState fatalErrorState = new PrintDiagnosticsState(); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void setOnlyAttachedForCrashHandler(IsolateThread thread) { @@ -86,72 +92,107 @@ private static boolean isThreadOnlyAttachedForCrashHandler(IsolateThread thread) return threadOnlyAttachedForCrashHandler.getAddress(thread).read() != 0; } - public static boolean isInProgress() { - return state.diagnosticThread.get().isNonNull(); + public static boolean isFatalErrorHandlingInProgress() { + return fatalErrorState.diagnosticThread.get().isNonNull(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isInProgressByCurrentThread() { - return state.diagnosticThread.get() == CurrentIsolate.getCurrentThread(); + public static boolean isFatalErrorHandlingThread() { + return fatalErrorState.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).baseInvocations(); + result += thunks.getThunk(i).maxInvocations(); } return result; } - public static void printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { + public static void printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { if (value.notEqual(0) && - !RuntimeCodeInfoMemory.singleton().printLocationInfo(log, value, diagnosticLevel) && - !VMThreads.printLocationInfo(log, value, diagnosticLevel) && - !Heap.getHeap().printLocationInfo(log, value, diagnosticLevel)) { + !RuntimeCodeInfoMemory.singleton().printLocationInfo(log, value, allowJavaHeapAccess, allowUnsafeOperations) && + !VMThreads.printLocationInfo(log, value, allowUnsafeOperations) && + !Heap.getHeap().printLocationInfo(log, value, allowJavaHeapAccess, allowUnsafeOperations)) { log.string("is an unknown value"); } } /** - * See - * {@link #printForFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters)}. + * See {@link #printInformation(Log, Pointer, CodePointer, RegisterDumper.Context, boolean)}. */ - public static boolean printForFatalError(Log log, Pointer sp, CodePointer ip) { - return printForFatalError(log, sp, ip, WordFactory.nullPointer(), false); + public static void printInformation(Log log, Pointer sp, CodePointer ip) { + printInformation(log, sp, ip, WordFactory.nullPointer(), false); } /** - * Called for fatal errors to print extensive diagnostic information to the given log. If a - * fatal error occurs while printing diagnostics, this method may get called recursively - * multiple times. + * This method prints diagnostic information. This method prints less information than + * {@link #printFatalError} as it only uses reasonably safe operations and memory accesses. So, + * as long as all parts of Native Image are fully functional, calling this method will never + * cause a crash. */ - public static boolean printForFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { - return printDiagnostics(log, sp, ip, context, frameHasCalleeSavedRegisters); + public static void printInformation(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context registerContext, boolean frameHasCalleeSavedRegisters) { + // Stack allocate an error context. + ErrorContext errorContext = StackValue.get(ErrorContext.class); + errorContext.setStackPointer(sp); + errorContext.setInstructionPointer(ip); + errorContext.setRegisterContext(registerContext); + errorContext.setFrameHasCalleeSavedRegisters(frameHasCalleeSavedRegisters); + + // Print all thunks in a reasonably safe way. + int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + for (int i = 0; i < numDiagnosticThunks; i++) { + DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); + int diagnosticLevel = DiagnosticThunkRegister.getSingleton().getDefaultDiagnosticLevel(i) & DiagnosticLevel.SAFE; + if (diagnosticLevel != DiagnosticLevel.DISABLED) { + thunk.printDiagnostics(log, errorContext, diagnosticLevel, 1); + } + } } - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - private static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { + /** + * See {@link #printFatalError(Log, Pointer, CodePointer, RegisterDumper.Context, boolean)}. + */ + public static boolean printFatalError(Log log, Pointer sp, CodePointer ip) { + return printFatalError(log, sp, ip, WordFactory.nullPointer(), false); + } + + /** + * This method prints extensive diagnostic information and is only called for fatal errors. This + * method may use operations and memory accesses that are not necessarily 100% safe. So, even if + * all parts of Native Image are fully functional, this method may cause crashes. + *

+ * If a segfault handler is present, then this method will be called recursively multiple times + * if further errors happen while printing diagnostics. On each recursive invocation, the level + * of detail of the diagnostic output will be reduced gradually. This recursion will terminate + * once the maximum amount of information is printed. + *

+ * In scenarios without a segfault handler, it can happen that this method reliably causes a + * subsequent error that crashes Native Image. In such a case, try to reduce the level of detail + * of the diagnostic output (see {@link SubstrateOptions#DiagnosticDetails} to get a reasonably + * complete diagnostic output. + */ + public static boolean printFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context registerContext, boolean frameHasCalleeSavedRegisters) { log.newline(); // 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, frameHasCalleeSavedRegisters) && !isInProgressByCurrentThread()) { + if (!fatalErrorState.trySet(log, sp, ip, registerContext, frameHasCalleeSavedRegisters) && !isFatalErrorHandlingThread()) { log.string("Error: printDiagnostics already in progress by another thread.").newline(); log.newline(); return false; } - int diagnosticLevel = SubstrateOptions.PrintDiagnosticDetails.getValue(); - printDiagnosticsForCurrentState(diagnosticLevel); + printFatalErrorForCurrentState(); 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(int diagnosticLevel) { - assert isInProgressByCurrentThread(); + private static void printFatalErrorForCurrentState() { + assert isFatalErrorHandlingThread(); - Log log = state.log; - if (state.diagnosticThunkIndex > 0) { + Log log = fatalErrorState.log; + if (fatalErrorState.diagnosticThunkIndex > 0) { // An error must have happened earlier as the code for printing diagnostics was invoked // recursively. log.resetIndentation(); @@ -159,24 +200,29 @@ private static void printDiagnosticsForCurrentState(int diagnosticLevel) { // Print the various sections of the diagnostics and skip all sections that were already // printed earlier. + ErrorContext errorContext = fatalErrorState.getErrorContext(); int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); - while (state.diagnosticThunkIndex < numDiagnosticThunks) { - DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(state.diagnosticThunkIndex); - while (++state.invocationCount <= thunk.baseInvocations()) { - try { - thunk.printDiagnostics(log, diagnosticLevel, state.invocationCount); - break; - } catch (Throwable e) { - dumpException(log, thunk, e); + while (fatalErrorState.diagnosticThunkIndex < numDiagnosticThunks) { + int index = fatalErrorState.diagnosticThunkIndex; + DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(index); + int diagnosticLevel = DiagnosticThunkRegister.getSingleton().getDefaultDiagnosticLevel(index); + if (diagnosticLevel != DiagnosticLevel.DISABLED) { + while (++fatalErrorState.invocationCount <= thunk.maxInvocations()) { + try { + thunk.printDiagnostics(log, errorContext, diagnosticLevel, fatalErrorState.invocationCount); + break; + } catch (Throwable e) { + dumpException(log, thunk, e); + } } } - state.diagnosticThunkIndex++; - state.invocationCount = 0; + fatalErrorState.diagnosticThunkIndex++; + fatalErrorState.invocationCount = 0; } // Reset the state so that another thread can print diagnostics. - state.clear(); + fatalErrorState.clear(); } static void dumpRuntimeCompilation(Log log) { @@ -189,7 +235,9 @@ static void dumpRuntimeCompilation(Log log) { log.newline(); try { - RuntimeCodeInfoMemory.singleton().printTable(log, DiagnosticLevel.MAX); + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(DiagnosticLevel.SAFE); + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(DiagnosticLevel.SAFE); + RuntimeCodeInfoMemory.singleton().printTable(log, true, false); } catch (Exception e) { dumpException(log, "DumpRuntimeCodeInfoMemory", e); } @@ -247,27 +295,97 @@ private static void logFrameAnchors(Log log, IsolateThread thread) { } } + public static void updateDiagnosticLevels(String thunks) { + int pos = 0; + int end; + while ((end = thunks.indexOf(',', pos)) >= 0) { + String entry = thunks.substring(pos, end); + updateDiagnosticLevel(entry); + pos = end + 1; + } + + String entry = thunks.substring(pos); + updateDiagnosticLevel(entry); + } + + private static void updateDiagnosticLevel(String entry) { + int pos = entry.indexOf(':'); + if (pos <= 0 || pos == entry.length() - 1) { + throw VMError.shouldNotReachHere("Invalid value specified for " + SubstrateOptions.DiagnosticDetails.getValue() + ": '" + entry + "'"); + } + + String pattern = entry.substring(0, pos); + int level = DiagnosticLevel.fromDiagnosticDetails(entry.substring(pos + 1)); + + int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + int matches = 0; + for (int i = 0; i < numDiagnosticThunks; i++) { + DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); + if (matches(thunk.getClass().getSimpleName(), pattern)) { + DiagnosticThunkRegister.getSingleton().setDiagnosticLevel(i, level); + matches++; + } + } + + VMError.guarantee(matches > 0, "The following pattern did not match any diagnostic thunk: '" + entry + "'"); + } + + private static boolean matches(String text, String pattern) { + assert pattern.length() > 0; + return matches(text, 0, pattern, 0); + } + + private static boolean matches(String text, int textPos, String pattern, int patternPos) { + while (textPos < text.length()) { + if (pattern.charAt(patternPos) == '*') { + while (!matches(text, textPos, pattern, patternPos + 1) && textPos < text.length()) { + textPos++; + } + return textPos < text.length(); + } else if (text.charAt(textPos) == pattern.charAt(patternPos)) { + textPos++; + patternPos++; + } else { + return false; + } + } + + while (patternPos < pattern.length() && pattern.charAt(patternPos) == '*') { + patternPos++; + } + return patternPos == pattern.length(); + } + private static class PrintDiagnosticsState { AtomicWord diagnosticThread = new AtomicWord<>(); volatile int diagnosticThunkIndex; volatile int invocationCount; Log log; - Pointer sp; - CodePointer ip; - RegisterDumper.Context context; - boolean frameHasCalleeSavedRegisters; + byte[] errorContextData; + + @Platforms(Platform.HOSTED_ONLY.class) + PrintDiagnosticsState() { + int errorContextSize = SizeOf.get(ErrorContext.class); + errorContextData = new byte[errorContextSize]; + } + + public ErrorContext getErrorContext() { + return NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(errorContextData), 0); + } @SuppressWarnings("hiding") - public boolean trySet(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context context, boolean frameHasCalleeSavedRegisters) { + public boolean trySet(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context registerContext, boolean frameHasCalleeSavedRegisters) { 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; - this.frameHasCalleeSavedRegisters = frameHasCalleeSavedRegisters; + + ErrorContext errorContext = getErrorContext(); + errorContext.setStackPointer(sp); + errorContext.setInstructionPointer(ip); + errorContext.setRegisterContext(registerContext); + errorContext.setFrameHasCalleeSavedRegisters(frameHasCalleeSavedRegisters); return true; } return false; @@ -275,10 +393,12 @@ public boolean trySet(Log log, Pointer sp, CodePointer ip, RegisterDumper.Contex public void clear() { log = null; - sp = WordFactory.nullPointer(); - ip = WordFactory.nullPointer(); - context = WordFactory.nullPointer(); - frameHasCalleeSavedRegisters = false; + + ErrorContext errorContext = getErrorContext(); + errorContext.setStackPointer(WordFactory.nullPointer()); + errorContext.setInstructionPointer(WordFactory.nullPointer()); + errorContext.setRegisterContext(WordFactory.nullPointer()); + errorContext.setFrameHasCalleeSavedRegisters(false); diagnosticThunkIndex = 0; invocationCount = 0; @@ -289,61 +409,51 @@ public void clear() { private static class DumpRegisters extends DiagnosticThunk { @Override - protected int baseInvocations() { - return 2; + public int maxInvocations() { + return 4; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) { - RegisterDumper.Context context = state.context; - boolean printLocationInfo = invocationCount < maxInvocations(diagnosticLevel); - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + boolean printLocationInfo = invocationCount < 4; + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; if (context.isNonNull()) { log.string("General purpose register values:").indent(true); - RegisterDumper.singleton().dumpRegisters(log, context, printLocationInfo, adjustedDiagnosticLevel); + RegisterDumper.singleton().dumpRegisters(log, context.getRegisterContext(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); log.indent(false); - } else if (CalleeSavedRegisters.supportedByPlatform() && state.frameHasCalleeSavedRegisters) { - CalleeSavedRegisters.singleton().dumpRegisters(log, state.sp, printLocationInfo, adjustedDiagnosticLevel); + } else if (CalleeSavedRegisters.supportedByPlatform() && context.frameHasCalleeSavedRegisters()) { + CalleeSavedRegisters.singleton().dumpRegisters(log, context.getStackPointer(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); } } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.JAVA_HEAP_ACCESS | DiagnosticLevel.UNSAFE_ACCESS; - } } private static class DumpInstructions extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (invocationCount < 3) { - printBytesBeforeAndAfterIp(log, invocationCount); + printBytesBeforeAndAfterIp(log, context.getInstructionPointer(), invocationCount); } else if (invocationCount == 3) { - printWord(log); + printWord(log, context.getInstructionPointer()); } } - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } - - private static void printBytesBeforeAndAfterIp(Log log, int invocationCount) { + private static void printBytesBeforeAndAfterIp(Log log, CodePointer ip, int invocationCount) { // print 64 or 32 instruction bytes. int bytesToPrint = 64 >> invocationCount; - hexDump(log, state.ip, bytesToPrint, bytesToPrint); + hexDump(log, ip, bytesToPrint, bytesToPrint); } - private static void printWord(Log log) { + private static void printWord(Log log, CodePointer ip) { // just print one word starting at the ip - hexDump(log, state.ip, 0, ConfigurationValues.getTarget().wordSize); + hexDump(log, ip, 0, ConfigurationValues.getTarget().wordSize); } private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesAfter) { @@ -355,14 +465,14 @@ private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesA private static class DumpTopOfCurrentThreadStack extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { - Pointer sp = state.sp; + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + Pointer sp = context.getStackPointer(); log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); UnsignedWord stackBase = VMThreads.StackBase.get(); @@ -371,7 +481,7 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) * 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(state.sp, 8, 16); + log.hexdump(sp, 8, 16); } else { int bytesToPrint = 512; UnsignedWord availableBytes = stackBase.subtract(sp); @@ -383,46 +493,36 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) } log.indent(false).newline(); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpDeoptStubPointer extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline(); } } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpTopFrame extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, 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; + Pointer sp = context.getStackPointer(); + CodePointer ip = context.getInstructionPointer(); log.string("Top frame info:").indent(true); if (sp.isNonNull() && ip.isNonNull()) { @@ -454,30 +554,26 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) } log.indent(false); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpThreads extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); - if (DiagnosticLevel.isUnsafeAccessAllowed(adjustedDiagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; + if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then it is unsafe to access thread locals of // another thread as the IsolateThread could be freed at any time. log.string("Threads:").indent(true); for (IsolateThread thread = VMThreads.firstThreadUnsafe(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) { log.zhex(thread).spaces(1).string(VMThreads.StatusSupport.getStatusString(thread)); - if (DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel)) { + if (allowJavaHeapAccess) { Thread threadObj = JavaThreads.fromVMThread(thread); log.string(" \"").string(threadObj.getName()).string("\" - ").object(threadObj); if (threadObj.isDaemon()) { @@ -490,25 +586,17 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) log.indent(false); } } - - @Override - protected int usedFeatures() { - if (VMOperation.isInProgressAtSafepoint()) { - return DiagnosticLevel.JAVA_HEAP_ACCESS; - } - return DiagnosticLevel.UNSAFE_ACCESS | DiagnosticLevel.JAVA_HEAP_ACCESS; - } } private static class DumpCurrentThreadLocals extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { IsolateThread currentThread = CurrentIsolate.getCurrentThread(); if (isThreadOnlyAttachedForCrashHandler(currentThread)) { if (invocationCount == 1) { @@ -516,168 +604,120 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) log.newline(); } } else { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); log.string("VM thread locals for the failing thread ").zhex(currentThread).string(":").indent(true); - VMThreadLocalInfos.dumpToLog(log, currentThread, adjustedDiagnosticLevel); + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + VMThreadLocalInfos.dumpToLog(log, currentThread, allowJavaHeapAccess); log.indent(false); } } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.JAVA_HEAP_ACCESS; - } } private static class DumpCurrentVMOperation extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); - boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); - VMOperationControl.printCurrentVMOperation(log, isJavaHeapAccessAllowed); + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + VMOperationControl.printCurrentVMOperation(log, allowJavaHeapAccess); log.newline(); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.JAVA_HEAP_ACCESS; - } } private static class DumpVMOperationHistory extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); - boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); - VMOperationControl.printRecentEvents(log, isJavaHeapAccessAllowed); - } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.JAVA_HEAP_ACCESS; + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + VMOperationControl.printRecentEvents(log, allowJavaHeapAccess); } } private static class DumpCodeCacheHistory extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); - boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); - RuntimeCodeInfoHistory.singleton().printRecentOperations(log, isJavaHeapAccessAllowed); + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + RuntimeCodeInfoHistory.singleton().printRecentOperations(log, allowJavaHeapAccess); } } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.JAVA_HEAP_ACCESS; - } } private static class DumpRuntimeCodeInfoMemory extends DiagnosticThunk { @Override - public int baseInvocations() { - return 1; + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { - int adjustedDiagnosticLevel = adjustDiagnosticLevel(diagnosticLevel, invocationCount); - boolean isJavaHeapAccessAllowed = DiagnosticLevel.isJavaHeapAccessAllowed(adjustedDiagnosticLevel); - boolean isUnsafeAccessAllowed = DiagnosticLevel.isUnsafeAccessAllowed(adjustedDiagnosticLevel); - RuntimeCodeInfoMemory.singleton().printTable(log, isJavaHeapAccessAllowed, isUnsafeAccessAllowed); - } - } - - @Override - protected int usedFeatures() { - // TEMP (chaeubl): completely decoupled from the use -> will get outdated soon! - if (VMOperation.isInProgressAtSafepoint()) { - return DiagnosticLevel.JAVA_HEAP_ACCESS; + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; + RuntimeCodeInfoMemory.singleton().printTable(log, allowJavaHeapAccess, allowUnsafeOperations); } - return DiagnosticLevel.UNSAFE_ACCESS | DiagnosticLevel.JAVA_HEAP_ACCESS; } } private static class DumpRecentDeoptimizations extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { if (DeoptimizationSupport.enabled()) { Deoptimizer.logRecentDeoptimizationEvents(log); } } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpCounters extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { log.string("Counters:").indent(true); Counter.logValues(); log.indent(false); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpCurrentThreadFrameAnchors extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, 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); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk { @@ -685,35 +725,30 @@ private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk Stage0StackFramePrintVisitor.SINGLETON}; @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { - Pointer sp = state.sp; - CodePointer ip = state.ip; + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + Pointer sp = context.getStackPointer(); + CodePointer ip = context.getInstructionPointer(); 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); } - - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } } private static class DumpOtherStackTraces extends DiagnosticThunk { @Override - public int baseInvocations() { + 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 diagnosticLevel, int invocationCount) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, 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). @@ -733,11 +768,6 @@ public void printDiagnostics(Log log, int diagnosticLevel, int invocationCount) } } - @Override - protected int usedFeatures() { - return DiagnosticLevel.ZERO; - } - private static void printFrameAnchors(Log log, IsolateThread vmThread) { log.string("Frame anchors:").indent(true); logFrameAnchors(log, vmThread); @@ -751,52 +781,105 @@ private static void printStackTrace(Log log, IsolateThread vmThread) { } } + public static class DiagnosticLevel { + // Individual bits. + private static final int JAVA_HEAP_ACCESS = 0b001; + private static final int UNSAFE_ACCESS = 0b010; + + // Predefined groups. + static final int DISABLED = 1 << 31; + private static final int MINIMAL = 0; + private static final int SAFE = JAVA_HEAP_ACCESS; + private static final int FULL = JAVA_HEAP_ACCESS | UNSAFE_ACCESS; + + public static boolean javaHeapAccessAllowed(int level) { + return level >= 1; + } + + public static boolean unsafeOperationsAllowed(int level) { + return level >= 2; + } + + public static int fromDiagnosticDetails(String details) { + switch (details) { + case "0": + return DISABLED; + case "1": + return MINIMAL; + case "2": + return SAFE; + case "3": + return FULL; + default: + throw new IllegalArgumentException(); + } + } + } + + @RawStructure + public interface ErrorContext extends PointerBase { + @RawField + Pointer getStackPointer(); + + @RawField + void setStackPointer(Pointer value); + + @RawField + CodePointer getInstructionPointer(); + + @RawField + void setInstructionPointer(CodePointer value); + + @RawField + RegisterDumper.Context getRegisterContext(); + + @RawField + void setRegisterContext(RegisterDumper.Context value); + + @RawField + boolean frameHasCalleeSavedRegisters(); + + @RawField + void setFrameHasCalleeSavedRegisters(boolean value); + } + + /** + * Subclasses of this class can implement printing of custom diagnostic information. All + * instances (of the subclasses) live in the image heap and may be used by multiple threads + * concurrently. + */ 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 #baseInvocations()} times. When the method is invoked for the - * first time, the argument invocationCount is 1. + * 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.") - public abstract void printDiagnostics(Log log, int diagnosticLevel, int invocationCount); + public abstract void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount); @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public int maxInvocations(int diagnosticLevel) { - int relevantDiagnostics = diagnosticLevel & usedFeatures(); - return Integer.bitCount(relevantDiagnostics) + baseInvocations(); - } - - protected abstract int baseInvocations(); - - protected abstract int usedFeatures(); - - protected int adjustDiagnosticLevel(int diagnosticLevel, int invocationCount) { - int result = diagnosticLevel & usedFeatures(); - for (int i = 1; i < invocationCount; i++) { - result ^= Integer.highestOneBit(result); - } - return result; - } + public abstract int maxInvocations(); } public static class DiagnosticThunkRegister { - DiagnosticThunk[] diagnosticThunks; + private DiagnosticThunk[] diagnosticThunks; + private int[] defaultDiagnosticLevel; /** * Get the register. - * + *

* This method is @Fold so anyone who uses it ensures there is a register. */ @Fold - /* { Checkstyle: allow synchronization. */ + /* Checkstyle: allow synchronization. */ public static synchronized DiagnosticThunkRegister getSingleton() { if (!ImageSingletons.contains(DiagnosticThunkRegister.class)) { ImageSingletons.add(DiagnosticThunkRegister.class, new DiagnosticThunkRegister()); } return ImageSingletons.lookup(DiagnosticThunkRegister.class); } - /* } Checkstyle: disallow synchronization. */ + /* Checkstyle: disallow synchronization. */ @Platforms(Platform.HOSTED_ONLY.class) DiagnosticThunkRegister() { @@ -804,17 +887,22 @@ public static synchronized DiagnosticThunkRegister getSingleton() { 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()}; + this.defaultDiagnosticLevel = new int[diagnosticThunks.length]; } - /** Register a diagnostic thunk to be called after a segfault. */ + /** + * Register a diagnostic thunk to be called after a segfault. + */ @Platforms(Platform.HOSTED_ONLY.class) - /* { Checkstyle: allow synchronization. */ + /* Checkstyle: allow synchronization. */ public synchronized void register(DiagnosticThunk diagnosticThunk) { - final DiagnosticThunk[] newArray = Arrays.copyOf(diagnosticThunks, diagnosticThunks.length + 1); - newArray[newArray.length - 1] = diagnosticThunk; - diagnosticThunks = newArray; + diagnosticThunks = Arrays.copyOf(diagnosticThunks, diagnosticThunks.length + 1); + diagnosticThunks[diagnosticThunks.length - 1] = diagnosticThunk; + + defaultDiagnosticLevel = Arrays.copyOf(defaultDiagnosticLevel, defaultDiagnosticLevel.length + 1); + defaultDiagnosticLevel[defaultDiagnosticLevel.length - 1] = DiagnosticLevel.FULL; } - /* } Checkstyle: disallow synchronization. */ + /* Checkstyle: disallow synchronization. */ @Fold int size() { @@ -824,20 +912,13 @@ int size() { DiagnosticThunk getThunk(int index) { return diagnosticThunks[index]; } - } - public static class DiagnosticLevel { - public static final int ZERO = 0; - public static final int JAVA_HEAP_ACCESS = 0b01; - public static final int UNSAFE_ACCESS = 0b10; - public static final int MAX = (UNSAFE_ACCESS << 1) - 1; - - public static boolean isJavaHeapAccessAllowed(int level) { - return level >= 1; + int getDefaultDiagnosticLevel(int index) { + return defaultDiagnosticLevel[index]; } - public static boolean isUnsafeAccessAllowed(int level) { - return level >= 2; + void setDiagnosticLevel(int index, int value) { + defaultDiagnosticLevel[index] = value; } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 93103cb58131..476507ed856c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -606,6 +606,13 @@ private static int getHostPageSize() { } } - @Option(help = "Determines how much diagnostic information is printed. Supported values range from 0 to 2, where 2 prints the largest amount of information.", type = Expert)// - public static final RuntimeOptionKey PrintDiagnosticDetails = new RuntimeOptionKey<>(2); + @Option(help = "Allows to specify which diagnostic level should be used for a certain diagnostic thunk, e.g.: 'DumpThreads:0,DumpRegisters:2'." + + " The diagnostic levels range from 0 (no information) to 3 (detailed information). By default, the most detailed output is selected for all diagnostic thunks.", type = Expert)// + public static final RuntimeOptionKey DiagnosticDetails = new RuntimeOptionKey("") { + @Override + protected void onValueUpdate(EconomicMap, Object> values, String oldValue, String newValue) { + SubstrateDiagnostics.updateDiagnosticLevels(newValue); + super.onValueUpdate(values, oldValue, newValue); + } + }; } 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 960106732c0a..2f5ff9018b2c 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 @@ -170,7 +170,7 @@ private static void dumpInterruptibly(PointerBase signalInfo, RegisterDumper.Con PointerBase sp = RegisterDumper.singleton().getSP(context); PointerBase ip = RegisterDumper.singleton().getIP(context); - boolean printedDiagnostics = SubstrateDiagnostics.printForFatalError(log, (Pointer) sp, (CodePointer) ip, context, false); + boolean printedDiagnostics = SubstrateDiagnostics.printFatalError(log, (Pointer) sp, (CodePointer) ip, context, false); if (printedDiagnostics) { log.string("Segfault detected, aborting process. Use runtime option -R:-InstallSegfaultHandler if you don't want to use SubstrateSegfaultHandler.").newline(); log.newline(); 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 7259e2aed29d..2a93426d72e1 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 @@ -254,7 +254,7 @@ public interface Thunk { /** Prints extensive diagnostic information to the given Log. */ public static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip) { - return SubstrateDiagnostics.printForFatalError(log, sp, ip, WordFactory.nullPointer(), false); + return SubstrateDiagnostics.printFatalError(log, sp, ip, WordFactory.nullPointer(), false); } /** 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 c37d0c19c29c..4d768ce99b47 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 @@ -246,8 +246,8 @@ private static int nextIndex(int index, int length) { return (index + 1 < length) ? (index + 1) : 0; } - public void printTable(Log log, boolean allowJavaHeapAccess, boolean allowUnsafeAccess) { - if (allowUnsafeAccess || VMOperation.isInProgressAtSafepoint()) { + public void printTable(Log log, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { + if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then the table could be freed at any time. log.string("RuntimeCodeInfoMemory contains ").signed(count).string(" methods:").indent(true); if (table.isNonNull()) { @@ -301,14 +301,13 @@ public void tearDown() { } @Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.") - public boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { - if (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess, boolean allowUnsafeOperations) { + if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then 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()) { - boolean allowJavaHeapAccess = DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel); if (info.equal(value)) { String name = allowJavaHeapAccess ? UntetheredCodeInfoAccess.getName(info) : null; printIsCodeInfoObject(log, name); 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 dcafe4dfb9d7..59a2cdb4828a 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 @@ -216,5 +216,5 @@ public List> getLoadedClasses() { * 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, int diagnosticLevel); + public abstract boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaHeapAccess, boolean allowUnsafeOperations); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java index 921111908dd1..c8dc6da515a5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java @@ -131,7 +131,7 @@ private static void doShutdown(CodePointer callerIP, String msg, Throwable ex) { log.newline(); } - SubstrateDiagnostics.printForFatalError(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); + SubstrateDiagnostics.printFatalError(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); /* * Print the error message again, so that the most important bit of information 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 index ffb2f2c6cba5..a064b67e88aa 100644 --- 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 @@ -24,9 +24,11 @@ */ package com.oracle.svm.core.locks; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.log.Log; @@ -46,13 +48,13 @@ public abstract class VMLockSupport { public static class DumpVMMutexes extends DiagnosticThunk { @Override - public int baseInvocations() { + 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) { + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { log.string("VM mutexes:").indent(true); VMLockSupport support = null; 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 2db0c9bf73cc..3c9c511c6984 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 @@ -133,7 +133,7 @@ public static void deactivateImplicitExceptionsAreFatal() { } private static void vmErrorIfImplicitExceptionsAreFatal() { - if ((implicitExceptionsAreFatal.get() > 0 || ExceptionUnwind.exceptionsAreFatal()) && !SubstrateDiagnostics.isInProgressByCurrentThread()) { + if ((implicitExceptionsAreFatal.get() > 0 || ExceptionUnwind.exceptionsAreFatal()) && !SubstrateDiagnostics.isFatalErrorHandlingThread()) { 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/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index 322e95625fc3..5dc4db252989 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 @@ -575,14 +575,14 @@ public static boolean ownsThreadMutex() { return THREAD_MUTEX.isOwner(); } - public static boolean printLocationInfo(Log log, UnsignedWord value, int diagnosticLevel) { + public static boolean printLocationInfo(Log log, UnsignedWord value, boolean allowUnsafeOperations) { for (IsolateThread thread = firstThreadUnsafe(); thread.isNonNull(); thread = nextThread(thread)) { if (thread.equal(value)) { log.string("is a thread"); return true; } - if (DiagnosticLevel.isUnsafeAccessAllowed(diagnosticLevel) || VMOperation.isInProgressAtSafepoint()) { + if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then it is unsafe to access thread locals of // another thread is unsafe as the IsolateThread could be freed at any time. UnsignedWord stackBase = StackBase.get(thread); 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 9a0cab7e49b5..e2a4b784723d 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 @@ -61,7 +61,7 @@ public static boolean setInfos(Collection infos) { } } - public static void dumpToLog(Log log, IsolateThread thread, int diagnosticLevel) { + public static void dumpToLog(Log log, IsolateThread thread, boolean isJavaHeapAccessAllowed) { 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) { @@ -74,7 +74,7 @@ public static void dumpToLog(Log log, IsolateThread thread, int diagnosticLevel) WordBase value = primitiveData(thread).readWord(WordFactory.signed(info.offset)); log.string("(Word) ").signed(value).string(" (").zhex(value.rawValue()).string(")"); } else if (info.threadLocalClass == FastThreadLocalObject.class) { - if (DiagnosticLevel.isJavaHeapAccessAllowed(diagnosticLevel)) { + if (isJavaHeapAccessAllowed) { Object value = ObjectAccess.readObject(objectData(thread), WordFactory.signed(info.offset)); log.string("(Object) "); if (value == null) { diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctions.java index b5f1bf18000c..ac3e41004243 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctions.java @@ -833,7 +833,7 @@ static void FatalError(JNIEnvironment env, CCharPointer message) { try { log.string("Fatal error reported via JNI: ").string(message).newline(); VMThreads.StatusSupport.setStatusIgnoreSafepoints(); - SubstrateDiagnostics.print(log, KnownIntrinsics.readCallerStackPointer(), callerIP); + SubstrateDiagnostics.printFatalError(log, KnownIntrinsics.readCallerStackPointer(), callerIP); } catch (Throwable ignored) { /* * Ignore exceptions reported during error reporting, we are going to exit anyway. From b5fba2e9ad73c0003586c99b9fa6ed924719057e Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Wed, 15 Sep 2021 10:16:20 +0200 Subject: [PATCH 3/6] Various fixes. --- .../genscavenge/GreyToBlackObjectVisitor.java | 5 +- .../oracle/svm/core/genscavenge/HeapImpl.java | 4 +- .../genscavenge/ThreadLocalAllocation.java | 4 +- .../svm/core/genscavenge/YoungGeneration.java | 1 + .../oracle/svm/core/SubstrateDiagnostics.java | 222 ++++++++++-------- .../com/oracle/svm/core/SubstrateOptions.java | 7 +- .../svm/core/code/RuntimeCodeInfoMemory.java | 1 - .../oracle/svm/core/locks/VMLockSupport.java | 5 +- .../svm/core/thread/VMOperationControl.java | 5 +- .../com/oracle/svm/core/thread/VMThreads.java | 2 - .../core/threadlocal/VMThreadLocalInfos.java | 1 - .../hosted/SubstrateDiagnosticFeature.java | 68 ++++++ 12 files changed, 212 insertions(+), 113 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java 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 1055262f4659..9a286ae1b26a 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 @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.genscavenge; -import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.word.Word; @@ -34,9 +33,9 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RestrictHeapAccess; @@ -134,7 +133,7 @@ public void noteObject(Object o) { } @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } 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 cb39a375bdd7..c9aaa0c3770e 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 @@ -787,7 +787,7 @@ private static Descriptor getTlabUnsafe(IsolateThread thread) { private static class DumpHeapSettingsAndStatistics extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -813,7 +813,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpChunkInformation extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } 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 663df156da27..40219c028e52 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 @@ -112,7 +112,7 @@ public interface Descriptor extends PointerBase { * Don't read this value directly, use the {@link Uninterruptible} accessor methods instead. * This is necessary to avoid races between the GC and code that accesses or modifies the TLAB. */ - private static final FastThreadLocalBytes regularTLAB = FastThreadLocalFactory.createBytes(ThreadLocalAllocation::getRegularTLABSize).setMaxOffset(FastThreadLocal.BYTE_OFFSET); + private static final FastThreadLocalBytes regularTLAB = FastThreadLocalFactory.createBytes(ThreadLocalAllocation::getTlabDescriptorSize).setMaxOffset(FastThreadLocal.BYTE_OFFSET); private ThreadLocalAllocation() { } @@ -123,7 +123,7 @@ static Log log() { } @Platforms(Platform.HOSTED_ONLY.class) - private static int getRegularTLABSize() { + private static int getTlabDescriptorSize() { return SizeOf.get(Descriptor.class); } 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 5bb3ac7b2acf..bfc13bd95599 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 @@ -62,6 +62,7 @@ public final class YoungGeneration extends Generation { } } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getMaxSurvivorSpaces() { return maxSurvivorSpaces; } 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 f2750b30568a..6904d3d65d0d 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 @@ -77,11 +77,9 @@ import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.util.Counter; -import com.oracle.svm.core.util.VMError; public class SubstrateDiagnostics { private static final FastThreadLocalBytes threadOnlyAttachedForCrashHandler = FastThreadLocalFactory.createBytes(() -> 1); - private static final PrintDiagnosticsState fatalErrorState = new PrintDiagnosticsState(); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void setOnlyAttachedForCrashHandler(IsolateThread thread) { @@ -92,20 +90,25 @@ private static boolean isThreadOnlyAttachedForCrashHandler(IsolateThread thread) return threadOnlyAttachedForCrashHandler.getAddress(thread).read() != 0; } + @Fold + static FatalErrorState fatalErrorState() { + return ImageSingletons.lookup(FatalErrorState.class); + } + public static boolean isFatalErrorHandlingInProgress() { - return fatalErrorState.diagnosticThread.get().isNonNull(); + return fatalErrorState().diagnosticThread.get().isNonNull(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isFatalErrorHandlingThread() { - return fatalErrorState.diagnosticThread.get() == CurrentIsolate.getCurrentThread(); + return fatalErrorState().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(); + result += thunks.getThunk(i).maxInvocationCount(); } return result; } @@ -144,9 +147,9 @@ public static void printInformation(Log log, Pointer sp, CodePointer ip, Registe int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); for (int i = 0; i < numDiagnosticThunks; i++) { DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); - int diagnosticLevel = DiagnosticThunkRegister.getSingleton().getDefaultDiagnosticLevel(i) & DiagnosticLevel.SAFE; - if (diagnosticLevel != DiagnosticLevel.DISABLED) { - thunk.printDiagnostics(log, errorContext, diagnosticLevel, 1); + int invocationCount = DiagnosticThunkRegister.getSingleton().getInitialInvocationCount(i); + if (invocationCount <= thunk.maxInvocationCount()) { + thunk.printDiagnostics(log, errorContext, DiagnosticLevel.SAFE, invocationCount); } } } @@ -177,7 +180,7 @@ public static boolean printFatalError(Log log, Pointer sp, CodePointer ip, Regis log.newline(); // Save the state of the initial error so that this state is consistently used, even if // further errors occur while printing diagnostics. - if (!fatalErrorState.trySet(log, sp, ip, registerContext, frameHasCalleeSavedRegisters) && !isFatalErrorHandlingThread()) { + if (!fatalErrorState().trySet(log, sp, ip, registerContext, frameHasCalleeSavedRegisters) && !isFatalErrorHandlingThread()) { log.string("Error: printDiagnostics already in progress by another thread.").newline(); log.newline(); return false; @@ -191,6 +194,7 @@ public static boolean printFatalError(Log log, Pointer sp, CodePointer ip, Regis private static void printFatalErrorForCurrentState() { assert isFatalErrorHandlingThread(); + FatalErrorState fatalErrorState = fatalErrorState(); Log log = fatalErrorState.log; if (fatalErrorState.diagnosticThunkIndex > 0) { // An error must have happened earlier as the code for printing diagnostics was invoked @@ -205,15 +209,18 @@ private static void printFatalErrorForCurrentState() { while (fatalErrorState.diagnosticThunkIndex < numDiagnosticThunks) { int index = fatalErrorState.diagnosticThunkIndex; DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(index); - int diagnosticLevel = DiagnosticThunkRegister.getSingleton().getDefaultDiagnosticLevel(index); - if (diagnosticLevel != DiagnosticLevel.DISABLED) { - while (++fatalErrorState.invocationCount <= thunk.maxInvocations()) { - try { - thunk.printDiagnostics(log, errorContext, diagnosticLevel, fatalErrorState.invocationCount); - break; - } catch (Throwable e) { - dumpException(log, thunk, e); - } + + // Start at the configured initial invocation count. + if (fatalErrorState.invocationCount == 0) { + fatalErrorState.invocationCount = DiagnosticThunkRegister.getSingleton().getInitialInvocationCount(index) - 1; + } + + while (++fatalErrorState.invocationCount <= thunk.maxInvocationCount()) { + try { + thunk.printDiagnostics(log, errorContext, DiagnosticLevel.FULL, fatalErrorState.invocationCount); + break; + } catch (Throwable e) { + dumpException(log, thunk, e); } } @@ -221,7 +228,7 @@ private static void printFatalErrorForCurrentState() { fatalErrorState.invocationCount = 0; } - // Reset the state so that another thread can print diagnostics. + // Reset the state so that another thread can print diagnostics for a fatal error. fatalErrorState.clear(); } @@ -237,7 +244,7 @@ static void dumpRuntimeCompilation(Log log) { try { boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(DiagnosticLevel.SAFE); boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(DiagnosticLevel.SAFE); - RuntimeCodeInfoMemory.singleton().printTable(log, true, false); + RuntimeCodeInfoMemory.singleton().printTable(log, allowJavaHeapAccess, allowUnsafeOperations); } catch (Exception e) { dumpException(log, "DumpRuntimeCodeInfoMemory", e); } @@ -295,39 +302,56 @@ private static void logFrameAnchors(Log log, IsolateThread thread) { } } - public static void updateDiagnosticLevels(String thunks) { + public static void updateInitialInvocationCounts(String configuration) throws IllegalArgumentException { int pos = 0; int end; - while ((end = thunks.indexOf(',', pos)) >= 0) { - String entry = thunks.substring(pos, end); - updateDiagnosticLevel(entry); + while ((end = configuration.indexOf(',', pos)) >= 0) { + String entry = configuration.substring(pos, end); + updateInitialInvocationCount(entry); pos = end + 1; } - String entry = thunks.substring(pos); - updateDiagnosticLevel(entry); + String entry = configuration.substring(pos); + updateInitialInvocationCount(entry); } - private static void updateDiagnosticLevel(String entry) { + private static void updateInitialInvocationCount(String entry) throws IllegalArgumentException { int pos = entry.indexOf(':'); if (pos <= 0 || pos == entry.length() - 1) { - throw VMError.shouldNotReachHere("Invalid value specified for " + SubstrateOptions.DiagnosticDetails.getValue() + ": '" + entry + "'"); + throw new IllegalArgumentException("'" + entry + "' has an invalid format."); } String pattern = entry.substring(0, pos); - int level = DiagnosticLevel.fromDiagnosticDetails(entry.substring(pos + 1)); + int initialInvocationCount = parseInvocationCount(entry, pos); - int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); int matches = 0; + int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); for (int i = 0; i < numDiagnosticThunks; i++) { DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); + // Checkstyle: allow Class.getSimpleName if (matches(thunk.getClass().getSimpleName(), pattern)) { - DiagnosticThunkRegister.getSingleton().setDiagnosticLevel(i, level); + // Checkstyle: disallow Class.getSimpleName + DiagnosticThunkRegister.getSingleton().setInitialInvocationCount(i, initialInvocationCount); matches++; } } - VMError.guarantee(matches > 0, "The following pattern did not match any diagnostic thunk: '" + entry + "'"); + if (matches == 0) { + throw new IllegalArgumentException("the pattern '" + entry + "' not match any diagnostic thunk."); + } + } + + private static int parseInvocationCount(String entry, int pos) { + int initialInvocationCount = 0; + try { + initialInvocationCount = Integer.parseInt(entry.substring(pos + 1)); + } catch (NumberFormatException e) { + } + + if (initialInvocationCount < 1) { + throw new IllegalArgumentException("'" + entry + "' does not specify an integer value >= 1."); + } + return initialInvocationCount; } private static boolean matches(String text, String pattern) { @@ -335,13 +359,25 @@ private static boolean matches(String text, String pattern) { return matches(text, 0, pattern, 0); } - private static boolean matches(String text, int textPos, String pattern, int patternPos) { + private static boolean matches(String text, int t, String pattern, int p) { + int textPos = t; + int patternPos = p; while (textPos < text.length()) { - if (pattern.charAt(patternPos) == '*') { - while (!matches(text, textPos, pattern, patternPos + 1) && textPos < text.length()) { + if (patternPos >= pattern.length()) { + return false; + } else if (pattern.charAt(patternPos) == '*') { + // Wildcard at the end of the pattern matches everything. + if (patternPos + 1 >= pattern.length()) { + return true; + } + + while (textPos < text.length()) { + if (matches(text, textPos, pattern, patternPos + 1)) { + return true; + } textPos++; } - return textPos < text.length(); + return false; } else if (text.charAt(textPos) == pattern.charAt(patternPos)) { textPos++; patternPos++; @@ -350,22 +386,27 @@ private static boolean matches(String text, int textPos, String pattern, int pat } } + // Filter wildcards at the end of the pattern in case we ran out of text. while (patternPos < pattern.length() && pattern.charAt(patternPos) == '*') { patternPos++; } return patternPos == pattern.length(); } - private static class PrintDiagnosticsState { - AtomicWord diagnosticThread = new AtomicWord<>(); + public static class FatalErrorState { + AtomicWord diagnosticThread; volatile int diagnosticThunkIndex; volatile int invocationCount; - Log log; - byte[] errorContextData; + private final byte[] errorContextData; @Platforms(Platform.HOSTED_ONLY.class) - PrintDiagnosticsState() { + public FatalErrorState() { + diagnosticThread = new AtomicWord<>(); + diagnosticThunkIndex = 0; + invocationCount = 0; + log = null; + int errorContextSize = SizeOf.get(ErrorContext.class); errorContextData = new byte[errorContextSize]; } @@ -409,7 +450,7 @@ public void clear() { private static class DumpRegisters extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 4; } @@ -419,7 +460,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev boolean printLocationInfo = invocationCount < 4; boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; - if (context.isNonNull()) { + if (context.getRegisterContext().isNonNull()) { log.string("General purpose register values:").indent(true); RegisterDumper.singleton().dumpRegisters(log, context.getRegisterContext(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations); log.indent(false); @@ -431,7 +472,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpInstructions extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 3; } @@ -465,7 +506,7 @@ private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesA private static class DumpTopOfCurrentThreadStack extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -497,7 +538,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpDeoptStubPointer extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -512,7 +553,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpTopFrame extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -558,7 +599,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpThreads extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 3; } @@ -590,7 +631,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpCurrentThreadLocals extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 2; } @@ -614,7 +655,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpCurrentVMOperation extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 2; } @@ -629,7 +670,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpVMOperationHistory extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 2; } @@ -643,7 +684,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpCodeCacheHistory extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 2; } @@ -659,7 +700,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpRuntimeCodeInfoMemory extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 3; } @@ -676,7 +717,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpRecentDeoptimizations extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -691,7 +732,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpCounters extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -706,7 +747,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpCurrentThreadFrameAnchors extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -725,7 +766,7 @@ private static class DumpCurrentThreadDecodedStackTrace extends DiagnosticThunk Stage0StackFramePrintVisitor.SINGLETON}; @Override - public int maxInvocations() { + public int maxInvocationCount() { return 3; } @@ -742,7 +783,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpOtherStackTraces extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } @@ -787,32 +828,15 @@ public static class DiagnosticLevel { private static final int UNSAFE_ACCESS = 0b010; // Predefined groups. - static final int DISABLED = 1 << 31; - private static final int MINIMAL = 0; private static final int SAFE = JAVA_HEAP_ACCESS; private static final int FULL = JAVA_HEAP_ACCESS | UNSAFE_ACCESS; public static boolean javaHeapAccessAllowed(int level) { - return level >= 1; + return (level & JAVA_HEAP_ACCESS) != 0; } public static boolean unsafeOperationsAllowed(int level) { - return level >= 2; - } - - public static int fromDiagnosticDetails(String details) { - switch (details) { - case "0": - return DISABLED; - case "1": - return MINIMAL; - case "2": - return SAFE; - case "3": - return FULL; - default: - throw new IllegalArgumentException(); - } + return (level & UNSAFE_ACCESS) != 0; } } @@ -844,27 +868,37 @@ public interface ErrorContext extends PointerBase { } /** - * Subclasses of this class can implement printing of custom diagnostic information. All - * instances (of the subclasses) live in the image heap and may be used by multiple threads - * concurrently. + * Can be used to implement printing of custom diagnostic information. Instances are singletons + * that live in the image heap. The same instance can be used by multiple threads concurrently. */ 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. + * Prints diagnostic information. This method may be invoked by multiple threads + * concurrently. + * + * If an error (e.g., exception or segfault) occurs while executing this method, then the + * same thread may execute this method multiple times sequentially. The parameter + * {@code invocationCount} is incremented for each sequential invocation. A typical + * implementation of {@link #printDiagnostics} will reduce the amount of diagnostic output + * when the {@code invocationCount} increases. This also reduces the risk of errors and + * makes it more likely that the method finishes executing successfully. + * + * @param log the output to which the diagnostics should be printed. + * @param context contextual data for the error, e.g., register information. + * @param maxDiagnosticLevel specifies which kind of operations the diagnostic thunk may + * perform, see {@link DiagnosticLevel}. + * @param invocationCount this value is >= 1 and <= {@link #maxInvocationCount()}). */ @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") public abstract void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount); @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") - public abstract int maxInvocations(); + public abstract int maxInvocationCount(); } public static class DiagnosticThunkRegister { private DiagnosticThunk[] diagnosticThunks; - private int[] defaultDiagnosticLevel; + private int[] initialInvocationCount; /** * Get the register. @@ -887,7 +921,9 @@ public static synchronized DiagnosticThunkRegister getSingleton() { 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()}; - this.defaultDiagnosticLevel = new int[diagnosticThunks.length]; + + this.initialInvocationCount = new int[diagnosticThunks.length]; + Arrays.fill(initialInvocationCount, 1); } /** @@ -899,8 +935,8 @@ public synchronized void register(DiagnosticThunk diagnosticThunk) { diagnosticThunks = Arrays.copyOf(diagnosticThunks, diagnosticThunks.length + 1); diagnosticThunks[diagnosticThunks.length - 1] = diagnosticThunk; - defaultDiagnosticLevel = Arrays.copyOf(defaultDiagnosticLevel, defaultDiagnosticLevel.length + 1); - defaultDiagnosticLevel[defaultDiagnosticLevel.length - 1] = DiagnosticLevel.FULL; + initialInvocationCount = Arrays.copyOf(initialInvocationCount, initialInvocationCount.length + 1); + initialInvocationCount[initialInvocationCount.length - 1] = 1; } /* Checkstyle: disallow synchronization. */ @@ -913,12 +949,12 @@ DiagnosticThunk getThunk(int index) { return diagnosticThunks[index]; } - int getDefaultDiagnosticLevel(int index) { - return defaultDiagnosticLevel[index]; + int getInitialInvocationCount(int index) { + return initialInvocationCount[index]; } - void setDiagnosticLevel(int index, int value) { - defaultDiagnosticLevel[index] = value; + void setInitialInvocationCount(int index, int value) { + initialInvocationCount[index] = value; } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 476507ed856c..8d443bd590a8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -606,13 +606,14 @@ private static int getHostPageSize() { } } - @Option(help = "Allows to specify which diagnostic level should be used for a certain diagnostic thunk, e.g.: 'DumpThreads:0,DumpRegisters:2'." + - " The diagnostic levels range from 0 (no information) to 3 (detailed information). By default, the most detailed output is selected for all diagnostic thunks.", type = Expert)// + @Option(help = "Allows to specify how many details should be printed for certain diagnostic thunk, e.g.: 'DumpThreads:1,DumpRegisters:2'. " + + "A value of 1 will result in the maximum amount of information, higher values will result in less information. " + + "By default, the most detailed output is enabled for all diagnostic thunks. Wildcards (*) are supported in the name of the diagnostic thunk.", type = Expert)// public static final RuntimeOptionKey DiagnosticDetails = new RuntimeOptionKey("") { @Override protected void onValueUpdate(EconomicMap, Object> values, String oldValue, String newValue) { - SubstrateDiagnostics.updateDiagnosticLevels(newValue); super.onValueUpdate(values, oldValue, newValue); + SubstrateDiagnostics.updateInitialInvocationCounts(newValue); } }; } 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 4d768ce99b47..fd6470cd5557 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 @@ -34,7 +34,6 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; 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 index a064b67e88aa..84a126c6d523 100644 --- 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 @@ -24,12 +24,11 @@ */ package com.oracle.svm.core.locks; -import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; -import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.log.Log; @@ -48,7 +47,7 @@ public abstract class VMLockSupport { public static class DumpVMMutexes extends DiagnosticThunk { @Override - public int maxInvocations() { + public int maxInvocationCount() { return 1; } 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 808da87ccd2e..c1b7e49f5701 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 @@ -40,7 +40,6 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.NeverInline; @@ -194,7 +193,7 @@ public static boolean mayExecuteVmOperations() { } } - public static void printCurrentVMOperation(Log log, boolean isJavaHeapAccessAllowed) { + 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. @@ -203,7 +202,7 @@ public static void printCurrentVMOperation(Log log, boolean isJavaHeapAccessAllo VMOperation op = control.inProgress.operation; if (op == null) { log.string("No VMOperation in progress").newline(); - } else if (isJavaHeapAccessAllowed) { + } 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(); 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 5dc4db252989..42cefd033828 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 @@ -44,8 +44,6 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateSegfaultHandler; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; 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 e2a4b784723d..1a671ad04cdb 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 @@ -39,7 +39,6 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticLevel; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.heap.ReferenceAccess; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java new file mode 100644 index 000000000000..25180d2dc516 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java @@ -0,0 +1,68 @@ +/* + * 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.hosted; + +import java.lang.reflect.Field; + +import com.oracle.svm.core.SubstrateDiagnostics; +import com.oracle.svm.core.SubstrateDiagnostics.FatalErrorState; +import com.sun.tools.javac.util.FatalError; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; + +@AutomaticFeature +class SubstrateDiagnosticFeature implements Feature { + @Override + public void duringSetup(DuringSetupAccess access) { + ImageSingletons.add(FatalErrorState.class, new FatalErrorState()); + + // Ensure that the diagnostic thunks are initialized. + DiagnosticThunkRegister.getSingleton(); + } + + @Override + public void beforeAnalysis(Feature.BeforeAnalysisAccess access) { + // Explicitly mark the option as used so that it is possible to specify a value at runtime. + BeforeAnalysisAccessImpl accessImpl = (BeforeAnalysisAccessImpl) access; + registerOptionAsRead(accessImpl, SubstrateOptions.class, SubstrateOptions.DiagnosticDetails.getName()); + } + + private static void registerOptionAsRead(BeforeAnalysisAccessImpl accessImpl, Class clazz, String fieldName) { + try { + Field javaField = clazz.getField(fieldName); + AnalysisField analysisField = accessImpl.getMetaAccess().lookupJavaField(javaField); + accessImpl.registerAsRead(analysisField); + } catch (NoSuchFieldException | SecurityException e) { + throw VMError.shouldNotReachHere(e); + } + } +} From 45a67d3bb50fe84af4ed54c9e387232000b207eb Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Wed, 15 Sep 2021 14:58:20 +0200 Subject: [PATCH 4/6] Fix issue that counters are printed to wrong log. --- .../oracle/svm/core/SubstrateDiagnostics.java | 2 +- .../src/com/oracle/svm/core/util/Counter.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) 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 6904d3d65d0d..eaef087e7a9c 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 @@ -740,7 +740,7 @@ public int maxInvocationCount() { @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { log.string("Counters:").indent(true); - Counter.logValues(); + Counter.logValues(log); log.indent(false); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/Counter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/Counter.java index 052bc7c51fd9..259f7525225a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/Counter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/Counter.java @@ -50,7 +50,7 @@ * option is enabled. Counters are {@link Group grouped} for printing. * * Currently there is no shutdown hook in Substrate VM that is invoked automatically, so - * {@link Counter#logValues()} needs to be called manually at the end of the application to print + * {@link Counter#logValues} needs to be called manually at the end of the application to print * counter values. * * Use this class in the following way: @@ -112,7 +112,7 @@ public void reset() { /** * Prints all counters of this group to the {@link Log}. */ - public void logValues() { + public void logValues(Log log) { long total = 0; int maxNameLen = 0; for (Counter counter : counters) { @@ -120,19 +120,19 @@ public void logValues() { maxNameLen = Math.max(counter.name.length(), maxNameLen); } - Log.log().string("=== ").string(name).string(" ===").newline(); + log.string("=== ").string(name).string(" ===").newline(); for (Counter counter : counters) { long counterValue = counter.getValue(); long percent = total == 0 ? 0 : counterValue * 100 / total; - Log.log().string(" ").string(counter.name, maxNameLen, Log.RIGHT_ALIGN).string(":"); - Log.log().unsigned(counterValue, 10, Log.RIGHT_ALIGN).unsigned(percent, 5, Log.RIGHT_ALIGN).string("%"); + log.string(" ").string(counter.name, maxNameLen, Log.RIGHT_ALIGN).string(":"); + log.unsigned(counterValue, 10, Log.RIGHT_ALIGN).unsigned(percent, 5, Log.RIGHT_ALIGN).string("%"); if (!counter.description.isEmpty()) { - Log.log().string(" // ").string(counter.description); + log.string(" // ").string(counter.description); } - Log.log().newline(); + log.newline(); } - Log.log().string(" ").string("TOTAL", maxNameLen, Log.RIGHT_ALIGN).string(":"); - Log.log().unsigned(total, 10, Log.RIGHT_ALIGN).newline(); + log.string(" ").string("TOTAL", maxNameLen, Log.RIGHT_ALIGN).string(":"); + log.unsigned(total, 10, Log.RIGHT_ALIGN).newline(); } } @@ -212,9 +212,9 @@ public void reset() { /** * Prints all counters of all enabled groups to the {@link Log}. */ - public static void logValues() { + public static void logValues(Log log) { for (Counter.Group group : ImageSingletons.lookup(CounterSupport.class).enabledGroups) { - group.logValues(); + group.logValues(log); } } } From ed622d4d5191ccfa7b2e9276b435a5cc11587369 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Thu, 16 Sep 2021 12:09:04 +0200 Subject: [PATCH 5/6] Further minor fixes. --- .../oracle/svm/core/genscavenge/GCImpl.java | 2 +- .../com/oracle/svm/core/JavaMainWrapper.java | 3 +- .../oracle/svm/core/SubstrateDiagnostics.java | 44 ++++++++++--------- .../com/oracle/svm/core/SubstrateOptions.java | 4 +- .../com/oracle/svm/core/SubstrateUtil.java | 2 +- .../com/oracle/svm/core/thread/VMThreads.java | 2 +- .../hosted/SubstrateDiagnosticFeature.java | 4 +- 7 files changed, 31 insertions(+), 30 deletions(-) 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 74042cb35638..8d8c7ff1fe6c 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 @@ -1065,7 +1065,7 @@ UnsignedWord possibleCollectionPrologue() { } /** - * Do whatever is necessary if a collection occurred since the a call to + * Do whatever is necessary if a collection occurred since the call to * {@link #possibleCollectionPrologue()}. Note that this method may get called by several * threads for the same collection. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java index bc4516603465..8a10aa7acef5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.List; +import com.oracle.svm.core.log.Log; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -170,7 +171,7 @@ public static int runCore() { */ RuntimeSupport.getRuntimeSupport().shutdown(); - Counter.logValues(); + Counter.logValues(Log.log()); } return exitCode; } 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 eaef087e7a9c..cc249cc4dd33 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 @@ -130,10 +130,8 @@ public static void printInformation(Log log, Pointer sp, CodePointer ip) { } /** - * This method prints diagnostic information. This method prints less information than - * {@link #printFatalError} as it only uses reasonably safe operations and memory accesses. So, - * as long as all parts of Native Image are fully functional, calling this method will never - * cause a crash. + * Prints less detailed information than {@link #printFatalError} but this method guarantees + * that it won't cause a crash if all parts of Native Image are fully functional. */ public static void printInformation(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context registerContext, boolean frameHasCalleeSavedRegisters) { // Stack allocate an error context. @@ -162,18 +160,17 @@ public static boolean printFatalError(Log log, Pointer sp, CodePointer ip) { } /** - * This method prints extensive diagnostic information and is only called for fatal errors. This - * method may use operations and memory accesses that are not necessarily 100% safe. So, even if - * all parts of Native Image are fully functional, this method may cause crashes. + * Used to print extensive diagnostic information in case of a fatal error. This method may use + * operations and memory accesses that are not necessarily 100% safe. So, even if all parts of + * Native Image are fully functional, this method may cause crashes. *

- * If a segfault handler is present, then this method will be called recursively multiple times + * If a segfault handler is present, then this method may be called recursively multiple times * if further errors happen while printing diagnostics. On each recursive invocation, the level - * of detail of the diagnostic output will be reduced gradually. This recursion will terminate - * once the maximum amount of information is printed. + * of detail of the diagnostic output will be reduced gradually. *

* In scenarios without a segfault handler, it can happen that this method reliably causes a * subsequent error that crashes Native Image. In such a case, try to reduce the level of detail - * of the diagnostic output (see {@link SubstrateOptions#DiagnosticDetails} to get a reasonably + * of the diagnostic output (see {@link SubstrateOptions#DiagnosticDetails}) to get a reasonably * complete diagnostic output. */ public static boolean printFatalError(Log log, Pointer sp, CodePointer ip, RegisterDumper.Context registerContext, boolean frameHasCalleeSavedRegisters) { @@ -346,6 +343,7 @@ private static int parseInvocationCount(String entry, int pos) { try { initialInvocationCount = Integer.parseInt(entry.substring(pos + 1)); } catch (NumberFormatException e) { + // handled below } if (initialInvocationCount < 1) { @@ -514,25 +512,29 @@ public int maxInvocationCount() { @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { Pointer sp = context.getStackPointer(); + UnsignedWord stackBase = VMThreads.StackBase.get(); log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); - UnsignedWord stackBase = VMThreads.StackBase.get(); + int bytesToPrint = computeBytesToPrint(sp, stackBase); + log.hexdump(sp, 8, bytesToPrint / 8); + log.indent(false).newline(); + } + + private static int computeBytesToPrint(Pointer sp, UnsignedWord stackBase) { if (stackBase.equal(0)) { /* * 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); - } else { - int bytesToPrint = 512; - UnsignedWord availableBytes = stackBase.subtract(sp); - if (availableBytes.belowThan(bytesToPrint)) { - bytesToPrint = NumUtil.safeToInt(availableBytes.rawValue()); - } + return 128; + } - log.hexdump(sp, 8, bytesToPrint / 8); + int bytesToPrint = 512; + UnsignedWord availableBytes = stackBase.subtract(sp); + if (availableBytes.belowThan(bytesToPrint)) { + bytesToPrint = NumUtil.safeToInt(availableBytes.rawValue()); } - log.indent(false).newline(); + return bytesToPrint; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 8d443bd590a8..cdb3029f5e26 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -606,8 +606,8 @@ private static int getHostPageSize() { } } - @Option(help = "Allows to specify how many details should be printed for certain diagnostic thunk, e.g.: 'DumpThreads:1,DumpRegisters:2'. " + - "A value of 1 will result in the maximum amount of information, higher values will result in less information. " + + @Option(help = "Specifies how many details are printed for certain diagnostic thunks, e.g.: 'DumpThreads:1,DumpRegisters:2'. " + + "A value of 1 will result in the maximum amount of information, higher values will print less information. " + "By default, the most detailed output is enabled for all diagnostic thunks. Wildcards (*) are supported in the name of the diagnostic thunk.", type = Expert)// public static final RuntimeOptionKey DiagnosticDetails = new RuntimeOptionKey("") { @Override 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 2a93426d72e1..0cb5e35fdeb1 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 @@ -252,7 +252,7 @@ public interface Thunk { void invoke(); } - /** Prints extensive diagnostic information to the given Log. */ + /** Prints extensive diagnostic information for a fatal error to the given log. */ public static boolean printDiagnostics(Log log, Pointer sp, CodePointer ip) { return SubstrateDiagnostics.printFatalError(log, sp, ip, WordFactory.nullPointer(), false); } 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 42cefd033828..25d7d2c58f7c 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 @@ -582,7 +582,7 @@ public static boolean printLocationInfo(Log log, UnsignedWord value, boolean all if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) { // If we are not at a safepoint, then it is unsafe to access thread locals of - // another thread is unsafe as the IsolateThread could be freed at any time. + // another thread as the IsolateThread could be freed at any time. UnsignedWord stackBase = StackBase.get(thread); UnsignedWord stackEnd = StackEnd.get(thread); if (value.belowThan(stackBase) && value.aboveOrEqual(stackEnd)) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java index 25180d2dc516..5ebb17352538 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java @@ -26,14 +26,12 @@ import java.lang.reflect.Field; -import com.oracle.svm.core.SubstrateDiagnostics; -import com.oracle.svm.core.SubstrateDiagnostics.FatalErrorState; -import com.sun.tools.javac.util.FatalError; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; +import com.oracle.svm.core.SubstrateDiagnostics.FatalErrorState; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.util.VMError; From a663301d0e17e7769e0895996ee9118d6f6fd859 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Fri, 17 Sep 2021 09:10:58 +0200 Subject: [PATCH 6/6] Renamed DiagnosticThunkRegister to DiagnosticThunkRegistry. --- .../genscavenge/GreyToBlackObjectVisitor.java | 4 +- .../oracle/svm/core/genscavenge/HeapImpl.java | 5 ++- .../oracle/svm/core/SubstrateDiagnostics.java | 37 ++++++++----------- .../hosted/SubstrateDiagnosticFeature.java | 4 +- 4 files changed, 23 insertions(+), 27 deletions(-) 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 9a286ae1b26a..7ece8ff6a1cc 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 @@ -34,7 +34,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry; import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.annotate.NeverInline; @@ -62,7 +62,7 @@ public final class GreyToBlackObjectVisitor implements ObjectVisitor { this.objRefVisitor = greyToBlackObjRefVisitor; if (DiagnosticReporter.getHistoryLength() > 0) { this.diagnosticReporter = new DiagnosticReporter(); - DiagnosticThunkRegister.getSingleton().register(diagnosticReporter); + DiagnosticThunkRegistry.singleton().register(diagnosticReporter); } else { this.diagnosticReporter = null; } 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 c9aaa0c3770e..31803a5b8e61 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 @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry; import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; @@ -119,8 +120,8 @@ public HeapImpl(int pageSize) { this.gcImpl = new GCImpl(); this.runtimeCodeInfoGcSupport = new RuntimeCodeInfoGCSupportImpl(); HeapParameters.initialize(); - SubstrateDiagnostics.DiagnosticThunkRegister.getSingleton().register(new DumpHeapSettingsAndStatistics()); - SubstrateDiagnostics.DiagnosticThunkRegister.getSingleton().register(new DumpChunkInformation()); + DiagnosticThunkRegistry.singleton().register(new DumpHeapSettingsAndStatistics()); + DiagnosticThunkRegistry.singleton().register(new DumpChunkInformation()); } @Fold 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 cc249cc4dd33..2ae835b6f177 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 @@ -106,7 +106,7 @@ public static boolean isFatalErrorHandlingThread() { public static int maxInvocations() { int result = 0; - DiagnosticThunkRegister thunks = DiagnosticThunkRegister.getSingleton(); + DiagnosticThunkRegistry thunks = DiagnosticThunkRegistry.singleton(); for (int i = 0; i < thunks.size(); i++) { result += thunks.getThunk(i).maxInvocationCount(); } @@ -142,10 +142,10 @@ public static void printInformation(Log log, Pointer sp, CodePointer ip, Registe errorContext.setFrameHasCalleeSavedRegisters(frameHasCalleeSavedRegisters); // Print all thunks in a reasonably safe way. - int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + int numDiagnosticThunks = DiagnosticThunkRegistry.singleton().size(); for (int i = 0; i < numDiagnosticThunks; i++) { - DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); - int invocationCount = DiagnosticThunkRegister.getSingleton().getInitialInvocationCount(i); + DiagnosticThunk thunk = DiagnosticThunkRegistry.singleton().getThunk(i); + int invocationCount = DiagnosticThunkRegistry.singleton().getInitialInvocationCount(i); if (invocationCount <= thunk.maxInvocationCount()) { thunk.printDiagnostics(log, errorContext, DiagnosticLevel.SAFE, invocationCount); } @@ -202,14 +202,14 @@ private static void printFatalErrorForCurrentState() { // Print the various sections of the diagnostics and skip all sections that were already // printed earlier. ErrorContext errorContext = fatalErrorState.getErrorContext(); - int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + int numDiagnosticThunks = DiagnosticThunkRegistry.singleton().size(); while (fatalErrorState.diagnosticThunkIndex < numDiagnosticThunks) { int index = fatalErrorState.diagnosticThunkIndex; - DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(index); + DiagnosticThunk thunk = DiagnosticThunkRegistry.singleton().getThunk(index); // Start at the configured initial invocation count. if (fatalErrorState.invocationCount == 0) { - fatalErrorState.invocationCount = DiagnosticThunkRegister.getSingleton().getInitialInvocationCount(index) - 1; + fatalErrorState.invocationCount = DiagnosticThunkRegistry.singleton().getInitialInvocationCount(index) - 1; } while (++fatalErrorState.invocationCount <= thunk.maxInvocationCount()) { @@ -322,13 +322,13 @@ private static void updateInitialInvocationCount(String entry) throws IllegalArg int initialInvocationCount = parseInvocationCount(entry, pos); int matches = 0; - int numDiagnosticThunks = DiagnosticThunkRegister.getSingleton().size(); + int numDiagnosticThunks = DiagnosticThunkRegistry.singleton().size(); for (int i = 0; i < numDiagnosticThunks; i++) { - DiagnosticThunk thunk = DiagnosticThunkRegister.getSingleton().getThunk(i); + DiagnosticThunk thunk = DiagnosticThunkRegistry.singleton().getThunk(i); // Checkstyle: allow Class.getSimpleName if (matches(thunk.getClass().getSimpleName(), pattern)) { // Checkstyle: disallow Class.getSimpleName - DiagnosticThunkRegister.getSingleton().setInitialInvocationCount(i, initialInvocationCount); + DiagnosticThunkRegistry.singleton().setInitialInvocationCount(i, initialInvocationCount); matches++; } } @@ -898,27 +898,22 @@ public abstract static class DiagnosticThunk { public abstract int maxInvocationCount(); } - public static class DiagnosticThunkRegister { + public static class DiagnosticThunkRegistry { private DiagnosticThunk[] diagnosticThunks; private int[] initialInvocationCount; - /** - * Get the register. - *

- * This method is @Fold so anyone who uses it ensures there is a register. - */ @Fold /* Checkstyle: allow synchronization. */ - public static synchronized DiagnosticThunkRegister getSingleton() { - if (!ImageSingletons.contains(DiagnosticThunkRegister.class)) { - ImageSingletons.add(DiagnosticThunkRegister.class, new DiagnosticThunkRegister()); + public static synchronized DiagnosticThunkRegistry singleton() { + if (!ImageSingletons.contains(DiagnosticThunkRegistry.class)) { + ImageSingletons.add(DiagnosticThunkRegistry.class, new DiagnosticThunkRegistry()); } - return ImageSingletons.lookup(DiagnosticThunkRegister.class); + return ImageSingletons.lookup(DiagnosticThunkRegistry.class); } /* Checkstyle: disallow synchronization. */ @Platforms(Platform.HOSTED_ONLY.class) - DiagnosticThunkRegister() { + DiagnosticThunkRegistry() { 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(), diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java index 5ebb17352538..b96537db6c0f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateDiagnosticFeature.java @@ -30,7 +30,7 @@ import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegister; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry; import com.oracle.svm.core.SubstrateDiagnostics.FatalErrorState; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -44,7 +44,7 @@ public void duringSetup(DuringSetupAccess access) { ImageSingletons.add(FatalErrorState.class, new FatalErrorState()); // Ensure that the diagnostic thunks are initialized. - DiagnosticThunkRegister.getSingleton(); + DiagnosticThunkRegistry.singleton(); } @Override