diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java index dee4d153f9ad..19b9f0895772 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java @@ -179,7 +179,7 @@ interface LINUX extends InternalPlatform.PLATFORM_JNI, InternalPlatform.NATIVE_O * @since 21.0 */ default String getOS() { - return LINUX.class.getSimpleName().toLowerCase(); + return "linux"; } } @@ -196,7 +196,7 @@ interface ANDROID extends LINUX { * @since 21.0 */ default String getOS() { - return ANDROID.class.getSimpleName().toLowerCase(); + return "android"; } } @@ -221,7 +221,7 @@ interface IOS extends DARWIN { * @since 21.0 */ default String getOS() { - return IOS.class.getSimpleName().toLowerCase(); + return "ios"; } } @@ -255,7 +255,7 @@ interface WINDOWS extends InternalPlatform.PLATFORM_JNI, InternalPlatform.NATIVE * @since 21.0 */ default String getOS() { - return WINDOWS.class.getSimpleName().toLowerCase(); + return "windows"; } } 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 1c1cdd0c4abe..48792f464adc 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 @@ -28,6 +28,7 @@ import org.graalvm.nativeimage.c.function.CEntryPoint.Publish; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.VoidPointer; import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; @@ -77,7 +78,10 @@ protected void printSignalInfo(Log log, PointerBase signalInfo) { if (sigInfo.si_errno() != 0) { log.string(", si_errno: ").signed(sigInfo.si_errno()); } - log.string(", si_addr: ").zhex(sigInfo.si_addr()); + + VoidPointer addr = sigInfo.si_addr(); + log.string(", si_addr: "); + printSegfaultAddressInfo(log, addr.rawValue()); log.newline(); } } 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 57c374592a62..a6c251dde7b1 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 @@ -119,7 +119,8 @@ protected void printSignalInfo(Log log, PointerBase signalInfo) { } else { log.string(", ExceptionInformation=").zhex(operation); } - log.string(" ").zhex(exInfo.addressOf(1).read()); + log.string(" "); + printSegfaultAddressInfo(log, exInfo.addressOf(1).read()); } else { if (numParameters > 0) { log.string(", ExceptionInformation="); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CPUFeatureAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CPUFeatureAccess.java index 722895055c59..3ba98a900645 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CPUFeatureAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CPUFeatureAccess.java @@ -26,9 +26,17 @@ import java.util.EnumSet; +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; + import jdk.vm.ci.code.Architecture; public interface CPUFeatureAccess { + @Fold + static CPUFeatureAccess singleton() { + return ImageSingletons.lookup(CPUFeatureAccess.class); + } + int verifyHostSupportsArchitectureEarly(); void verifyHostSupportsArchitectureEarlyOrExit(); 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 e05ac0ac79c1..78794d2a7995 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 @@ -73,7 +73,7 @@ import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.thread.ThreadListenerSupport; import com.oracle.svm.core.thread.VMThreads; -import com.oracle.svm.core.util.Counter; +import com.oracle.svm.core.util.CounterSupport; import com.oracle.svm.core.util.VMError; @InternalVMMethod @@ -206,7 +206,7 @@ private static void runShutdown0() { */ RuntimeSupport.getRuntimeSupport().shutdown(); - Counter.logValues(Log.log()); + CounterSupport.singleton().logValues(Log.log()); } @Uninterruptible(reason = "Thread state not set up yet.") 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 be6508cc9357..fbb2694fef60 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 @@ -26,6 +26,7 @@ import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates; +import java.util.ArrayList; import java.util.Arrays; import org.graalvm.collections.EconomicMap; @@ -71,6 +72,7 @@ import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicWord; +import com.oracle.svm.core.jdk.management.SubstrateRuntimeMXBean; import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.RuntimeOptionKey; @@ -89,7 +91,7 @@ import com.oracle.svm.core.threadlocal.FastThreadLocalBytes; 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.CounterSupport; public class SubstrateDiagnostics { private static final int MAX_THREADS_TO_PRINT = 100_000; @@ -505,19 +507,6 @@ public void clear() { } } - private static class DumpCurrentTimestamp extends DiagnosticThunk { - @Override - public int maxInvocationCount() { - return 1; - } - - @Override - @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("Current timestamp: ").unsigned(System.currentTimeMillis()).newline().newline(); - } - } - private static class DumpRegisters extends DiagnosticThunk { @Override public int maxInvocationCount() { @@ -543,34 +532,32 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev private static class DumpInstructions extends DiagnosticThunk { @Override public int maxInvocationCount() { - return 3; + return 4; } @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - if (invocationCount < 3) { - printBytesBeforeAndAfterIp(log, context.getInstructionPointer(), invocationCount); - } else if (invocationCount == 3) { - printWord(log, context.getInstructionPointer()); + CodePointer ip = context.getInstructionPointer(); + log.string("Printing instructions (ip=").zhex(ip).string("):").indent(true); + if (ip.isNull()) { + // can't print any instructions + } else if (invocationCount < 4) { + // print 512, 128, or 32 instruction bytes. + int bytesToPrint = 1024 >> (invocationCount * 2); + hexDump(log, ip, bytesToPrint, bytesToPrint); + } else if (invocationCount == 4) { + // just print one word starting at the ip + hexDump(log, ip, 0, ConfigurationValues.getTarget().wordSize); } - } - - private static void printBytesBeforeAndAfterIp(Log log, CodePointer ip, int invocationCount) { - // print 64 or 32 instruction bytes. - int bytesToPrint = 64 >> invocationCount; - hexDump(log, ip, bytesToPrint, bytesToPrint); - } - - private static void printWord(Log log, CodePointer ip) { - // just print one word starting at the ip - hexDump(log, ip, 0, ConfigurationValues.getTarget().wordSize); + log.indent(false).newline(); } private static void hexDump(Log log, CodePointer ip, int bytesBefore, int bytesAfter) { - log.string("Printing Instructions (ip=").zhex(ip).string("):").indent(true); - log.hexdump(((Pointer) ip).subtract(bytesBefore), 1, bytesBefore + bytesAfter); - log.indent(false).newline(); + log.hexdump(((Pointer) ip).subtract(bytesBefore), 1, bytesBefore); + log.indent(false); + log.string("> ").redent(true); + log.hexdump(ip, 1, bytesAfter); } } @@ -583,30 +570,42 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, 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. + */ Pointer sp = context.getStackPointer(); - UnsignedWord stackBase = VMThreads.StackBase.get(); - log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); + UnsignedWord stackEnd = VMThreads.StackEnd.get(); // low + UnsignedWord stackBase = VMThreads.StackBase.get(); // high + int wordSize = ConfigurationValues.getTarget().wordSize; - 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. - */ - return 128; + log.string("Top of stack (sp=").zhex(sp).string("):").indent(true); + int bytesToPrintBelowSp = 32; + if (stackEnd.notEqual(0)) { + UnsignedWord availableBytes = sp.subtract(stackEnd); + if (availableBytes.belowThan(bytesToPrintBelowSp)) { + bytesToPrintBelowSp = NumUtil.safeToInt(availableBytes.rawValue()); + } } - int bytesToPrint = 512; - UnsignedWord availableBytes = stackBase.subtract(sp); - if (availableBytes.belowThan(bytesToPrint)) { - bytesToPrint = NumUtil.safeToInt(availableBytes.rawValue()); + int bytesToPrintAboveSp = 128; + if (stackBase.notEqual(0)) { + bytesToPrintAboveSp = 512; + UnsignedWord availableBytes = stackBase.subtract(sp); + if (availableBytes.belowThan(bytesToPrintAboveSp)) { + bytesToPrintAboveSp = NumUtil.safeToInt(availableBytes.rawValue()); + } } - return bytesToPrint; + + int wordsToPrintBelowSp = bytesToPrintBelowSp / wordSize; + log.hexdump(sp.subtract(wordsToPrintBelowSp * wordSize), wordSize, wordsToPrintBelowSp, 32); + log.indent(false); + + log.string("> ").redent(true); + log.hexdump(sp, wordSize, bytesToPrintAboveSp / wordSize, 32); + log.indent(false); + + log.newline(); } } @@ -619,13 +618,11 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - if (DeoptimizationSupport.enabled()) { - log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline(); - } + log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline(); } } - private static class DumpTopFrame extends DiagnosticThunk { + private static class DumpTopDeoptimizedFrame extends DiagnosticThunk { @Override public int maxInvocationCount() { return 1; @@ -634,39 +631,19 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") 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 = context.getStackPointer(); CodePointer ip = context.getInstructionPointer(); if (sp.isNonNull() && ip.isNonNull()) { - log.string("Top frame info:").indent(true); long totalFrameSize = getTotalFrameSize(sp, ip); DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(sp); if (deoptFrame != null) { + log.string("Top frame info:").indent(true); log.string("RSP ").zhex(sp).string(" frame was deoptimized:").newline(); log.string("SourcePC ").zhex(deoptFrame.getSourcePC()).newline(); log.string("SourceTotalFrameSize ").signed(totalFrameSize).newline(); - } else if (totalFrameSize != -1) { - log.string("TotalFrameSize in CodeInfoTable ").signed(totalFrameSize).newline(); - } - - if (totalFrameSize == -1) { - log.string("Does not look like a Java Frame. Use JavaFrameAnchors to find LastJavaSP:").newline(); - JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor(); - while (anchor.isNonNull() && anchor.getLastJavaSP().belowOrEqual(sp)) { - anchor = anchor.getPreviousAnchor(); - } - - if (anchor.isNonNull()) { - log.string("Found matching Anchor:").zhex(anchor).newline(); - Pointer lastSp = anchor.getLastJavaSP(); - log.string("LastJavaSP ").zhex(lastSp).newline(); - CodePointer lastIp = anchor.getLastJavaIP(); - log.string("LastJavaIP ").zhex(lastIp).newline(); - } + log.indent(false); } - log.indent(false); } } } @@ -680,8 +657,8 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") 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; + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 3; /* * If we are not at a safepoint, then it is unsafe to access the thread locals of * another thread as the IsolateThread could be freed at any time. @@ -703,9 +680,13 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev if (allowJavaHeapAccess) { Thread threadObj = PlatformThreads.fromVMThread(thread); - log.string(" \"").string(threadObj.getName()).string("\" - ").zhex(Word.objectToUntrackedPointer(threadObj)); - if (threadObj != null && threadObj.isDaemon()) { - log.string(", daemon"); + if (threadObj == null) { + log.string(" null"); + } else { + log.string(" \"").string(threadObj.getName()).string("\" - ").zhex(Word.objectToUntrackedPointer(threadObj)); + if (threadObj.isDaemon()) { + log.string(", daemon"); + } } } log.string(", stack(").zhex(VMThreads.StackEnd.get(thread)).string(",").zhex(VMThreads.StackBase.get(thread)).string(")"); @@ -780,10 +761,8 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - if (RuntimeCompilation.isEnabled()) { - boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; - RuntimeCodeInfoHistory.singleton().printRecentOperations(log, allowJavaHeapAccess); - } + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 2; + RuntimeCodeInfoHistory.singleton().printRecentOperations(log, allowJavaHeapAccess); } } @@ -796,11 +775,9 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - if (RuntimeCompilation.isEnabled()) { - boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; - boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; - RuntimeCodeInfoMemory.singleton().printTable(log, allowJavaHeapAccess, allowUnsafeOperations); - } + boolean allowJavaHeapAccess = DiagnosticLevel.javaHeapAccessAllowed(maxDiagnosticLevel) && invocationCount < 3; + boolean allowUnsafeOperations = DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel) && invocationCount < 2; + RuntimeCodeInfoMemory.singleton().printTable(log, allowJavaHeapAccess, allowUnsafeOperations); } } @@ -813,13 +790,11 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - if (DeoptimizationSupport.enabled()) { - Deoptimizer.logRecentDeoptimizationEvents(log); - } + Deoptimizer.logRecentDeoptimizationEvents(log); } } - private static class DumpCounters extends DiagnosticThunk { + static class DumpGeneralInfo extends DiagnosticThunk { @Override public int maxInvocationCount() { return 1; @@ -828,10 +803,47 @@ public int maxInvocationCount() { @Override @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(log); + log.string("General information:").indent(true); + log.string("VM version: ").string(ImageSingletons.lookup(VM.class).version); + + Platform platform = ImageSingletons.lookup(Platform.class); + log.string(", ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); + + log.string("Current timestamp: ").unsigned(System.currentTimeMillis()).newline(); + log.string("VM uptime: ").signed(ImageSingletons.lookup(SubstrateRuntimeMXBean.class).getUptime()).string(" ms").newline(); + + CodeInfo info = CodeInfoTable.getImageCodeInfo(); + Pointer codeStart = (Pointer) CodeInfoAccess.getCodeStart(info); + UnsignedWord codeSize = CodeInfoAccess.getCodeSize(info); + Pointer codeEnd = codeStart.add(codeSize).subtract(1); + log.string("AOT compiled code: ").zhex(codeStart).string(" - ").zhex(codeEnd).newline(); + + log.string("CPU features used for AOT compiled code: ").string(getBuildTimeCpuFeatures()).newline(); log.indent(false); } + + @Fold + static String getBuildTimeCpuFeatures() { + return String.join(", ", CPUFeatureAccess.singleton().buildtimeCPUFeatures().stream().map(Enum::toString).toList()); + } + } + + private static class DumpCounters extends DiagnosticThunk { + @Override + public int maxInvocationCount() { + return 1; + } + + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") + public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { + CounterSupport counters = CounterSupport.singleton(); + if (counters.hasCounters()) { + log.string("Counters:").indent(true); + counters.logValues(log); + log.indent(false); + } + } } private static class DumpCurrentThreadFrameAnchors extends DiagnosticThunk { @@ -862,29 +874,56 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev Pointer sp = context.getStackPointer(); CodePointer ip = context.getInstructionPointer(); - log.string("Stacktrace for the failing thread ").zhex(CurrentIsolate.getCurrentThread()).string(":").indent(true); + log.string("Stacktrace for the failing thread ").zhex(CurrentIsolate.getCurrentThread()).string(" (A=AOT compiled, J=JIT compiled, D=deoptimized, i=inlined):").indent(true); boolean success = ThreadStackPrinter.printStacktrace(sp, ip, printVisitors[invocationCount - 1].reset(), log); if (!success && DiagnosticLevel.unsafeOperationsAllowed(maxDiagnosticLevel)) { /* * If the stack pointer is not sufficiently aligned, then we might be in the middle - * of a call (i.e., only the return address and the arguments are on the stack). In - * that case, we can read the return address from the top of the stack, align the - * stack pointer, and start a stack walk in the caller. + * of a call (i.e., only the return address and the arguments are on the stack). */ int expectedStackAlignment = ConfigurationValues.getTarget().stackAlignment; if (sp.unsignedRemainder(expectedStackAlignment).notEqual(0) && sp.unsignedRemainder(ConfigurationValues.getTarget().wordSize).equal(0)) { log.newline(); - log.string("WARNING: stack pointer is NOT aligned to ").signed(expectedStackAlignment).string(" bytes. Starting a stack walk in the most likely caller instead.").newline(); - ip = sp.readWord(0); - sp = sp.add(ConfigurationValues.getTarget().wordSize); - - ThreadStackPrinter.printStacktrace(sp, ip, printVisitors[invocationCount - 1].reset(), log); + log.string("WARNING: stack pointer is NOT aligned to ").signed(expectedStackAlignment).string(" bytes.").newline(); } + + startStackWalkInMostLikelyCaller(log, invocationCount, sp); } log.indent(false); } + + private static void startStackWalkInMostLikelyCaller(Log log, int invocationCount, Pointer originalSp) { + UnsignedWord stackBase = VMThreads.StackBase.get(); + if (stackBase.equal(0)) { + /* We don't know the stack boundaries, so only search within 32 bytes. */ + stackBase = originalSp.add(32); + } + + /* Search until we find a valid (return address, stack pointer) tuple. */ + int wordSize = ConfigurationValues.getTarget().wordSize; + Pointer pos = originalSp; + while (pos.belowThan(stackBase)) { + CodePointer possibleIp = pos.readWord(0); + if (pointsIntoNativeImageCode(possibleIp)) { + Pointer possibleCallerSp = pos.readWord(wordSize); + if (possibleCallerSp.aboveThan(originalSp) && possibleCallerSp.belowThan(stackBase)) { + Pointer sp = pos.add(wordSize); + log.newline(); + log.string("Starting the stack walk in a possible caller:").newline(); + ThreadStackPrinter.printStacktrace(sp, possibleIp, printVisitors[invocationCount - 1].reset(), log); + break; + } + } + pos = pos.add(wordSize); + } + } + + @Uninterruptible(reason = "Prevent the GC from freeing the CodeInfo.") + private static boolean pointsIntoNativeImageCode(CodePointer possibleIp) { + return CodeInfoTable.lookupCodeInfo(possibleIp).isNonNull(); + } } private static class DumpOtherStackTraces extends DiagnosticThunk { @@ -935,7 +974,7 @@ private static void printStackTrace(Log log, IsolateThread vmThread, int invocat } } - private static class DumpAOTCompiledCodeInfo extends DiagnosticThunk { + private static class DumpCommandLine extends DiagnosticThunk { @Override public int maxInvocationCount() { return 1; @@ -944,13 +983,14 @@ public int maxInvocationCount() { @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.") public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { - CodeInfo info = CodeInfoTable.getImageCodeInfo(); - Pointer codeStart = (Pointer) CodeInfoAccess.getCodeStart(info); - UnsignedWord codeSize = CodeInfoAccess.getCodeSize(info); - Pointer codeEnd = codeStart.add(codeSize).subtract(1); - - log.string("AOT compiled code is mapped at ").zhex(codeStart).string(" - ").zhex(codeEnd).newline(); - log.newline(); + String[] args = ImageSingletons.lookup(JavaMainWrapper.JavaMainSupport.class).mainArgs; + if (args != null) { + log.string("Command line: ").newline(); + for (String arg : args) { + log.string("'").string(arg).string("' "); + } + log.newline(); + } } } @@ -1087,11 +1127,34 @@ public static synchronized DiagnosticThunkRegistry singleton() { @Platforms(Platform.HOSTED_ONLY.class) DiagnosticThunkRegistry() { - this.diagnosticThunks = new DiagnosticThunk[]{new DumpCurrentTimestamp(), new DumpRegisters(), new DumpInstructions(), new DumpTopOfCurrentThreadStack(), new DumpDeoptStubPointer(), - new DumpTopFrame(), new DumpThreads(), new DumpCurrentThreadLocals(), new DumpCurrentVMOperation(), new DumpVMOperationHistory(), new DumpCodeCacheHistory(), - new DumpRuntimeCodeInfoMemory(), new DumpRecentDeoptimizations(), new DumpCounters(), new DumpCurrentThreadFrameAnchors(), new DumpCurrentThreadDecodedStackTrace(), - new DumpOtherStackTraces(), new VMLockSupport.DumpVMMutexes(), new DumpAOTCompiledCodeInfo()}; + ArrayList thunks = new ArrayList<>(); + thunks.add(new DumpRegisters()); + thunks.add(new DumpInstructions()); + thunks.add(new DumpTopOfCurrentThreadStack()); + if (RuntimeCompilation.isEnabled()) { + thunks.add(new DumpTopDeoptimizedFrame()); + } + thunks.add(new DumpCurrentThreadLocals()); + thunks.add(new DumpCurrentThreadFrameAnchors()); + thunks.add(new DumpCurrentThreadDecodedStackTrace()); + thunks.add(new DumpThreads()); + thunks.add(new DumpOtherStackTraces()); + thunks.add(new DumpCurrentVMOperation()); + thunks.add(new DumpVMOperationHistory()); + thunks.add(new VMLockSupport.DumpVMMutexes()); + thunks.add(new DumpGeneralInfo()); + if (ImageSingletons.contains(JavaMainWrapper.JavaMainSupport.class)) { + thunks.add(new DumpCommandLine()); + } + thunks.add(new DumpCounters()); + if (RuntimeCompilation.isEnabled()) { + thunks.add(new DumpCodeCacheHistory()); + thunks.add(new DumpRuntimeCodeInfoMemory()); + thunks.add(new DumpDeoptStubPointer()); + thunks.add(new DumpRecentDeoptimizations()); + } + this.diagnosticThunks = thunks.toArray(new DiagnosticThunk[0]); this.initialInvocationCount = new int[diagnosticThunks.length]; Arrays.fill(initialInvocationCount, 1); } 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 38d4af098250..c64092a21766 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 @@ -864,6 +864,9 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol } }; + @Option(help = "Specifies the number of entries that diagnostic buffers have.", type = OptionType.Debug)// + public static final HostedOptionKey DiagnosticBufferSize = new HostedOptionKey<>(30); + @SuppressWarnings("unused")// @APIOption(name = "configure-reflection-metadata")// @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert, deprecated = true)// 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 caf7e0bb0f0d..eed513a3057b 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 @@ -187,6 +187,15 @@ private static void dumpInterruptibly(PointerBase signalInfo, RegisterDumper.Con logHandler.fatalError(); } + protected static void printSegfaultAddressInfo(Log log, long addr) { + log.zhex(addr); + if (addr != 0) { + long delta = addr - CurrentIsolate.getIsolate().rawValue(); + String sign = (delta >= 0 ? "+" : "-"); + log.string("(heapBase ").string(sign).signed(delta).string(")"); + } + } + static class SingleIsolateSegfaultSetup implements IsolateListener { /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java index 330962dda3b1..f8891f44c5cb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java @@ -309,7 +309,7 @@ public static void setEncodings(CodeInfo info, NonmovableObjectArray obj } public static Log log(CodeInfo info, Log log) { - return info.isNull() ? log.string("null") : log.string(CodeInfo.class.getName()).string("@").hex(info); + return info.isNull() ? log.string("null") : log.string("CodeInfo@").hex(info); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java index a2b7ebab5822..480e17602f5d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java @@ -515,7 +515,8 @@ static CodeInfoDecoderCounters counters() { /** * This class can be used to iterate the Java-level stack trace information for a given * instruction pointer (IP). A single physical stack frame may correspond to multiple Java-level - * stack frames. + * stack frames. Iteration starts in the deepest inlined method and ends at the compilation + * root. */ public static class FrameInfoCursor { private final ReusableTypeReader frameInfoReader = new ReusableTypeReader(); @@ -550,6 +551,12 @@ public boolean advance() { return result != null; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public boolean hasCaller() { + assert result != null; + return !state.isDone; + } + /** * Returns the information for the current frame. * diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java index 9d5703a38b98..497ea72cf512 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java @@ -377,10 +377,11 @@ private static FrameInfoQueryResult decodeUncompressedFrameInfo(boolean isDeoptE FrameInfoQueryResult prev = null; ValueInfo[][] virtualObjects = null; - while (true) { + while (!state.isDone) { long start = readBuffer.getByteIndex(); int encodedBci = readBuffer.getSVInt(); if (encodedBci == NO_CALLER_BCI) { + state.isDone = true; return result; } @@ -460,6 +461,8 @@ private static FrameInfoQueryResult decodeUncompressedFrameInfo(boolean isDeoptE state.isFirstFrame = false; } + + return result; } @Uninterruptible(reason = "Some allocators are interruptible.", calleeMustBe = false) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java index 5b849f06dc7e..7cebedc593ba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoHistory.java @@ -24,22 +24,23 @@ */ package com.oracle.svm.core.code; -import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.code.CodeInfoAccess.HasInstalledCode; -import com.oracle.svm.core.deopt.SubstrateInstalledCode; -import com.oracle.svm.core.thread.Safepoint; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CodePointer; +import org.graalvm.word.UnsignedWord; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.code.CodeInfoAccess.HasInstalledCode; +import com.oracle.svm.core.collections.RingBuffer; +import com.oracle.svm.core.deopt.SubstrateInstalledCode; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.thread.Safepoint; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; -import com.oracle.svm.core.util.RingBuffer; -import org.graalvm.word.UnsignedWord; public class RuntimeCodeInfoHistory { private static final RingBuffer.Consumer PRINT_WITH_JAVA_HEAP_DATA = RuntimeCodeInfoHistory::printEntryWithJavaHeapData; @@ -49,7 +50,7 @@ public class RuntimeCodeInfoHistory { @Platforms(Platform.HOSTED_ONLY.class) RuntimeCodeInfoHistory() { - recentOperations = new RingBuffer<>(20, CodeCacheLogEntry::new); + recentOperations = new RingBuffer<>(SubstrateOptions.DiagnosticBufferSize.getValue(), CodeCacheLogEntry::new); } @Fold diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/RingBuffer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java similarity index 93% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/RingBuffer.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java index dd4f6f2ba971..8cd6b4df17f8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/RingBuffer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java @@ -22,18 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.util; +package com.oracle.svm.core.collections; import java.util.function.Supplier; import com.oracle.svm.core.Uninterruptible; -/** - * Keeps the last-n entries and allows to read the out on demand.. - */ +/** Keeps the last-n entries. */ public final class RingBuffer { - private static final int DEFAULT_BUFFER_SIZE = 30; - private final T[] entries; private int pos; private boolean wrapped; @@ -42,10 +38,6 @@ public interface Consumer { void accept(Object context, T t); } - public RingBuffer() { - this(DEFAULT_BUFFER_SIZE); - } - @SuppressWarnings("unchecked") public RingBuffer(int numEntries) { this.entries = (T[]) new Object[numEntries]; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index c478fb1c7ffd..abd292ee6418 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -60,6 +60,7 @@ import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.FrameInfoQueryResult.ValueInfo; import com.oracle.svm.core.code.UntetheredCodeInfo; +import com.oracle.svm.core.collections.RingBuffer; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.deopt.DeoptimizedFrame.RelockObjectData; @@ -82,7 +83,6 @@ import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; -import com.oracle.svm.core.util.RingBuffer; import com.oracle.svm.core.util.VMError; import jdk.internal.misc.Unsafe; @@ -149,7 +149,7 @@ */ public final class Deoptimizer { private static final int MAX_DEOPTIMIZATION_EVENT_PRINT_LENGTH = 1000; - private static final RingBuffer recentDeoptimizationEvents = new RingBuffer<>(); + private static final RingBuffer recentDeoptimizationEvents = new RingBuffer<>(SubstrateOptions.DiagnosticBufferSize.getValue()); private static final int actionShift = 0; private static final int actionBits = Integer.SIZE - Integer.numberOfLeadingZeros(DeoptimizationAction.values().length); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementFeature.java index 76a6f091ee31..47d3443d70c3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementFeature.java @@ -47,12 +47,12 @@ import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import org.graalvm.nativeimage.hosted.RuntimeReflection; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.JNIRegistrationUtil; import com.oracle.svm.core.jdk.RuntimeSupportFeature; import com.oracle.svm.core.thread.ThreadListenerSupport; import com.oracle.svm.core.thread.ThreadListenerSupportFeature; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.util.ReflectionUtil; /** See {@link ManagementSupport} for documentation. */ @@ -67,10 +67,13 @@ public List> getRequiredFeatures() { @Override public void afterRegistration(AfterRegistrationAccess access) { + SubstrateRuntimeMXBean runtimeMXBean = new SubstrateRuntimeMXBean(); + ImageSingletons.add(SubstrateRuntimeMXBean.class, runtimeMXBean); + SubstrateThreadMXBean threadMXBean = new SubstrateThreadMXBean(); ImageSingletons.add(SubstrateThreadMXBean.class, threadMXBean); - ManagementSupport managementSupport = new ManagementSupport(threadMXBean); + ManagementSupport managementSupport = new ManagementSupport(runtimeMXBean, threadMXBean); ImageSingletons.add(ManagementSupport.class, managementSupport); ThreadListenerSupport.get().register(managementSupport); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementSupport.java index 928a82f52a35..dae3b63b8853 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/ManagementSupport.java @@ -45,7 +45,6 @@ import javax.management.StandardEmitterMBean; import javax.management.StandardMBean; -import jdk.management.jfr.FlightRecorderMXBean; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.IsolateThread; @@ -54,15 +53,17 @@ import org.graalvm.nativeimage.impl.InternalPlatform; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.jfr.HasJfrSupport; import com.oracle.svm.core.thread.ThreadListener; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.jfr.HasJfrSupport; import com.sun.jmx.mbeanserver.MXBeanLookup; +import jdk.management.jfr.FlightRecorderMXBean; + /** * This class provides the SVM support implementation for the MXBean that provide VM introspection, * which is accessible in the JDK via {@link ManagementFactory}. There are two mostly independent @@ -118,7 +119,6 @@ public final class ManagementSupport implements ThreadListener { private final SubstrateClassLoadingMXBean classLoadingMXBean; private final SubstrateCompilationMXBean compilationMXBean; - private final SubstrateRuntimeMXBean runtimeMXBean; private final SubstrateThreadMXBean threadMXBean; /* Initialized lazily at run time. */ @@ -129,13 +129,12 @@ public final class ManagementSupport implements ThreadListener { MBeanServer platformMBeanServer; @Platforms(Platform.HOSTED_ONLY.class) - ManagementSupport(SubstrateThreadMXBean threadMXBean) { + ManagementSupport(SubstrateRuntimeMXBean runtimeMXBean, SubstrateThreadMXBean threadMXBean) { platformManagedObjectsMap = new HashMap<>(); platformManagedObjectsSet = Collections.newSetFromMap(new IdentityHashMap<>()); classLoadingMXBean = new SubstrateClassLoadingMXBean(); compilationMXBean = new SubstrateCompilationMXBean(); - runtimeMXBean = new SubstrateRuntimeMXBean(); this.threadMXBean = threadMXBean; /* diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateRuntimeMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateRuntimeMXBean.java index 4e319c67e521..3f5cc6a6effb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateRuntimeMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateRuntimeMXBean.java @@ -48,7 +48,7 @@ import sun.management.Util; -final class SubstrateRuntimeMXBean implements RuntimeMXBean { +public final class SubstrateRuntimeMXBean implements RuntimeMXBean { private final String managementSpecVersion; private long startMillis; @@ -153,7 +153,7 @@ public String getBootClassPath() { @Override public long getUptime() { - return System.currentTimeMillis() - startMillis; + return Math.max(0, System.currentTimeMillis() - startMillis); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMLockSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/locks/VMLockSupport.java index 9a640c058e81..bbafc2c39575 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 @@ -68,7 +68,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev } if (support == null || support.getMutexes() == null) { - log.string("No mutex information is available."); + log.string("No mutex information is available.").newline(); } else { VMMutex[] mutexes = support.getMutexes(); for (int i = 0; i < mutexes.length; i++) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java index 3fb00f676b84..820c9d9b6668 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/Log.java @@ -202,13 +202,18 @@ protected Log() { */ public abstract Log signed(long value); + /** + * Prints the value, treated as a signed value, filling spaces before or after. + */ + public abstract Log signed(long value, int fill, int align); + /** * Prints the value, treated as an unsigned value, in decimal format. */ public abstract Log unsigned(WordBase value); /** - * Prints the value, treated as an unsigned value, filing spaces before or after. + * Prints the value, treated as an unsigned value, filling spaces before or after. */ public abstract Log unsigned(WordBase value, int fill, int align); @@ -223,7 +228,7 @@ protected Log() { public abstract Log unsigned(long value); /** - * Prints the value, treated as an unsigned value, filing spaces before or after. + * Prints the value, treated as an unsigned value, filling spaces before or after. */ public abstract Log unsigned(long value, int fill, int align); @@ -283,6 +288,16 @@ protected Log() { */ public abstract Log hexdump(PointerBase from, int wordSize, int numWords); + /** + * Prints a hexdump. + * + * @param from pointer to memory where dumping should start from + * @param wordSize size in bytes that a single word should have + * @param numWords number of words to dump + * @param bytesPerLine number of bytes that should be printed on one line + */ + public abstract Log hexdump(PointerBase from, int wordSize, int numWords, int bytesPerLine); + /** * Change current amount of indentation. Indentation determines the amount of spaces emitted * after each newline. @@ -443,6 +458,11 @@ public Log signed(long value) { return this; } + @Override + public Log signed(long value, int fill, int align) { + return this; + } + @Override public Log unsigned(WordBase value) { return this; @@ -543,6 +563,11 @@ public Log hexdump(PointerBase from, int wordSize, int numWords) { return this; } + @Override + public Log hexdump(PointerBase from, int wordSize, int numWords, int bytesPerLine) { + return this; + } + @Override public Log exception(Throwable t) { return this; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java index ba19178f8bdf..b68efc1635d2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java @@ -291,6 +291,12 @@ public Log signed(long value) { return this; } + @Override + public Log signed(long value, int fill, int align) { + number(value, 10, true, fill, align); + return this; + } + @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") public Log unsigned(WordBase value) { @@ -560,13 +566,19 @@ public Log zhex(byte value) { } @Override - @NeverInline("Logging is always slow-path code") @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") public Log hexdump(PointerBase from, int wordSize, int numWords) { + return hexdump(from, wordSize, numWords, 16); + } + + @Override + @NeverInline("Logging is always slow-path code") + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") + public Log hexdump(PointerBase from, int wordSize, int numWords, int bytesPerLine) { Pointer base = WordFactory.pointer(from.rawValue()); int sanitizedWordsize = wordSize > 0 ? Integer.highestOneBit(Math.min(wordSize, 8)) : 2; for (int offset = 0; offset < sanitizedWordsize * numWords; offset += sanitizedWordsize) { - if (offset % 16 == 0) { + if (offset % bytesPerLine == 0) { zhex(base.add(offset)); string(":"); } @@ -585,7 +597,7 @@ public Log hexdump(PointerBase from, int wordSize, int numWords) { zhex(base.readLong(offset)); break; } - if ((offset + sanitizedWordsize) % 16 == 0 && (offset + sanitizedWordsize) < sanitizedWordsize * numWords) { + if ((offset + sanitizedWordsize) % bytesPerLine == 0 && (offset + sanitizedWordsize) < sanitizedWordsize * numWords) { newline(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java index 3e434fb3949d..447da531ab1b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java @@ -27,6 +27,7 @@ import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.word.Pointer; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.Uninterruptible; @@ -53,7 +54,7 @@ public StackFramePrintVisitor() { @Override protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame) { if (deoptFrame != null) { - logVirtualFrames(log, sp, ip, deoptFrame); + logVirtualFrames(log, sp, ip, codeInfo, deoptFrame); return; } @@ -68,17 +69,21 @@ protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, if (!isFirst) { log.newline(); } - logFrameRaw(log, sp, ip); - FrameInfoQueryResult frame = frameInfoCursor.get(); - logFrameInfo(log, frame, CodeInfoAccess.getName(codeInfo)); + boolean compilationRoot = !frameInfoCursor.hasCaller(); + printFrameIdentifier(log, codeInfo, null, compilationRoot); + logFrameRaw(log, sp, ip, codeInfo); + + String codeInfoName = DeoptimizationSupport.enabled() ? CodeInfoAccess.getName(codeInfo) : null; + logFrameInfo(log, frameInfoCursor.get(), codeInfoName); isFirst = false; printedFrames++; } if (isFirst) { - /* The code above failed, so print less detailed information. */ - super.logFrame(log, sp, ip, codeInfo, deoptFrame); + /* We don't have any metadata, so print less detailed information. */ + super.logFrame(log, sp, ip, codeInfo, null); + log.string("missing metadata"); } } } @@ -111,7 +116,7 @@ protected final boolean visitFrame(Pointer sp, CodePointer ip, CodeInfo codeInfo @Override protected final boolean unknownFrame(Pointer sp, CodePointer ip, DeoptimizedFrame deoptimizedFrame, Object data) { Log log = (Log) data; - logFrameRaw(log, sp, ip); + logFrameRaw(log, sp, ip, WordFactory.nullPointer()); if (DeoptimizationSupport.enabled()) { log.string(" deoptFrame=").object(deoptimizedFrame); } @@ -122,14 +127,17 @@ protected final boolean unknownFrame(Pointer sp, CodePointer ip, DeoptimizedFram @SuppressWarnings("unused") protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame) { - logFrameRaw(log, sp, ip); - log.string(" FrameSize ").signed(CodeInfoAccess.lookupTotalFrameSize(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip))); + logFrameRaw(log, sp, ip, codeInfo); printedFrames++; } - protected static void logFrameRaw(Log log, Pointer sp, CodePointer ip) { + protected static void logFrameRaw(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo) { log.string("SP ").zhex(sp); log.string(" IP ").zhex(ip); + if (codeInfo.isNonNull()) { + long frameSize = CodeInfoAccess.lookupTotalFrameSize(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip)); + log.string(" size=").signed(frameSize, 4, Log.LEFT_ALIGN); + } } } @@ -140,22 +148,24 @@ public Stage1StackFramePrintVisitor() { @Override protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame) { if (deoptFrame != null) { - logVirtualFrames(log, sp, ip, deoptFrame); + logVirtualFrames(log, sp, ip, codeInfo, deoptFrame); } else { logStackFrame(log, sp, ip, codeInfo); } } - protected void logVirtualFrames(Log log, Pointer sp, CodePointer ip, DeoptimizedFrame deoptFrame) { + protected void logVirtualFrames(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame) { for (DeoptimizedFrame.VirtualFrame frame = deoptFrame.getTopFrame(); frame != null; frame = frame.getCaller()) { if (printedFrames >= MAX_STACK_FRAMES_PER_THREAD_TO_PRINT) { log.string("... (truncated)").newline(); break; } - logFrameRaw(log, sp, ip); + boolean compilationRoot = frame.getCaller() == null; + printFrameIdentifier(log, WordFactory.nullPointer(), deoptFrame, compilationRoot); + logFrameRaw(log, sp, ip, codeInfo); logFrameInfo(log, frame.getFrameInfo(), ImageCodeInfo.CODE_INFO_NAME + ", deopt"); - if (frame.getCaller() != null) { + if (!compilationRoot) { log.newline(); } printedFrames++; @@ -163,10 +173,12 @@ protected void logVirtualFrames(Log log, Pointer sp, CodePointer ip, Deoptimized } private void logStackFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo) { - logFrameRaw(log, sp, ip); + printFrameIdentifier(log, codeInfo, null, true); + logFrameRaw(log, sp, ip, codeInfo); log.spaces(2); - CodeInfoAccess.log(codeInfo, log); - log.string(" name = ").string(CodeInfoAccess.getName(codeInfo)); + if (DeoptimizationSupport.enabled()) { + log.string("[").string(CodeInfoAccess.getName(codeInfo)).string("] "); + } printedFrames++; } @@ -177,9 +189,28 @@ protected static void logFrameInfo(Log log, FrameInfoQueryResult frameInfo, Stri } frameInfo.log(log); } + + protected static void printFrameIdentifier(Log log, CodeInfo codeInfo, DeoptimizedFrame deoptFrame, boolean isCompilationRoot) { + char ch = getFrameIdentifier(codeInfo, deoptFrame, isCompilationRoot); + log.character(ch).spaces(2); + } + + private static char getFrameIdentifier(CodeInfo codeInfo, DeoptimizedFrame deoptFrame, boolean isCompilationRoot) { + if (deoptFrame != null) { + return 'D'; + } else if (!isCompilationRoot) { + return 'i'; + } else if (codeInfo == CodeInfoTable.getImageCodeInfo()) { + return 'A'; + } else { + return 'J'; + } + } } - /** Walk the stack printing each frame. */ + /** + * Walk the stack printing each frame. + */ @NeverInline("debugger breakpoint") @Uninterruptible(reason = "Called from uninterruptible code.") public static void printBacktrace() { @@ -204,7 +235,7 @@ public static boolean printStacktrace(Pointer startSP, CodePointer startIP, Stag @Uninterruptible(reason = "CodeInfo in JavaStackWalk is currently null, so printing to log is safe right now.", calleeMustBe = false) private static void logFrameAnchor(Log log, Pointer startSP, CodePointer startIP) { - Stage0StackFramePrintVisitor.logFrameRaw(log, startSP, startIP); + Stage0StackFramePrintVisitor.logFrameRaw(log, startSP, startIP, WordFactory.nullPointer()); log.string(" IP is not within Java code. Trying frame anchor of last Java frame instead.").newline(); } } 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 d857bcbb8e47..babd82dd0394 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 @@ -44,6 +44,7 @@ import com.oracle.svm.core.SubstrateOptions.ConcealedOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.UnmanagedMemoryUtil; +import com.oracle.svm.core.collections.RingBuffer; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.RestrictHeapAccess; @@ -56,7 +57,6 @@ import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.thread.VMThreads.SafepointBehavior; import com.oracle.svm.core.thread.VMThreads.StatusSupport; -import com.oracle.svm.core.util.RingBuffer; import com.oracle.svm.core.util.VMError; /** @@ -902,7 +902,7 @@ private static class VMOpHistory { @Platforms(Platform.HOSTED_ONLY.class) VMOpHistory() { - history = new RingBuffer<>(15, VMOpStatusChange::new); + history = new RingBuffer<>(SubstrateOptions.DiagnosticBufferSize.getValue(), VMOpStatusChange::new); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) 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 ed8925cc0c35..5e262bb516c0 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 @@ -48,8 +48,8 @@ * 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 - * counter values. + * {@link CounterSupport#logValues} needs to be called manually at the end of the application to + * print counter values. * * Use this class in the following way: * @@ -206,15 +206,6 @@ public void add(long increment) { public void reset() { value = 0; } - - /** - * Prints all counters of all enabled groups to the {@link Log}. - */ - public static void logValues(Log log) { - for (Counter.Group group : ImageSingletons.lookup(CounterSupport.class).enabledGroups) { - group.logValues(log); - } - } } @TargetClass(com.oracle.svm.core.util.Counter.class) @@ -243,13 +234,3 @@ public void addGroup(Group group) { value.add(group); } } - -class CounterSupport { - - final Counter.Group[] enabledGroups; - - CounterSupport(Counter.Group[] enabledGroups) { - this.enabledGroups = enabledGroups; - } - -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/CounterSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/CounterSupport.java new file mode 100644 index 000000000000..ebab33d660a2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/CounterSupport.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.util; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.log.Log; + +public class CounterSupport { + private final Counter.Group[] enabledGroups; + + @Platforms(Platform.HOSTED_ONLY.class) + CounterSupport(Counter.Group[] enabledGroups) { + this.enabledGroups = enabledGroups; + } + + @Fold + public static CounterSupport singleton() { + return ImageSingletons.lookup(CounterSupport.class); + } + + /** + * Prints all counters of all enabled groups to the {@link Log}. + */ + public void logValues(Log log) { + for (Counter.Group group : enabledGroups) { + group.logValues(log); + } + } + + public boolean hasCounters() { + return enabledGroups != null && enabledGroups.length > 0; + } + +}