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 4587cedad8f7..6a6fabd0ad8a 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 @@ -128,9 +128,9 @@ public HeapImpl(int pageSize) { this.gcImpl = new GCImpl(); this.runtimeCodeInfoGcSupport = new RuntimeCodeInfoGCSupportImpl(); HeapParameters.initialize(); - DiagnosticThunkRegistry.singleton().register(new DumpHeapSettingsAndStatistics()); - DiagnosticThunkRegistry.singleton().register(new DumpHeapUsage()); - DiagnosticThunkRegistry.singleton().register(new DumpChunkInformation()); + DiagnosticThunkRegistry.singleton().add(new DumpHeapSettingsAndStatistics()); + DiagnosticThunkRegistry.singleton().add(new DumpHeapUsage()); + DiagnosticThunkRegistry.singleton().add(new DumpChunkInformation()); } @Fold @@ -647,7 +647,7 @@ public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaH if (printLocationInfo(log, ptr, allowJavaHeapAccess, allowUnsafeOperations)) { if (allowJavaHeapAccess && objectHeaderImpl.pointsToObjectHeader(ptr)) { log.indent(true); - SubstrateDiagnostics.printObjectInfo(log, ptr); + SubstrateDiagnostics.printObjectInfo(log, ptr.toObject()); log.redent(false); } return true; @@ -875,6 +875,8 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.string("Heap base: ").zhex(KnownIntrinsics.heapBase()).newline(); } log.string("Object reference size: ").signed(ConfigurationValues.getObjectLayout().getReferenceSize()).newline(); + log.string("Reserved object header bits: 0b").number(Heap.getHeap().getObjectHeader().getReservedBitsMask(), 2, false).newline(); + log.string("Aligned chunk size: ").unsigned(HeapParameters.getAlignedHeapChunkSize()).newline(); log.string("Large array threshold: ").unsigned(HeapParameters.getLargeArrayThreshold()).newline(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java index 9a99d1d26245..f870ac2dc944 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java @@ -166,7 +166,13 @@ public boolean walkObjects(ObjectVisitor visitor) { } public void logUsage(Log log, boolean logIfEmpty) { - UnsignedWord chunkBytes = getChunkBytes(); + UnsignedWord chunkBytes; + if (isEdenSpace() && !VMOperation.isGCInProgress()) { + chunkBytes = HeapImpl.getAccounting().getEdenUsedBytes(); + } else { + chunkBytes = getChunkBytes(); + } + if (logIfEmpty || chunkBytes.aboveThan(0)) { log.string(getName()).string(": ").rational(chunkBytes, GCImpl.M, 2).string("M (") .rational(accounting.getAlignedChunkBytes(), GCImpl.M, 2).string("M in ").signed(accounting.getAlignedChunkCount()).string(" aligned chunks, ") diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/DumpLinuxOSInfo.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/DumpLinuxOSInfo.java new file mode 100644 index 000000000000..d807fade3116 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/DumpLinuxOSInfo.java @@ -0,0 +1,109 @@ +/* + * 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.posix.linux; + +import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.word.Pointer; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.SubstrateDiagnostics; +import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry; +import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.heap.RestrictHeapAccess; +import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.os.RawFileOperationSupport; + +class DumpLinuxOSInfo extends SubstrateDiagnostics.DiagnosticThunk { + private static final CGlobalData MAX_THREADS_PATH = CGlobalDataFactory.createCString("/proc/sys/kernel/threads-max"); + private static final CGlobalData MAX_MAPPINGS_PATH = CGlobalDataFactory.createCString("/proc/sys/vm/max_map_count"); + private static final CGlobalData MAX_PID_PATH = CGlobalDataFactory.createCString("/proc/sys/kernel/pid_max"); + + @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("OS information:").indent(true); + + log.string("Max threads: "); + printFirstLine(log, MAX_THREADS_PATH.get()); + log.newline(); + + log.string("Max memory mappings: "); + printFirstLine(log, MAX_MAPPINGS_PATH.get()); + log.newline(); + + log.string("Max PID: "); + printFirstLine(log, MAX_PID_PATH.get()); + log.newline(); + + log.indent(false); + } + + private static void printFirstLine(Log log, CCharPointer filename) { + RawFileOperationSupport fs = RawFileOperationSupport.nativeByteOrder(); + RawFileOperationSupport.RawFileDescriptor fd = fs.open(filename, RawFileOperationSupport.FileAccessMode.READ); + if (!fs.isValid(fd)) { + log.string("unknown"); + return; + } + + try { + int bufferSize = 64; + CCharPointer buffer = StackValue.get(bufferSize); + long readBytes = fs.read(fd, (Pointer) buffer, WordFactory.unsigned(bufferSize)); + int length = countLineBytes(buffer, NumUtil.safeToInt(readBytes)); + log.string(buffer, length); + } finally { + fs.close(fd); + } + } + + private static int countLineBytes(CCharPointer buffer, int len) { + for (int i = 0; i < len; i++) { + if (buffer.read(i) == '\n') { + return i; + } + } + return len; + } +} + +@AutomaticallyRegisteredFeature +class DumpLinuxOSInfoFeature implements InternalFeature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + DiagnosticThunkRegistry.singleton().addAfter(new DumpLinuxOSInfo(), SubstrateDiagnostics.DumpMachineInfo.class); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java index cfdb4d115a98..950535e188e5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java @@ -264,7 +264,7 @@ public static void printHub(@SuppressWarnings("unused") IsolateThread thread, Po @CEntryPoint(name = "svm_dbg_print_obj", include = IncludeDebugHelperMethods.class, publishAs = Publish.SymbolOnly) @CEntryPointOptions(prologue = SetThreadAndHeapBasePrologue.class, epilogue = NoEpilogue.class) public static void printObject(@SuppressWarnings("unused") IsolateThread thread, Pointer objPtr) { - SubstrateDiagnostics.printObjectInfo(Log.log(), objPtr); + SubstrateDiagnostics.printObjectInfo(Log.log(), objPtr.toObject()); Log.log().newline(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Processor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Processor.java new file mode 100644 index 000000000000..dc2889d096b4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Processor.java @@ -0,0 +1,66 @@ +/* + * 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; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.util.VMError; + +@AutomaticallyRegisteredImageSingleton +public class Processor { + private int lastQueriedActiveProcessorCount = -1; + + @Fold + public static Processor singleton() { + return ImageSingletons.lookup(Processor.class); + } + + public int getActiveProcessorCount() { + VMError.guarantee(!SubstrateUtil.HOSTED, "must not be executed during the image build"); + + int result = getActiveProcessorCount0(); + lastQueriedActiveProcessorCount = result; + return result; + } + + private static int getActiveProcessorCount0() { + int optionValue = SubstrateOptions.ActiveProcessorCount.getValue(); + if (optionValue > 0) { + return optionValue; + } + + if (SubstrateOptions.MultiThreaded.getValue()) { + return Containers.activeProcessorCount(); + } else { + return 1; + } + } + + public int getLastQueriedActiveProcessorCount() { + return lastQueriedActiveProcessorCount; + } +} 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 229fc216c4b8..64a59683f117 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,9 +34,11 @@ import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.nodes.PauseNode; +import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.word.ObjectAccess; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; @@ -69,12 +71,17 @@ import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.PhysicalMemory; import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.jdk.Jvm; +import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicWord; import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.RuntimeOptionKey; +import com.oracle.svm.core.os.VirtualMemoryProvider; import com.oracle.svm.core.stack.JavaFrameAnchor; import com.oracle.svm.core.stack.JavaFrameAnchors; import com.oracle.svm.core.stack.JavaStackWalker; @@ -92,6 +99,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.util.CounterSupport; import com.oracle.svm.core.util.TimeUtils; +import com.oracle.svm.core.util.VMError; public class SubstrateDiagnostics { private static final int MAX_THREADS_TO_PRINT = 100_000; @@ -146,16 +154,59 @@ public static void printLocationInfo(Log log, UnsignedWord value, boolean allowJ } } - @Uninterruptible(reason = "Called with a raw object pointer.", calleeMustBe = false) - public static void printObjectInfo(Log log, Pointer ptr) { - DynamicHub objHub = Heap.getHeap().getObjectHeader().readDynamicHubFromPointer(ptr); - if (objHub == DynamicHub.fromClass(DynamicHub.class)) { - // The pointer is already a hub, so print some information about the hub. - DynamicHub hub = (DynamicHub) ptr.toObject(); + public static void printObjectInfo(Log log, Object obj) { + if (obj instanceof DynamicHub hub) { + // The object itself is a dynamic hub, so print some information about the hub. log.string("is the hub of ").string(hub.getName()); + return; + } + + // Print some information about the object. + log.string("is an object of type "); + + if (obj instanceof Throwable e) { + log.exception(e, 2); } else { - // The pointer is an object, so print some information about the object's hub. - log.string("is an object of type ").string(objHub.getName()); + log.string(obj.getClass().getName()); + + if (obj instanceof String s) { + log.string(": ").string(s, 60); + } else { + int layoutEncoding = DynamicHub.fromClass(obj.getClass()).getLayoutEncoding(); + + if (LayoutEncoding.isArrayLike(layoutEncoding)) { + int length = ArrayLengthNode.arrayLength(obj); + log.string(" with length ").signed(length).string(": "); + + printSomeArrayData(log, obj, length, layoutEncoding); + } + } + } + } + + private static void printSomeArrayData(Log log, Object obj, int length, int layoutEncoding) { + int elementSize = LayoutEncoding.getArrayIndexScale(layoutEncoding); + int arrayBaseOffset = LayoutEncoding.getArrayBaseOffsetAsInt(layoutEncoding); + + int maxPrintedBytes = elementSize == 1 ? 8 : 16; + int printedElements = UninterruptibleUtils.Math.min(length, maxPrintedBytes / elementSize); + + UnsignedWord curOffset = WordFactory.unsigned(arrayBaseOffset); + UnsignedWord endOffset = curOffset.add(WordFactory.unsigned(printedElements).multiply(elementSize)); + while (curOffset.belowThan(endOffset)) { + switch (elementSize) { + case 1 -> log.zhex(ObjectAccess.readByte(obj, curOffset)); + case 2 -> log.zhex(ObjectAccess.readShort(obj, curOffset)); + case 4 -> log.zhex(ObjectAccess.readInt(obj, curOffset)); + case 8 -> log.zhex(ObjectAccess.readLong(obj, curOffset)); + default -> throw VMError.shouldNotReachHereAtRuntime(); + } + log.spaces(1); + curOffset = curOffset.add(elementSize); + } + + if (printedElements < length) { + log.string("..."); } } @@ -797,7 +848,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev } } - static class DumpGeneralInfo extends DiagnosticThunk { + static class DumpVMInfo extends DiagnosticThunk { @Override public int maxInvocationCount() { return 1; @@ -806,14 +857,61 @@ 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("General information:").indent(true); - log.string("VM version: ").string(ImageSingletons.lookup(VM.class).version); + log.string("Build time information:").indent(true); + + VM vm = ImageSingletons.lookup(VM.class); + log.string("Version: ").string(vm.version).string(", ").string(vm.info).newline(); Platform platform = ImageSingletons.lookup(Platform.class); - log.string(", ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); + log.string("Platform: ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); + log.string("Page size: ").unsigned(SubstrateOptions.getPageSize()).newline(); + log.string("Container support: ").bool(Containers.Options.UseContainerSupport.getValue()).newline(); + log.string("CPU features used for AOT compiled code: ").string(getBuildTimeCpuFeatures()).newline(); + log.indent(false); + } - log.string("Current timestamp: ").unsigned(System.currentTimeMillis()).newline(); + @Fold + static String getBuildTimeCpuFeatures() { + return String.join(", ", CPUFeatureAccess.singleton().buildtimeCPUFeatures().stream().map(Enum::toString).toList()); + } + } + + public static class DumpMachineInfo 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("Runtime information:").indent(true); + + int activeProcessorCount = Processor.singleton().getLastQueriedActiveProcessorCount(); + log.string("CPU cores (container): "); + if (activeProcessorCount > 0) { + log.signed(activeProcessorCount).newline(); + } else { + log.string("unknown").newline(); + } + + log.string("CPU cores (OS): "); + if (SubstrateOptions.JNI.getValue()) { + log.signed(Jvm.JVM_ActiveProcessorCount()).newline(); + } else { + log.string("unknown").newline(); + } + + log.string("Memory: "); + if (PhysicalMemory.isInitialized()) { + log.rational(PhysicalMemory.getCachedSize(), 1024 * 1024, 0).string("M").newline(); + } else { + log.string("unknown").newline(); + } + + log.string("Page size: ").unsigned(VirtualMemoryProvider.get().getGranularity()).newline(); log.string("VM uptime: ").rational(Isolates.getCurrentUptimeMillis(), TimeUtils.millisPerSecond, 3).string("s").newline(); + log.string("Current timestamp: ").unsigned(System.currentTimeMillis()).newline(); CodeInfo info = CodeInfoTable.getImageCodeInfo(); Pointer codeStart = (Pointer) CodeInfoAccess.getCodeStart(info); @@ -821,14 +919,8 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev 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 { @@ -883,7 +975,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev 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). + * of a call (i.e., only the arguments and the return address are on the stack). */ int expectedStackAlignment = ConfigurationValues.getTarget().stackAlignment; if (sp.unsignedRemainder(expectedStackAlignment).notEqual(0) && sp.unsignedRemainder(ConfigurationValues.getTarget().wordSize).equal(0)) { @@ -906,20 +998,17 @@ private static void startStackWalkInMostLikelyCaller(Log log, int invocationCoun stackBase = originalSp.add(32); } - /* Search until we find a valid (return address, stack pointer) tuple. */ + /* Search until we find a valid return address. We may encounter false-positives. */ 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; - } + 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); } @@ -1147,7 +1236,8 @@ public static synchronized DiagnosticThunkRegistry singleton() { thunks.add(new DumpCurrentVMOperation()); thunks.add(new DumpVMOperationHistory()); thunks.add(new VMLockSupport.DumpVMMutexes()); - thunks.add(new DumpGeneralInfo()); + thunks.add(new DumpVMInfo()); + thunks.add(new DumpMachineInfo()); if (ImageSingletons.contains(JavaMainWrapper.JavaMainSupport.class)) { thunks.add(new DumpCommandLine()); } @@ -1164,18 +1254,39 @@ public static synchronized DiagnosticThunkRegistry singleton() { Arrays.fill(initialInvocationCount, 1); } - /** - * Register a diagnostic thunk to be called after a segfault. - */ @Platforms(Platform.HOSTED_ONLY.class) - public synchronized void register(DiagnosticThunk diagnosticThunk) { + public synchronized void add(DiagnosticThunk thunk) { diagnosticThunks = Arrays.copyOf(diagnosticThunks, diagnosticThunks.length + 1); - diagnosticThunks[diagnosticThunks.length - 1] = diagnosticThunk; + diagnosticThunks[diagnosticThunks.length - 1] = thunk; initialInvocationCount = Arrays.copyOf(initialInvocationCount, initialInvocationCount.length + 1); initialInvocationCount[initialInvocationCount.length - 1] = 1; } + @Platforms(Platform.HOSTED_ONLY.class) + public synchronized void addAfter(DiagnosticThunk thunk, Class before) { + int insertPos = indexOf(before) + 1; + + DiagnosticThunk[] newThunks = new DiagnosticThunk[diagnosticThunks.length + 1]; + System.arraycopy(diagnosticThunks, 0, newThunks, 0, insertPos); + newThunks[insertPos] = thunk; + System.arraycopy(diagnosticThunks, insertPos, newThunks, insertPos + 1, diagnosticThunks.length - insertPos); + diagnosticThunks = newThunks; + + initialInvocationCount = Arrays.copyOf(initialInvocationCount, initialInvocationCount.length + 1); + initialInvocationCount[initialInvocationCount.length - 1] = 1; + } + + @Platforms(Platform.HOSTED_ONLY.class) + private int indexOf(Class clazz) { + for (int i = 0; i < diagnosticThunks.length; i++) { + if (diagnosticThunks[i].getClass() == clazz) { + return i; + } + } + throw VMError.shouldNotReachHere("Could not find diagnostic thunk " + clazz); + } + @Fold int size() { return diagnosticThunks.length; 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 0aee8c452f88..36d2d10d2535 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 @@ -48,6 +48,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.FrameAccess; +import com.oracle.svm.core.Isolates; import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.ReservedRegisters; import com.oracle.svm.core.SubstrateOptions; @@ -83,6 +84,7 @@ 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.TimeUtils; import com.oracle.svm.core.util.VMError; import jdk.internal.misc.Unsafe; @@ -1121,7 +1123,7 @@ private static JavaConstant readConstant(Pointer addr, SignedWord offset, JavaKi } private static void printDeoptimizedFrame(Log log, Pointer sp, DeoptimizedFrame deoptimizedFrame, FrameInfoQueryResult sourceFrameInfo, boolean printOnlyTopFrames) { - log.string("[Deoptimization of frame (timestamp ").unsigned(System.currentTimeMillis()).string(")").newline(); + log.string("[Deoptimization of frame (").rational(Isolates.getCurrentUptimeMillis(), TimeUtils.millisPerSecond, 3).string("s)").newline(); SubstrateInstalledCode installedCode = deoptimizedFrame.getSourceInstalledCode(); if (installedCode != null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java index e1dbdbc5c587..76c895499762 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java @@ -673,7 +673,7 @@ private static void reportExceptionInterruptibly(Throwable exception) { @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in fatal error handling.") private static void logException(Throwable exception) { try { - Log.log().exception(exception); + Log.log().exception(exception).newline(); } catch (Throwable ex) { /* Logging failed, so there is nothing we can do anymore to log. */ } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/AccessControllerUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/AccessControllerUtil.java index 043136a91901..e351ea04945d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/AccessControllerUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/AccessControllerUtil.java @@ -64,7 +64,7 @@ public Class getCaller() { static { @SuppressWarnings("unchecked") Class> cls = (Class>) (Object) ArrayDeque.class; - stack = FastThreadLocalFactory.createObject(cls, "AccessControlContextStack"); + stack = FastThreadLocalFactory.createObject(cls, "PrivilegedStack.AccessControlContextStack"); } @SuppressWarnings("unchecked") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java index a61fe0fe92a2..347e6591fd8f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java @@ -54,9 +54,8 @@ import org.graalvm.nativeimage.hosted.FieldValueTransformer; import org.graalvm.nativeimage.impl.InternalPlatform; -import com.oracle.svm.core.Containers; import com.oracle.svm.core.NeverInline; -import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.Processor; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.annotate.Alias; @@ -399,16 +398,7 @@ public void runFinalization() { @Substitute @Platforms(InternalPlatform.PLATFORM_JNI.class) private int availableProcessors() { - int optionValue = SubstrateOptions.ActiveProcessorCount.getValue(); - if (optionValue > 0) { - return optionValue; - } - - if (SubstrateOptions.MultiThreaded.getValue()) { - return Containers.activeProcessorCount(); - } else { - return 1; - } + return Processor.singleton().getActiveProcessorCount(); } } 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 bd7b2bb953a2..1350e99f0423 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 @@ -164,9 +164,8 @@ private static void doShutdown(CodePointer callerIP, String msg, Throwable ex) { } if (ex != null) { log.string(": ").exception(ex); - } else { - log.newline(); } + log.newline(); SubstrateDiagnostics.printFatalError(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); 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 29590c743b25..3472cb92a64a 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 @@ -136,30 +136,43 @@ protected Log() { public abstract Log string(String value); /** - * Prints all characters in the string, filling with spaces before or after. + * Prints all characters in the string, filling with spaces before or after. Does not do any + * platform- or charset-depending conversions. */ public abstract Log string(String str, int fill, int align); + /** + * Prints the string characters, up to the given maximum length. Does not do any platform- or + * charset-depending conversions. + */ + public abstract Log string(String value, int maxLen); + /** * Prints all characters in the array, without any platform- or charset-depending conversions. */ public abstract Log string(char[] value); /** - * Prints all bytes in the array, without any conversion. + * Prints all bytes in the array, without any platform- or charset-depending conversions. */ public abstract Log string(byte[] value); /** - * Prints the provided range of bytes in the array, without any conversion. + * Prints the provided range of bytes in the array, without any platform- or charset-depending + * conversions. */ public abstract Log string(byte[] value, int offset, int length); /** - * Prints the C string. + * Prints the null-terminated C string. */ public abstract Log string(CCharPointer value); + /** + * Prints {@code length} characters of the C string. + */ + public abstract Log string(CCharPointer value, int length); + /** * Prints the provided character. */ @@ -411,6 +424,11 @@ public Log string(String str, int fill, int align) { return this; } + @Override + public Log string(String value, int maxLen) { + return this; + } + @Override public Log string(char[] value) { return this; @@ -431,6 +449,11 @@ public Log string(CCharPointer value) { return this; } + @Override + public Log string(CCharPointer bytes, int length) { + return this; + } + @Override public Log character(char value) { return this; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/log/RealLog.java index 6445c1d9ad93..81e14036079a 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 @@ -92,6 +92,14 @@ public Log string(String str, int fill, int align) { return this; } + @Override + @NeverInline("Logging is always slow-path code") + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") + public Log string(String value, int maxLen) { + rawString(value, maxLen); + return this; + } + private static final char[] NULL_CHARS = "null".toCharArray(); @Override @@ -179,6 +187,14 @@ public Log string(CCharPointer value) { return this; } + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") + public Log string(CCharPointer bytes, int length) { + if (length == 0) { + return this; + } + return rawBytes(bytes, WordFactory.unsigned(length)); + } + @Override @NeverInline("Logging is always slow-path code") @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when logging.") @@ -661,7 +677,6 @@ public Log exception(Throwable t, int maxFrames) { printRemainingFramesCount(remaining); } } - newline(); return this; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java index ce053fcb3853..ff6343ac14dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java @@ -157,7 +157,7 @@ private static void reportRecursiveUnwind(Throwable exception) { */ private static void reportFatalUnwind(Throwable exception) { Log.log().string("Fatal error: exception unwind while thread is not in Java state: "); - Log.log().exception(exception); + Log.log().exception(exception).newline(); ImageSingletons.lookup(LogHandler.class).fatalError(); } @@ -169,7 +169,7 @@ private static void reportFatalUnwind(Throwable exception) { */ private static void reportUnhandledException(Throwable exception) { Log.log().string("Fatal error: unhandled exception in isolate ").hex(CurrentIsolate.getIsolate()).string(": "); - Log.log().exception(exception); + Log.log().exception(exception).newline(); ImageSingletons.lookup(LogHandler.class).fatalError(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java index 51bce278994e..10a8d795c6cc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java @@ -629,7 +629,7 @@ private static boolean tearDownPlatformThreads() { try { thread.interrupt(); // not final and subclasses can unexpectedly throw } catch (Throwable t) { - trace.string(" threw (ignored): ").exception(t); + trace.string(" threw (ignored): ").exception(t).newline(); } trace.newline().flush(); @@ -670,7 +670,7 @@ private static boolean tearDownPlatformThreads() { pool.shutdownNow(); } } catch (Throwable t) { - trace.string(" threw (ignored): ").exception(t); + trace.string(" threw (ignored): ").exception(t).newline(); } trace.newline().flush(); trace.string(" shutdown initiated: ").object(pool).newline().flush(); 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 84847d7e4da1..ea85580398f6 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 @@ -633,21 +633,26 @@ public static void guaranteeOwnsThreadMutex(String message, boolean allowUnspeci } public static boolean printLocationInfo(Log log, UnsignedWord value, boolean allowUnsafeOperations) { + if (!allowUnsafeOperations && !VMOperation.isInProgressAtSafepoint()) { + /* + * Iterating the threads or accessing thread locals of other threads is unsafe if we are + * outside a VM operation because the IsolateThread data structure could be freed at any + * time (we can't use any locking to prevent races). + */ + return false; + } + for (IsolateThread thread = firstThreadUnsafe(); thread.isNonNull(); thread = nextThread(thread)) { if (thread.equal(value)) { log.string("is a thread"); return true; } - 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. - 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; - } + 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 cc8287a13387..761478db4757 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 @@ -37,6 +37,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.BuildPhaseProvider.ReadyForCompilation; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; @@ -61,33 +62,43 @@ public static void setInfos(Collection infos) { 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(" = "); + log.signed(info.offset).string(": ").string(info.name).string(" = "); if (info.threadLocalClass == FastThreadLocalInt.class) { int value = primitiveData(thread).readInt(WordFactory.signed(info.offset)); - log.string("(int) ").signed(value).string(" (").zhex(value).string(")"); + log.string("(int) ").zhex(value).string(" (").signed(value).string(")"); } else if (info.threadLocalClass == FastThreadLocalLong.class) { long value = primitiveData(thread).readLong(WordFactory.signed(info.offset)); - log.string("(long) ").signed(value).string(" (").zhex(value).string(")"); + log.string("(long) ").zhex(value).string(" (").signed(value).string(")"); } else if (info.threadLocalClass == FastThreadLocalWord.class) { WordBase value = primitiveData(thread).readWord(WordFactory.signed(info.offset)); - log.string("(Word) ").signed(value).string(" (").zhex(value).string(")"); + log.string("(Word) ").zhex(value).string(" (").signed(value).string(")"); } else if (info.threadLocalClass == FastThreadLocalObject.class) { if (isJavaHeapAccessAllowed) { Object value = ObjectAccess.readObject(objectData(thread), WordFactory.signed(info.offset)); - log.string("(Object) "); - if (value == null) { - log.string("null"); - } else { - log.string(value.getClass().getName()).string(" (").zhex(Word.objectToUntrackedPointer(value)).string(")"); + log.string("(Object) ").zhex(Word.objectToUntrackedPointer(value)); + if (value != null) { + log.indent(true); + SubstrateDiagnostics.printObjectInfo(log, value); + log.redent(false); } } else { Word value = ReferenceAccess.singleton().readObjectAsUntrackedPointer(Word.objectToUntrackedPointer(objectData(thread)).add(info.offset), true); log.string("(Object) ").zhex(value); } } else if (info.threadLocalClass == FastThreadLocalBytes.class) { - log.string("(bytes) ").indent(true); - log.hexdump(primitiveData(thread).add(WordFactory.signed(info.offset)), 8, info.sizeInBytes / 8); - log.redent(false); + log.string("(bytes) "); + Pointer data = primitiveData(thread).add(WordFactory.signed(info.offset)); + if (info.sizeInBytes == 8) { + log.zhex(data.readWord(0)); + } else { + log.indent(true); + if (info.sizeInBytes % 8 == 0) { + log.hexdump(data, 8, info.sizeInBytes / 8); + } else { + log.hexdump(data, 1, info.sizeInBytes); + } + log.redent(false); + } } else { log.string("unknown class ").string(info.threadLocalClass.getName()); }