diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java index 482cd77c476f..0e2b6e673aa9 100644 --- a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java @@ -90,6 +90,7 @@ import org.graalvm.compiler.api.test.ModuleSupport; import org.graalvm.compiler.bytecode.Bytecodes; import org.graalvm.compiler.core.phases.HighTier; +import org.graalvm.compiler.core.phases.fuzzing.FuzzedSuites; import org.graalvm.compiler.core.test.ReflectionOptionDescriptors; import org.graalvm.compiler.debug.GlobalMetrics; import org.graalvm.compiler.debug.GraalError; @@ -1510,7 +1511,11 @@ public static CompileTheWorld create() { HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler(); HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); OptionValues harnessOptions = loadHarnessOptions(); - return new CompileTheWorld(jvmciRuntime, compiler, harnessOptions, graalRuntime.getOptions()); + OptionValues compilerOptions = graalRuntime.getOptions(); + if (Options.FuzzPhasePlan.getValue(harnessOptions)) { + compilerOptions = FuzzedSuites.fuzzingOptions(compilerOptions); + } + return new CompileTheWorld(jvmciRuntime, compiler, harnessOptions, compilerOptions); } public static void main(String[] args) throws Throwable { diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java index de4a0ee98763..e4786d22f4a8 100644 --- a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java @@ -40,6 +40,7 @@ import org.graalvm.compiler.core.phases.fuzzing.AbstractCompilationPlan; import org.graalvm.compiler.core.phases.fuzzing.FullFuzzedCompilationPlan; import org.graalvm.compiler.core.phases.fuzzing.FullFuzzedTierPlan; +import org.graalvm.compiler.core.phases.fuzzing.FuzzedSuites; import org.graalvm.compiler.core.phases.fuzzing.MinimalFuzzedCompilationPlan; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.core.test.TestPhase; @@ -120,8 +121,16 @@ protected void assertDeepEquals(Object expected, Object actual) { } } + protected OptionValues getOptions() { + OptionValues options = getInitialOptions(); + if (Boolean.getBoolean(COMPILATION_PLAN_FUZZING_SYSTEM_PROPERTY)) { + options = FuzzedSuites.fuzzingOptions(options); + } + return options; + } + protected void runTest(String name, Object... args) { - runTest(getInitialOptions(), name, args); + runTest(getOptions(), name, args); } protected void runTest(OptionValues options, String name, Object... args) { @@ -129,7 +138,7 @@ protected void runTest(OptionValues options, String name, Object... args) { } protected void runTest(Set shouldNotDeopt, String name, Object... args) { - runTest(getInitialOptions(), shouldNotDeopt, name, args); + runTest(getOptions(), shouldNotDeopt, name, args); } protected void runTest(OptionValues options, Set shouldNotDeopt, String name, Object... args) { @@ -254,10 +263,11 @@ private void testAgainstExpectedWithFuzzedCompilationPlan(OptionValues options, */ private void testFuzzedCompilationPlan(MinimalFuzzedCompilationPlan plan, OptionValues options, ResolvedJavaMethod method, Result expect, Set shouldNotDeopt, Object receiver, Object... args) { + OptionValues fuzzingOptions = FuzzedSuites.fuzzingOptions(options); fuzzedSuites = plan.getSuites(); Result actual = null; try { - actual = executeActualCheckDeopt(options, method, shouldNotDeopt, receiver, args); + actual = executeActualCheckDeopt(fuzzingOptions, method, shouldNotDeopt, receiver, args); } catch (AssumptionViolatedException e) { throw e; } catch (Throwable t) { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/core/phases/fuzzing/FuzzedSuites.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/core/phases/fuzzing/FuzzedSuites.java index b774942e1afd..b752632d5d7a 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/core/phases/fuzzing/FuzzedSuites.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/core/phases/fuzzing/FuzzedSuites.java @@ -28,6 +28,8 @@ import org.graalvm.compiler.nodes.GraphState; import org.graalvm.compiler.nodes.GraphState.MandatoryStages; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.tiers.Suites; /** @@ -68,6 +70,23 @@ public void saveFuzzedSuites(String dumpPath) { fullFuzzedCompilationPlan.saveCompilationPlan(dumpPath); } + /** + * Adjusts the {@code baseOptions} with extra compiler options to be used when running with a + * fuzzed phase plan. This can be used to relax some strict verification conditions that should + * always hold during normal compilations but are not strictly required for the correctness of + * fuzzed compilations. + * + * @return a new {@link OptionValues} instance containing extra compiler options + */ + public static OptionValues fuzzingOptions(OptionValues baseOptions) { + return new OptionValues(baseOptions, + /* + * Allow GVN even in a graph state where we would no longer allow GVN in + * normal compilations. + */ + CanonicalizerPhase.Options.CanonicalizerVerifyGVNAllowed, false); + } + @Override public String toString() { return fullFuzzedCompilationPlan.toString(); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/Invokable.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/Invokable.java index 1506145f1599..e07553468ddd 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/Invokable.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/Invokable.java @@ -92,6 +92,9 @@ default void updateInliningLogAfterRegister(StructuredGraph newGraph) { */ default void updateInliningLogAfterClone(Node other) { StructuredGraph graph = asFixedNodeOrNull().graph(); + if (graph == null) { + return; + } InliningLog log = graph.getInliningLog(); if (log != null) { // At this point, the invokable node was already added to the inlining log diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java index fd37116f4080..076058a5c1db 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java @@ -79,6 +79,9 @@ import org.graalvm.compiler.nodes.spi.Simplifiable; import org.graalvm.compiler.nodes.spi.SimplifierTool; import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.BasePhase; @@ -87,6 +90,13 @@ public class CanonicalizerPhase extends BasePhase { + public static class Options { + // @formatter:off + @Option(help = "Verify if the current graph state allows GVN to be performed.", type = OptionType.Debug) + public static final OptionKey CanonicalizerVerifyGVNAllowed = new OptionKey<>(true); + // @formatter:on + } + /** * Constants for types of canonicalization that can be performed. * @@ -527,9 +537,11 @@ public static boolean gvn(Node node, NodeClass nodeClass) { } public boolean tryGlobalValueNumbering(Node node, NodeClass nodeClass) { - assert ((StructuredGraph) node.graph()).getGraphState().isBeforeStage(StageFlag.PARTIAL_REDUNDANCY_SCHEDULE) : "GVN must not occur after expanding partially redundant nodes, trying to gvn " + - node + - " for graph " + node.graph(); + if (Options.CanonicalizerVerifyGVNAllowed.getValue(node.getOptions())) { + assert ((StructuredGraph) node.graph()).getGraphState().isBeforeStage( + StageFlag.PARTIAL_REDUNDANCY_SCHEDULE) : "GVN must not occur after expanding partially redundant nodes, trying to gvn " + node + " for graph " + + node.graph(); + } return gvn(node, nodeClass); } diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/util/GraphOrder.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/util/GraphOrder.java index ffe9c02843e0..7c9afeaef7e6 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/util/GraphOrder.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/util/GraphOrder.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeBitMap; import org.graalvm.compiler.graph.Position; +import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractEndNode; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.ConstantNode; @@ -181,6 +182,48 @@ private static boolean assertScheduleableBeforeFSA(final StructuredGraph graph) protected NodeBitMap processBlock(final HIRBlock block, final NodeBitMap currentState) { final List list = graph.getLastSchedule().getBlockToNodesMap().get(block); + AbstractBeginNode blockBegin = block.getBeginNode(); + if (blockBegin instanceof AbstractMergeNode merge) { + /** + * Phis aren't scheduled, so they need to be added explicitly. We must do + * this first because there might be floating nodes depending on the phis + * that have floated to the beginning of the block. + *

+ * + * That is, we might have a graph like this: + * + *

+                         *      ...   ...  (inputs from predecessor blocks)
+                         *        \   /
+                         *  +----- Phi
+                         *  |       |
+                         *  |      Pi (or any other floating node)
+                         *  |       |
+                         *  |   FrameState
+                         *  |       |
+                         *  +---> Merge
+                         * 
+ * + * In the schedule the order of these nodes can be: + * + *
+                         *     Pi
+                         *     FrameState
+                         *     Merge
+                         *     ...
+                         * 
+ * + * This may seem unnatural, but due to the cyclic dependency on the state, + * any other order would be unnatural as well. For the use case of checking + * the schedule, pretend that all phis in the block precede everything else. + */ + currentState.markAll(merge.phis()); + if (merge instanceof LoopBeginNode loopBegin) { + // remember the state at the loop entry, it's restored at exits + loopEntryStates.put(loopBegin, currentState.copy()); + } + } + /* * A stateAfter is not valid directly after its associated state split, but * right before the next fixed node. Therefore a pending stateAfter is kept that @@ -211,12 +254,7 @@ public void apply(Node from, Position p) { } if (node instanceof AbstractMergeNode) { - // phis aren't scheduled, so they need to be added explicitly - currentState.markAll(((AbstractMergeNode) node).phis()); - if (node instanceof LoopBeginNode) { - // remember the state at the loop entry, it's restored at exits - loopEntryStates.put((LoopBeginNode) node, currentState.copy()); - } + GraalError.guarantee(node == blockBegin, "block should contain only one merge, found %s, expected %s", node, blockBegin); } else if (node instanceof ProxyNode) { assert false : "proxy nodes should not be in the schedule"; } else if (node instanceof LoopExitNode) { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java index 4568eb705c02..f79d8a59a38a 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java @@ -320,7 +320,7 @@ public void lower(Node n, LoweringTool tool) { if (graph.getGuardsStage().areFrameStatesAtDeopts()) { lowerComputeObjectAddressNode((ComputeObjectAddressNode) n); } - } else if (n instanceof FloatingIntegerDivRemNode && tool.getLoweringStage() == LoweringTool.StandardLoweringStage.MID_TIER) { + } else if (n instanceof FloatingIntegerDivRemNode divRem && divRem.graph().isAfterStage(GraphState.StageFlag.FSA)) { lowerFloatingIntegerDivRem((FloatingIntegerDivRemNode) n, tool); } else if (!(n instanceof LIRLowerable)) { // Assume that nodes that implement both Lowerable and LIRLowerable will be handled @@ -346,6 +346,9 @@ protected void lowerFloatingIntegerDivRem(FloatingIntegerDivRemNode divRem, L divRemFixed.setCanDeopt(false); divRem.replaceAtUsagesAndDelete(divRemFixed); graph.addAfterFixed(insertAfter, divRemFixed); + if (tool.getLoweringStage() == LoweringTool.StandardLoweringStage.LOW_TIER) { + divRemFixed.lower(tool); + } } private static void lowerComputeObjectAddressNode(ComputeObjectAddressNode n) { 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/SerialGCOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java index c0c37dbeee7c..d04e6e6e876e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java @@ -76,10 +76,6 @@ public Integer getValue(OptionValues values) { @Option(help = "Print summary GC information after application main method returns. Serial GC only.", type = OptionType.Debug)// public static final RuntimeOptionKey PrintGCSummary = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); - /* Will be removed as part of GR-48148. */ - @Option(help = "Deprecated. Print a time stamp at each collection, if +PrintGC or +VerboseGC. Serial GC only.", type = OptionType.Debug)// - public static final RuntimeOptionKey PrintGCTimeStamps = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); - @Option(help = "Print the time for each of the phases of each collection, if +VerboseGC. Serial GC only.", type = OptionType.Debug)// public static final RuntimeOptionKey PrintGCTimes = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); 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/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index 0e1b21cb5dcc..ddbcf533339b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -46,12 +46,6 @@ public class PhysicalMemory { /** Implemented by operating-system specific code. */ public interface PhysicalMemorySupport { - - /* Will be removed in GR-48001. */ - default boolean hasSize() { - throw VMError.shouldNotReachHere("Unused, will be removed"); - } - /** Get the size of physical memory from the OS. */ UnsignedWord size(); } 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/stack/StackOverflowCheck.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java index 6f8383cdffcd..61a6bd4fd1ad 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java @@ -29,7 +29,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.option.HostedOptionKey; @@ -88,10 +87,6 @@ class Options { interface PlatformSupport { @Fold static PlatformSupport singleton() { - if (ImageSingletons.contains(OSSupport.class)) { - assert !ImageSingletons.contains(PlatformSupport.class); - return ImageSingletons.lookup(OSSupport.class); - } return ImageSingletons.lookup(PlatformSupport.class); } @@ -113,27 +108,6 @@ static PlatformSupport singleton() { boolean lookupStack(WordPointer stackBasePtr, WordPointer stackEndPtr); } - /* Only used by legacy code, will be removed as part of GR-48332. */ - interface OSSupport extends PlatformSupport { - /** The highest address of the stack or zero if not supported. */ - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - default UnsignedWord lookupStackBase() { - return WordFactory.zero(); - } - - /** The lowest address of the stack. */ - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - UnsignedWord lookupStackEnd(); - - @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - default boolean lookupStack(WordPointer stackBasePtr, WordPointer stackEndPtr) { - stackBasePtr.write(lookupStackBase()); - stackEndPtr.write(lookupStackEnd()); - return true; - } - } - @Fold static StackOverflowCheck singleton() { return ImageSingletons.lookup(StackOverflowCheck.class); 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()); }