From d88c987fa1ec476437226e641a9210b136790fee Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Wed, 26 Jan 2022 13:36:01 +0000 Subject: [PATCH 01/22] Reimplement inline method and line debug info generation using CompilationResultFrameTree --- .../objectfile/debugentry/DebugInfoBase.java | 55 ++-- .../objectfile/debugentry/MethodEntry.java | 8 +- .../oracle/objectfile/debugentry/Range.java | 97 ------ .../debuginfo/DebugInfoProvider.java | 47 ++- .../core/code/CompilationResultFrameTree.java | 67 ++++- .../src/com/oracle/svm/hosted/SVMHost.java | 4 + .../image/NativeImageDebugInfoProvider.java | 275 +++++++++++++----- 7 files changed, 354 insertions(+), 199 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 0ddd0911cfb4..5458d37ef936 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocationInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; import com.oracle.objectfile.elf.dwarf.DwarfDebugInfo; @@ -255,8 +256,8 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * Record all subranges even if they have no line or file so we at least get a symbol * for them and don't see a break in the address range. */ - debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> recursivelyAddSubRanges(debugLineInfo, primaryRange, classEntry, debugContext)); - primaryRange.mergeSubranges(debugContext); + HashMap subRangeIndex = new HashMap<>(); + debugCodeInfo.locationInfoProvider().forEach(debugLocationInfo -> addSubrange(debugLocationInfo, primaryRange, classEntry, subRangeIndex, debugContext)); })); debugInfoProvider.dataInfoProvider().forEach(debugDataInfo -> debugDataInfo.debugContext((debugContext) -> { @@ -338,44 +339,50 @@ ClassEntry lookupClassEntry(String typeName) { } /** - * Recursively creates subranges based on DebugLineInfo including, and appropriately linking, - * nested inline subranges. + * Recursively creates subranges based on DebugLocationInfo including, and appropriately + * linking, nested inline subranges. * - * @param lineInfo + * @param locationInfo * @param primaryRange * @param classEntry * @param debugContext - * @return the subrange for {@code lineInfo} linked with all its caller subranges up to the + * @return the subrange for {@code locationInfo} linked with all its caller subranges up to the * primaryRange */ @SuppressWarnings("try") - private Range recursivelyAddSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Range primaryRange, ClassEntry classEntry, DebugContext debugContext) { - if (lineInfo == null) { - return primaryRange; - } + private Range addSubrange(DebugLocationInfo locationInfo, Range primaryRange, ClassEntry classEntry, HashMap subRangeIndex, DebugContext debugContext) { /* * We still insert subranges for the primary method but they don't actually count as inline. * we only need a range so that subranges for inline code can refer to the top level line - * number + * number. */ - boolean isInline = lineInfo.getCaller() != null; - assert (isInline || (lineInfo.name().equals(primaryRange.getMethodName()) && TypeEntry.canonicalize(lineInfo.ownerType().toJavaName()).equals(primaryRange.getClassName()))); - - Range caller = recursivelyAddSubRanges(lineInfo.getCaller(), primaryRange, classEntry, debugContext); - final String fileName = lineInfo.fileName(); - final Path filePath = lineInfo.filePath(); - final ResolvedJavaType ownerType = lineInfo.ownerType(); - final String methodName = lineInfo.name(); - final int lo = primaryRange.getLo() + lineInfo.addressLo(); - final int hi = primaryRange.getLo() + lineInfo.addressHi(); - final int line = lineInfo.line(); + DebugLocationInfo callerLocationInfo = locationInfo.getCaller(); + boolean isInline = locationInfo.getCaller() != null; + // ranges with no caller should be for the primary method + // but we seem to be seeing inline snippets also + // assert (isInline || (locationInfo.name().equals(primaryRange.getMethodName()) && TypeEntry.canonicalize(locationInfo.ownerType().toJavaName()).equals(primaryRange.getClassName()))); + Range caller = (isInline ? subRangeIndex.get(callerLocationInfo) : primaryRange); + // the frame tree is walked topdown so inline ranges should always have a caller range + assert !(isInline && (caller == null)); + + final String fileName = locationInfo.fileName(); + final Path filePath = locationInfo.filePath(); + final ResolvedJavaType ownerType = locationInfo.ownerType(); + final String methodName = locationInfo.name(); + final int lo = primaryRange.getLo() + locationInfo.addressLo(); + final int hi = primaryRange.getLo() + locationInfo.addressHi(); + final int line = locationInfo.line(); ClassEntry subRangeClassEntry = ensureClassEntry(ownerType); - MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); + MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(locationInfo, this, debugContext); Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isInline, caller); classEntry.indexSubRange(subRange); + subRangeIndex.put(locationInfo, subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { - debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", + debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %s %s:%d [0x%x, 0x%x]", ownerType.toJavaName(), methodName, filePath, fileName, line, lo, hi); + locationInfo.localsProvider().forEach(localInfo -> { + debugContext.log(DebugContext.DETAILED_LEVEL, " local %s:%s = %s", localInfo.name(), localInfo.typeName(), localInfo.valueString()); + }); } return subRange; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index 7ab410ad6cb3..6fb3d811c9f5 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -27,7 +27,7 @@ package com.oracle.objectfile.debugentry; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocationInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; public class MethodEntry extends MemberEntry { @@ -154,9 +154,9 @@ public boolean isConstructor() { * @param debugMethodInfo */ public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo) { - if (debugMethodInfo instanceof DebugLineInfo) { - DebugLineInfo lineInfo = (DebugLineInfo) debugMethodInfo; - if (lineInfo.getCaller() != null) { + if (debugMethodInfo instanceof DebugLocationInfo) { + DebugLocationInfo locationInfo = (DebugLocationInfo) debugMethodInfo; + if (locationInfo.getCaller() != null) { /* this is a real inlined method not just a top level primary range */ setIsInlined(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 3d24a6e40aaa..92a9fed3718e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -26,8 +26,6 @@ package com.oracle.objectfile.debugentry; -import org.graalvm.compiler.debug.DebugContext; - /** * Details of a specific address range in a compiled method either a primary range identifying a * whole method or a sub-range identifying a sequence of instructions that belong to an inlined @@ -256,99 +254,4 @@ public boolean isLeaf() { public int getDepth() { return depth; } - - /** - * Minimizes the nodes in the tree that track the inline call hierarchy and associated code - * ranges. The initial range tree models the call hierarchy as presented in the original debug - * line info. It consists of a root node each of whose children is a sequence of linear call - * chains, either a single leaf node for some given file and line or a series of inline calls to - * such a leaf node. In this initial tree all node ranges in a given chain have the same lo and - * hi address and chains are properly ordered by range The merge algorithm works across siblings - * at successive depths starting at depth 1. Once all possible nodes at a given depth have been - * merged their children can then be merged. A successor node may only be merged into its - * predecessor if the nodes have contiguous ranges and idenitfy the same method, line and file. - * The range and children of the merged node are, respectively, the union of the input ranges - * and children. This preserves the invariant that child ranges lie within their parent range. - * - * @param debugContext - */ - public void mergeSubranges(DebugContext debugContext) { - Range next = getFirstCallee(); - if (next == null) { - return; - } - debugContext.log(DebugContext.DETAILED_LEVEL, "Merge subranges [0x%x, 0x%x] %s", lo, hi, getFullMethodNameWithParams()); - /* merge siblings together if possible, reparenting children to the merged node */ - while (next != null) { - next = next.maybeMergeSibling(debugContext); - } - /* now recurse down to merge children of whatever nodes remain */ - next = getFirstCallee(); - /* now this level is merged recursively merge children of each child node. */ - while (next != null) { - next.mergeSubranges(debugContext); - next = next.getSiblingCallee(); - } - } - - /** - * Removes and merges the next sibling returning the current node or it skips past the current - * node as is and returns the next sibling or null if no sibling exists. - */ - private Range maybeMergeSibling(DebugContext debugContext) { - Range sibling = getSiblingCallee(); - debugContext.log(DebugContext.DETAILED_LEVEL, "Merge subrange (maybe) [0x%x, 0x%x] %s", lo, hi, getFullMethodNameWithParams()); - if (sibling == null) { - /* all child nodes at this level have been merged */ - return null; - } - if (hi < sibling.lo) { - /* cannot merge non-contiguous ranges, move on. */ - return sibling; - } - if (getMethodEntry() != sibling.getMethodEntry()) { - /* cannot merge distinct callers, move on. */ - return sibling; - } - if (getLine() != sibling.getLine()) { - /* cannot merge callers with different line numbers, move on. */ - return sibling; - } - if (isLeaf() != sibling.isLeaf()) { - /* - * cannot merge leafs with non-leafs as that results in them becoming non-leafs and not - * getting proper line info - */ - return sibling; - } - /* splice out the sibling from the chain and update this one to include it. */ - unlink(debugContext, sibling); - /* relocate the siblings children to this node. */ - reparentChildren(debugContext, sibling); - /* return the merged node so we can maybe merge it again. */ - return this; - } - - private void unlink(DebugContext debugContext, Range sibling) { - assert hi == sibling.lo : String.format("gap in range [0x%x,0x%x] %s [0x%x,0x%x] %s", - lo, hi, getFullMethodNameWithParams(), sibling.getLo(), sibling.getHi(), sibling.getFullMethodNameWithParams()); - assert this.isInlined == sibling.isInlined : String.format("change in inlined [0x%x,0x%x] %s %s [0x%x,0x%x] %s %s", - lo, hi, getFullMethodNameWithParams(), Boolean.valueOf(this.isInlined), sibling.lo, sibling.hi, sibling.getFullMethodNameWithParams(), Boolean.valueOf(sibling.isInlined)); - debugContext.log(DebugContext.DETAILED_LEVEL, "Combining [0x%x, 0x%x] %s into [0x%x, 0x%x] %s", sibling.lo, sibling.hi, sibling.getFullMethodName(), lo, hi, getFullMethodNameWithParams()); - this.hi = sibling.hi; - this.siblingCallee = sibling.siblingCallee; - } - - private void reparentChildren(DebugContext debugContext, Range sibling) { - Range siblingNext = sibling.getFirstCallee(); - while (siblingNext != null) { - debugContext.log(DebugContext.DETAILED_LEVEL, "Reparenting [0x%x, 0x%x] %s to [0x%x, 0x%x] %s", siblingNext.lo, siblingNext.hi, siblingNext.getFullMethodName(), lo, hi, - getFullMethodNameWithParams()); - siblingNext.caller = this; - Range newSiblingNext = siblingNext.siblingCallee; - siblingNext.siblingCallee = null; - addCallee(siblingNext); - siblingNext = newSiblingNext; - } - } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 20e055c26a58..e17c7b7f7b55 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -31,6 +31,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; @@ -274,10 +275,10 @@ interface DebugCodeInfo extends DebugRangeInfo { int line(); /** - * @return a stream of records detailing line numbers and addresses within the compiled - * method. + * @return a stream of records detailing source local var and line locations within the + * compiled method. */ - Stream lineInfoProvider(); + Stream locationInfoProvider(); /** * @return the size of the method frame between prologue and epilogue. @@ -314,7 +315,7 @@ interface DebugDataInfo { * Access details of code generated for a specific outer or inlined method at a given line * number. */ - interface DebugLineInfo extends DebugRangeInfo { + interface DebugLocationInfo extends DebugRangeInfo { /** * @return the lowest address containing code generated for an outer or inlined code segment * reported at this line represented as an offset into the code segment. @@ -333,9 +334,43 @@ interface DebugLineInfo extends DebugRangeInfo { int line(); /** - * @return the {@link DebugLineInfo} of the nested inline caller-line + * @return the {@link DebugLocationInfo} of the nested inline caller-line */ - DebugLineInfo getCaller(); + DebugLocationInfo getCaller(); + + /** + * @return a stream of {@link DebugLocalInfo} objects identifying local or parameter + * variables present in the frame of the current range. + */ + Stream localsProvider(); + } + + /** + * A DebugLocalInfo identifies a local or parameter variable present in the current frame, which + * may be undefined. If not then the instance records its type and either its (constant) value + * or the register or slot location in which the value can be found. + */ + interface DebugLocalInfo { + enum LocalKind { + UNDEFINED, + REGISTER, + STACKSLOT, + CONSTANT + } + + String name(); + + String typeName(); + + String valueString(); + + LocalKind localKind(); + + int regIndex(); + + int stackSlot(); + + Constant constantValue(); } interface DebugFrameSizeChange { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java index cd726f9929ac..a1a09fc2f3c8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java @@ -26,10 +26,16 @@ package com.oracle.svm.core.code; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.function.Consumer; +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.meta.JavaValue; +import jdk.vm.ci.meta.Local; +import jdk.vm.ci.meta.LocalVariableTable; +import jdk.vm.ci.meta.ResolvedJavaMethod; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.code.SourceMapping; import org.graalvm.compiler.debug.DebugContext; @@ -214,7 +220,18 @@ public int compareTo(SourcePositionSupplier o) { return res; } if (o instanceof SourceMappingWrapper) { - assert this == o; + int thisSize = getSize(); + int thatSize = o.getSize(); + // sort zero length source mappings earlier + if (thisSize == 0) { + if (thatSize > 0) { + return -1; + } + } else if (thatSize == 0) { + if (thisSize > 0) { + return 1; + } + } return 0; } return -1; /* make Infopoints go first */ @@ -278,6 +295,48 @@ public String toString() { return String.format("-%s, span %d, bci %d, method %s", getPosStr(), getSpan(), frame.getBCI(), frame.getMethod().format("%h.%n(%p)")); } + public String getLocalsStr() { + String localsInfo = ""; + if (frame instanceof BytecodeFrame) { + BytecodeFrame bcf = (BytecodeFrame) frame; + Local[] locals = getLocalsBySlot(frame.getMethod()); + StringBuilder sb = new StringBuilder(); + if (locals == null) { + return localsInfo; + } + int combinedLength = Integer.min(locals.length, bcf.numLocals); + for (int i = 0; i < combinedLength; i++) { + JavaValue value = bcf.getLocalValue(i); + if (i > 0) { + sb.append(", "); + } + sb.append("li("); + Local local = locals != null ? locals[i] : null; + if (local != null) { + sb.append(local.getName()); + sb.append("="); + } + sb.append(value); + sb.append(")"); + } + localsInfo = sb.toString(); + } + return localsInfo; + } + + private static Local[] getLocalsBySlot(ResolvedJavaMethod method) { + LocalVariableTable lvt = method.getLocalVariableTable(); + Local[] nonEmptySortedLocals = null; + if (lvt != null) { + Local[] locals = lvt.getLocals(); + if (locals != null && locals.length > 0) { + nonEmptySortedLocals = Arrays.copyOf(locals, locals.length); + Arrays.sort(nonEmptySortedLocals, (Local l1, Local l2) -> l1.getSlot() - l2.getSlot()); + } + } + return nonEmptySortedLocals; + } + public void visit(Visitor visitor, Object... varargs) { visitor.apply(this, varargs); } @@ -678,12 +737,14 @@ private static final class FrameTreeDumper implements Visitor { private final Consumer printer; private final boolean onlyCallTree; private final boolean showSourcePos; + private final boolean showLocals; private final int maxDepth; FrameTreeDumper(Consumer printer, boolean onlyCallTree, boolean showSourcePos, int maxDepth) { this.printer = printer; this.onlyCallTree = onlyCallTree; this.showSourcePos = showSourcePos; + this.showLocals = true; this.maxDepth = maxDepth; } @@ -710,6 +771,10 @@ public void apply(FrameNode node, Object... args) { StackTraceElement stackTraceElement = node.getStackTraceElement(); printer.accept(" at " + stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber()); } + if (showLocals) { + printer.accept(" locals: "); + printer.accept(node.getLocalsStr()); + } printer.accept("\n"); node.visitChildren(this, level + 1); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 2de8664974b8..6741ce43e236 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -293,8 +293,12 @@ public boolean isInitialized(AnalysisType type) { @Override public GraphBuilderConfiguration updateGraphBuilderConfiguration(GraphBuilderConfiguration config, AnalysisMethod method) { +<<<<<<< HEAD return config.withRetainLocalVariables(retainLocalVariables()) .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())); +======= + return config.withRetainLocalVariables(retainLocalVariables()).withFullInfopoints(SubstrateOptions.GenerateDebugInfo.getValue() > 0); +>>>>>>> 30e67accfc64 (Reimplement inline method and line debug info generation using CompilationResultFrameTree) } private boolean retainLocalVariables() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index a3a5fb31b328..c85747530c63 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -25,35 +25,16 @@ */ package com.oracle.svm.hosted.image; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; - -import java.lang.reflect.Modifier; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; -import jdk.vm.ci.meta.JavaType; -import org.graalvm.compiler.code.CompilationResult; -import org.graalvm.compiler.code.SourceMapping; -import org.graalvm.compiler.core.common.CompressEncoding; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.graph.NodeSourcePosition; -import org.graalvm.nativeimage.ImageSingletons; - import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod; +import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.code.CompilationResultFrameTree.Builder; +import com.oracle.svm.core.code.CompilationResultFrameTree.CallNode; +import com.oracle.svm.core.code.CompilationResultFrameTree.FrameNode; +import com.oracle.svm.core.code.CompilationResultFrameTree.Visitor; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.graal.code.SubstrateBackend; @@ -77,13 +58,40 @@ import com.oracle.svm.hosted.substitute.SubstitutionField; import com.oracle.svm.hosted.substitute.SubstitutionMethod; import com.oracle.svm.hosted.substitute.SubstitutionType; - +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.LineNumberTable; +import jdk.vm.ci.meta.Local; +import jdk.vm.ci.meta.LocalVariableTable; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.Signature; +import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.core.common.CompressEncoding; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.nativeimage.ImageSingletons; + +import java.lang.reflect.Modifier; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; /** * Implementation of the DebugInfoProvider API interface that allows type, code and heap data info @@ -949,13 +957,38 @@ public int line() { } @Override - public Stream lineInfoProvider() { + public Stream locationInfoProvider() { + // can we still provide locals if we have no file name? if (fileName().length() == 0) { return Stream.empty(); } - return compilation.getSourceMappings().stream() - .filter(NativeImageDebugInfoProvider::filterLineInfoSourceMapping) - .map(sourceMapping -> new NativeImageDebugLineInfo(sourceMapping)); + final CallNode root = new Builder(debugContext, compilation.getTargetCodeSize(), true).build(compilation); + if (root == null) { + return Stream.empty(); + } + final List locationInfos = new ArrayList<>(); + final ResolvedJavaMethod rootMethod = root.frame.getMethod(); + if (SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue()) { + root.visitChildren(new Visitor() { + @Override + public void apply(FrameNode node, Object... args) { + if (node.frame.getCaller() == null && node.frame.getMethod() == rootMethod) { + locationInfos.add(new NativeImageDebugLocationInfo(node)); + } + } + }); + } else { + root.visitChildren(new Visitor() { + @Override + public void apply(FrameNode node, Object... args) { + NativeImageDebugLocationInfo callerInfo = (args.length == 0 ? null : (NativeImageDebugLocationInfo) args[0]); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(node, callerInfo); + locationInfos.add(locationInfo); + node.visitChildren(this, locationInfo); + } + }); + } + return locationInfos.stream(); } @Override @@ -1051,57 +1084,81 @@ public boolean isOverride() { } } - private static boolean filterLineInfoSourceMapping(SourceMapping sourceMapping) { - NodeSourcePosition sourcePosition = sourceMapping.getSourcePosition(); - /* Don't report line info for zero length ranges. */ - if (sourceMapping.getStartOffset() == sourceMapping.getEndOffset()) { - return false; - } - /* Don't report inline line info unless the user has configured it. */ - if (SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue() && sourcePosition.getCaller() != null) { - return false; - } - return true; - } - /** - * Implementation of the DebugLineInfo API interface that allows line number info (and more) to - * be passed to an ObjectFile when generation of debug info is enabled. + * Implementation of the DebugLocationInfo API interface that allows line number and local var + * info to be passed to an ObjectFile when generation of debug info is enabled. */ - private class NativeImageDebugLineInfo implements DebugLineInfo { + private class NativeImageDebugLocationInfo implements DebugLocationInfo { private final int bci; private final ResolvedJavaMethod method; private final int lo; private final int hi; private Path cachePath; private Path fullFilePath; - private DebugLineInfo callersLineInfo; + private DebugLocationInfo callersLocationInfo; + private List localInfoList; - NativeImageDebugLineInfo(SourceMapping sourceMapping) { - this(sourceMapping.getSourcePosition(), sourceMapping.getStartOffset(), sourceMapping.getEndOffset()); + NativeImageDebugLocationInfo(FrameNode frameNode) { + this(frameNode, null); } - NativeImageDebugLineInfo(DebugLineInfo lineInfo, NodeSourcePosition position) { - this(position, lineInfo.addressLo(), lineInfo.addressHi()); + NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { + BytecodePosition bcpos = frameNode.frame; + this.bci = bcpos.getBCI(); + this.method = bcpos.getMethod(); + this.lo = frameNode.getStartPos(); + this.hi = frameNode.getEndPos() + 1; + this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); + this.callersLocationInfo = callersLocationInfo; + computeFullFilePath(); + this.localInfoList = initLocalInfoList(bcpos); } - NativeImageDebugLineInfo(NodeSourcePosition position, int lo, int hi) { - this.bci = position.getBCI(); - this.method = position.getMethod(); - this.lo = lo; - this.hi = hi; - this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); - NodeSourcePosition callerPosition = position.getCaller(); - /* Skip substitutions with bytecode index -1 */ - while (callerPosition != null && callerPosition.isSubstitution() && callerPosition.getBCI() == -1) { - callerPosition = callerPosition.getCaller(); + private List initLocalInfoList(BytecodePosition bcpos) { + if (!(bcpos instanceof BytecodeFrame)) { + return null; } - if (callerPosition != null) { - callersLineInfo = new NativeImageDebugLineInfo(this, callerPosition); - } else { - callersLineInfo = null; + + BytecodeFrame frame = (BytecodeFrame) bcpos; + if (frame.numLocals == 0) { + return null; } - computeFullFilePath(); + Local[] localsBySlot = getLocalsBySlot(); + if (localsBySlot == null) { + // TODO perhaps try synthesising locals info here + return null; + } + int count = Integer.min(localsBySlot.length, frame.numLocals); + ArrayList localInfos = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + JavaValue value = frame.getLocalValue(i); + JavaKind kind = frame.getLocalValueKind(i); + Local l = localsBySlot[i]; + String name; + ResolvedJavaType type; + if (l != null) { + name = l.getName(); + type = l.getType().resolve(method.getDeclaringClass()); + } else { + name = "_" + i; + type = null; + } + localInfos.add(new NativeImageDebugLocalInfo(name, value, kind, type)); + } + return localInfos; + } + + private Local[] getLocalsBySlot() { + LocalVariableTable lvt = method.getLocalVariableTable(); + Local[] nonEmptySortedLocals = null; + if (lvt != null) { + Local[] locals = lvt.getLocals(); + if (locals != null && locals.length > 0) { + nonEmptySortedLocals = Arrays.copyOf(locals, locals.length); + Arrays.sort(nonEmptySortedLocals, (Local l1, Local l2) -> l1.getSlot() - l2.getSlot()); + } + } + return nonEmptySortedLocals; } @Override @@ -1240,8 +1297,17 @@ public int modifiers() { } @Override - public DebugLineInfo getCaller() { - return callersLineInfo; + public DebugLocationInfo getCaller() { + return callersLocationInfo; + } + + @Override + public Stream localsProvider() { + if (localInfoList != null) { + return localInfoList.stream(); + } else { + return Stream.empty(); + } } @SuppressWarnings("try") @@ -1287,6 +1353,81 @@ public int vtableOffset() { } } + public class NativeImageDebugLocalInfo implements DebugLocalInfo { + private final String name; + private ResolvedJavaType type; + private final JavaValue value; + private final JavaKind kind; + private LocalKind localKind; + + NativeImageDebugLocalInfo(String name, JavaValue value, JavaKind kind, ResolvedJavaType type) { + this.name = name; + this.value = value; + this.kind = kind; + this.type = type; + if (value instanceof RegisterValue) { + this.localKind = LocalKind.REGISTER; + } else if (value instanceof StackSlot) { + this.localKind = LocalKind.STACKSLOT; + } else if (value instanceof Constant) { + this.localKind = LocalKind.CONSTANT; + } else { + this.localKind = LocalKind.UNDEFINED; + } + } + + @Override + public String name() { + return name; + } + + @Override + public String typeName() { + if (type != null) { + return toJavaName(type); + } + if (kind == JavaKind.Object) { + return "java.lang.Object"; + } else { + return kind.getJavaName(); + } + } + + @Override + public String valueString() { + switch (localKind) { + case REGISTER: + return "reg[" + regIndex() + "]"; + case STACKSLOT: + return "stack[" + stackSlot() + "]"; + case CONSTANT: + return (constantValue() != null ? constantValue().toValueString() : "null"); + default: + return "-"; + } + } + + @Override + public LocalKind localKind() { + return null; + } + + @Override + public int regIndex() { + return ((RegisterValue) value).getRegister().encoding(); + } + + @Override + public int stackSlot() { + return ((StackSlot) value).getRawOffset(); + } + + @Override + public Constant constantValue() { + return null; + } + } + /** * Implementation of the DebugFrameSizeChange API interface that allows stack frame size change * info to be passed to an ObjectFile when generation of debug info is enabled. From 6456883748043dc89c32b9355db459a48ce014ab Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 31 Jan 2022 12:10:01 +0000 Subject: [PATCH 02/22] Update expected debuginfotest output. --- substratevm/mx.substratevm/testhello.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 1feef417b86a..4bb017f8a332 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -317,6 +317,7 @@ def test(): r"%s_z_\.java\.lang\.String \*name;"%(spaces_pattern), r"", r"%spublic:"%(spaces_pattern), + r"%svoid NamedGreeter\(java\.lang\.String \*\);"%(spaces_pattern), r"%svoid greet\(void\);"%(spaces_pattern), r"}"] else: @@ -325,6 +326,7 @@ def test(): r"%sjava\.lang\.String \*name;"%(spaces_pattern), r"", r"%spublic:"%(spaces_pattern), + r"%svoid NamedGreeter\(java\.lang\.String \*\);"%(spaces_pattern), r"%svoid greet\(void\);"%(spaces_pattern), r"}"] @@ -334,6 +336,7 @@ def test(): exec_string = execute("ptype 'hello.Hello$Greeter'") rexp = [r"type = class hello\.Hello\$Greeter : public java\.lang\.Object {", r"%spublic:"%(spaces_pattern), + r"%svoid Greeter\(void\);"%(spaces_pattern), r"%sstatic hello\.Hello\$Greeter \* greeter\(java\.lang\.String\[\] \*\);"%(spaces_pattern), r"}"] @@ -344,9 +347,6 @@ def test(): rexp = [r"type = class java\.lang\.Object : public _objhdr {", r"%spublic:"%(spaces_pattern), r"%svoid Object\(void\);"%(spaces_pattern), - r"%sprotected:"%(spaces_pattern), - r"%sjava\.lang\.Object \* clone\(void\);"%(spaces_pattern), - r"%spublic:"%(spaces_pattern), r"%sboolean equals\(java\.lang\.Object \*\);"%(spaces_pattern), r"%sjava\.lang\.Class \* getClass\(void\);"%(spaces_pattern), r"%sint hashCode\(void\);"%(spaces_pattern), @@ -517,9 +517,16 @@ def test(): # list inlineIs and inlineA and check that the listing maps to the inlined code instead of the actual code, # although not ideal this is how GDB treats inlined code in C/C++ as well - rexp = [r"130%snoInlineTest\(\);"%spaces_pattern] + rexp = [r'file: "hello/Hello\.java", line number: 125, symbol: "hello\.Hello::inlineIs"', + r"125%sinlineA\(\);"%spaces_pattern, + r'file: "hello/Hello\.java", line number: 126, symbol: "hello\.Hello::inlineIs"', + r"126%s}"%spaces_pattern] checker = Checker('list inlineIs', rexp) checker.check(execute("list inlineIs")) + rexp = [r'file: "hello/Hello\.java", line number: 130, symbol: "hello\.Hello::inlineA"', + r"130%snoInlineTest\(\);"%spaces_pattern, + r'file: "hello/Hello\.java", line number: 131, symbol: "hello\.Hello::inlineA"', + r"131%s}"%spaces_pattern] checker = Checker('list inlineA', rexp) checker.check(execute("list inlineA")) From 2070ace4d658b9767e7502bf21d31fe7658d4c1d Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 11 Feb 2022 14:48:29 +0000 Subject: [PATCH 03/22] Correct the algorithm that walks the compiler result frame tree. --- substratevm/mx.substratevm/testhello.py | 14 +- .../objectfile/debugentry/DebugInfoBase.java | 30 +- .../oracle/objectfile/debugentry/Range.java | 34 +- .../debuginfo/DebugInfoProvider.java | 8 + .../elf/dwarf/DwarfInfoSectionImpl.java | 84 ++-- .../core/code/CompilationResultFrameTree.java | 9 + .../image/NativeImageDebugInfoProvider.java | 415 +++++++++++++++++- 7 files changed, 489 insertions(+), 105 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 4bb017f8a332..72b5c130ec8b 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -517,10 +517,7 @@ def test(): # list inlineIs and inlineA and check that the listing maps to the inlined code instead of the actual code, # although not ideal this is how GDB treats inlined code in C/C++ as well - rexp = [r'file: "hello/Hello\.java", line number: 125, symbol: "hello\.Hello::inlineIs"', - r"125%sinlineA\(\);"%spaces_pattern, - r'file: "hello/Hello\.java", line number: 126, symbol: "hello\.Hello::inlineIs"', - r"126%s}"%spaces_pattern] + rexp = [r"125%sinlineA\(\);"%spaces_pattern] checker = Checker('list inlineIs', rexp) checker.check(execute("list inlineIs")) rexp = [r'file: "hello/Hello\.java", line number: 130, symbol: "hello\.Hello::inlineA"', @@ -533,7 +530,7 @@ def test(): execute("delete breakpoints") # Set breakpoint at inlined method and step through its nested inline methods exec_string = execute("break hello.Hello::inlineIs") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 130\."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: hello\.Hello::inlineIs\. \(2 locations\)"%(digits_pattern, address_pattern) checker = Checker('break inlineIs', rexp) checker.check(exec_string, skip_fails=False) @@ -578,15 +575,12 @@ def test(): execute("delete breakpoints") # Set breakpoint at method with inline and not-inlined invocation in same line exec_string = execute("break hello.Hello::inlineFrom") - rexp = r"Breakpoint %s at %s: hello\.Hello::inlineFrom\. \(4 locations\)"%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 141."%(digits_pattern, address_pattern) checker = Checker('break inlineFrom', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("info break 6") - rexp = [r"6.1%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:141"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.2%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:179"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.3%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:162"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.4%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:179"%(spaces_pattern, spaces_pattern, address_pattern)] + rexp = [r"6%sbreakpoint%skeep%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:141"%(spaces_pattern, spaces_pattern, spaces_pattern, spaces_pattern, address_pattern)] checker = Checker('info break inlineFrom', rexp) checker.check(exec_string) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 5458d37ef936..3c1448ed68d1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -357,33 +357,35 @@ private Range addSubrange(DebugLocationInfo locationInfo, Range primaryRange, Cl * number. */ DebugLocationInfo callerLocationInfo = locationInfo.getCaller(); - boolean isInline = locationInfo.getCaller() != null; - // ranges with no caller should be for the primary method - // but we seem to be seeing inline snippets also - // assert (isInline || (locationInfo.name().equals(primaryRange.getMethodName()) && TypeEntry.canonicalize(locationInfo.ownerType().toJavaName()).equals(primaryRange.getClassName()))); - Range caller = (isInline ? subRangeIndex.get(callerLocationInfo) : primaryRange); + boolean isTopLevel = callerLocationInfo == null; + assert (!isTopLevel || (locationInfo.name().equals(primaryRange.getMethodName()) && + TypeEntry.canonicalize(locationInfo.ownerType().toJavaName()).equals(primaryRange.getClassName()))); + Range caller = (isTopLevel ? primaryRange : subRangeIndex.get(callerLocationInfo)); // the frame tree is walked topdown so inline ranges should always have a caller range - assert !(isInline && (caller == null)); + assert caller != null; final String fileName = locationInfo.fileName(); final Path filePath = locationInfo.filePath(); + final String fullPath = (filePath == null ? "" : filePath.toString() + "/") + fileName; final ResolvedJavaType ownerType = locationInfo.ownerType(); final String methodName = locationInfo.name(); + final int loOff = locationInfo.addressLo(); + final int hiOff = locationInfo.addressHi() - 1; final int lo = primaryRange.getLo() + locationInfo.addressLo(); final int hi = primaryRange.getLo() + locationInfo.addressHi(); final int line = locationInfo.line(); + final boolean isPrologueEnd = locationInfo.isPrologueEnd(); ClassEntry subRangeClassEntry = ensureClassEntry(ownerType); MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(locationInfo, this, debugContext); - Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isInline, caller); + Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isTopLevel, caller, isPrologueEnd); classEntry.indexSubRange(subRange); subRangeIndex.put(locationInfo, subRange); - try (DebugContext.Scope s = debugContext.scope("Subranges")) { - debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %s %s:%d [0x%x, 0x%x]", - ownerType.toJavaName(), methodName, filePath, fileName, line, lo, hi); - locationInfo.localsProvider().forEach(localInfo -> { - debugContext.log(DebugContext.DETAILED_LEVEL, " local %s:%s = %s", localInfo.name(), localInfo.typeName(), localInfo.valueString()); - }); - } + debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %d %s:%d [0x%x, 0x%x] (%d, %d)", + ownerType.toJavaName(), methodName, subRange.getDepth(), fullPath, line, lo, hi, loOff, hiOff); + assert (callerLocationInfo == null || (callerLocationInfo.addressLo() <= loOff && callerLocationInfo.addressHi() >= hiOff)) : "parent range should enclose subrange!"; + locationInfo.localsProvider().forEach(localInfo -> { + debugContext.log(DebugContext.DETAILED_LEVEL, " local %s:%s = %s", localInfo.name(), localInfo.typeName(), localInfo.valueString()); + }); return subRange; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 92a9fed3718e..6bb1eb1d42ed 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -40,7 +40,7 @@ public class Range { private final int lo; private int hi; private final int line; - private final boolean isInlined; + private boolean isPrologueEnd; private final int depth; /** * This is null for a primary range. For sub ranges it holds the root of the call tree they @@ -71,30 +71,30 @@ public class Range { * Create a primary range. */ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line) { - this(stringTable, methodEntry, lo, hi, line, null, false, null); + this(stringTable, methodEntry, lo, hi, line, null, false, null, false); } /* * Create a primary or secondary range. */ - public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isInline, Range caller) { + public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isTopLevel, Range caller, boolean isPrologueEnd) { assert methodEntry != null; if (methodEntry.fileEntry != null) { stringTable.uniqueDebugString(methodEntry.fileEntry.getFileName()); stringTable.uniqueDebugString(methodEntry.fileEntry.getPathName()); } this.methodEntry = methodEntry; - this.fullMethodName = isInline ? stringTable.uniqueDebugString(constructClassAndMethodName()) : stringTable.uniqueString(constructClassAndMethodName()); + this.fullMethodName = isTopLevel ? stringTable.uniqueDebugString(constructClassAndMethodName()) : stringTable.uniqueString(constructClassAndMethodName()); this.fullMethodNameWithParams = constructClassAndMethodNameWithParams(); this.lo = lo; this.hi = hi; this.line = line; - this.isInlined = isInline; this.primary = primary; this.firstCallee = null; this.lastCallee = null; this.siblingCallee = null; this.caller = caller; + this.isPrologueEnd = isPrologueEnd; if (caller != null) { caller.addCallee(this); } @@ -210,6 +210,13 @@ public FileEntry getFileEntry() { return methodEntry.fileEntry; } + public int getFileIndex() { + // the primary range's class entry indexes all files defined by the compilation unit + Range primaryRange = (isPrimary() ? this : getPrimary()); + ClassEntry owner = primaryRange.methodEntry.ownerType(); + return owner.localFilesIdx(getFileEntry()); + } + public int getModifiers() { return methodEntry.modifiers; } @@ -227,10 +234,6 @@ public MethodEntry getMethodEntry() { return methodEntry; } - public boolean isInlined() { - return isInlined; - } - public Range getCaller() { return caller; } @@ -251,7 +254,20 @@ public boolean isLeaf() { return firstCallee == null; } + public boolean includesInlineRanges() { + Range child = firstCallee; + while (child != null && child.isLeaf()) { + child = child.siblingCallee; + } + return child != null; + } + public int getDepth() { return depth; } + + @SuppressWarnings("unused") + public boolean isPrologueEnd() { + return isPrologueEnd; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index e17c7b7f7b55..87fa65e82ca3 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -338,6 +338,14 @@ interface DebugLocationInfo extends DebugRangeInfo { */ DebugLocationInfo getCaller(); + /** + * Indicates whether the end of this leaf range corresponds to the end of a method prologue + * for either a top level of inline method. + * + * @return true if this range is a prologue end otherwise false + */ + boolean isPrologueEnd(); + /** * @return a stream of {@link DebugLocalInfo} objects identifying local or parameter * variables present in the frame of the current range. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 6a4d9d5e4238..f82112022f3f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -959,26 +959,19 @@ private int writeAbstractInlineMethods(DebugContext context, ClassEntry classEnt /** * Go through the subranges and generate concrete debug entries for inlined methods. */ - private int generateConcreteInlinedMethods(DebugContext context, ClassEntry classEntry, - PrimaryEntry primaryEntry, byte[] buffer, int p) { + private int generateConcreteInlinedMethods(DebugContext context, PrimaryEntry primaryEntry, byte[] buffer, int p) { Range primary = primaryEntry.getPrimary(); if (primary.isLeaf()) { return p; } int pos = p; log(context, " [0x%08x] concrete entries [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodName()); - int depth = 1; + int depth = 0; Iterator iterator = primaryEntry.topDownRangeIterator(); while (iterator.hasNext()) { Range subrange = iterator.next(); - /* - * Top level subranges don't need concrete methods. They just provide a file and line - * for their callee. - */ - if (!subrange.isInlined()) { - // only happens if the subrange is for the top-level compiled method - assert subrange.getCaller() == primaryEntry.getPrimary(); - assert subrange.getDepth() == 0; + if (subrange.isLeaf()) { + // we only generate concrete methods for non-leaf entries continue; } // if we just stepped out of a child range write nulls for each step up @@ -986,19 +979,11 @@ private int generateConcreteInlinedMethods(DebugContext context, ClassEntry clas pos = writeAttrNull(buffer, pos); depth--; } - MethodEntry method = subrange.getMethodEntry(); - ClassEntry methodClassEntry = method.ownerType(); - String methodKey = method.getSymbolName(); - /* the abstract index was written in the method's class entry */ - int specificationIndex = getAbstractInlineMethodIndex(methodClassEntry, methodKey); - pos = writeInlineSubroutine(context, classEntry, subrange, specificationIndex, depth, buffer, pos); - if (!subrange.isLeaf()) { - // increment depth before writing the children - depth++; - } + depth = subrange.getDepth(); + pos = writeInlineSubroutine(context, subrange, depth, buffer, pos); } // if we just stepped out of a child range write nulls for each step up - while (depth > 1) { + while (depth > 0) { pos = writeAttrNull(buffer, pos); depth--; } @@ -1322,7 +1307,7 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri * the method has inlined ranges so write concrete inlined method entries as its * children */ - pos = generateConcreteInlinedMethods(context, classEntry, primaryEntry, buffer, pos); + pos = generateConcreteInlinedMethods(context, primaryEntry, buffer, pos); } /* * Write a terminating null attribute. @@ -1354,44 +1339,47 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr return writeAttrNull(buffer, pos); } - private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, int subprogramOffset, int depth, byte[] buffer, int p) { - assert range.isInlined(); + private int writeInlineSubroutine(DebugContext context, Range caller, int depth, byte[] buffer, int p) { + assert !caller.isLeaf(); + // identify the inlined method by looking at the first callee + Range callee = caller.getFirstCallee(); + MethodEntry methodEntry = callee.getMethodEntry(); + ClassEntry methodClassEntry = methodEntry.ownerType(); + String methodKey = methodEntry.getSymbolName(); + /* the abstract index was written in the method's class entry */ + int specificationIndex = getAbstractInlineMethodIndex(methodClassEntry, methodKey); + int pos = p; - log(context, " [0x%08x] concrete inline subroutine [0x%x, 0x%x] %s", pos, range.getLo(), range.getHi(), range.getSymbolName()); - final Range callerSubrange = range.getCaller(); - assert callerSubrange != null; - int callLine = callerSubrange.getLine(); + log(context, " [0x%08x] concrete inline subroutine [0x%x, 0x%x] %s", pos, caller.getLo(), caller.getHi(), methodKey); + + int callLine = caller.getLine(); assert callLine >= -1 : callLine; Integer fileIndex; if (callLine == -1) { - log(context, " Unable to retrieve call line for inlined method %s", range.getFullMethodName()); + log(context, " Unable to retrieve call line for inlined method %s", callee.getFullMethodName()); /* continue with line 0 and fileIndex 1 as we must insert a tree node */ callLine = 0; fileIndex = 1; } else { - if (callerSubrange == range) { - fileIndex = 1; - } else { - FileEntry subFileEntry = callerSubrange.getFileEntry(); - assert subFileEntry != null : callerSubrange.getClassName() + "." + callerSubrange.getMethodName() + "(" + callerSubrange.getFileName() + ":" + callLine + ")"; - fileIndex = classEntry.localFilesIdx(subFileEntry); - assert fileIndex != null; - } + FileEntry subFileEntry = caller.getFileEntry(); + assert subFileEntry != null : caller.getClassName() + "." + caller.getMethodName() + "(" + caller.getFileName() + ":" + callLine + ")"; + fileIndex = caller.getFileIndex(); + assert fileIndex != null; } final int code; - if (range.isLeaf()) { - code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; - } else { + if (caller.includesInlineRanges()) { code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; + } else { + code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; } - log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 1, code); + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 2, code); pos = writeAbbrevCode(code, buffer, pos); - log(context, " [0x%08x] abstract_origin 0x%x", pos, subprogramOffset); - pos = writeAttrRef4(subprogramOffset, buffer, pos); - log(context, " [0x%08x] lo_pc 0x%08x", pos, range.getLo()); - pos = writeAttrAddress(range.getLo(), buffer, pos); - log(context, " [0x%08x] hi_pc 0x%08x", pos, range.getHi()); - pos = writeAttrAddress(range.getHi(), buffer, pos); + log(context, " [0x%08x] abstract_origin 0x%x", pos, specificationIndex); + pos = writeAttrRef4(specificationIndex, buffer, pos); + log(context, " [0x%08x] lo_pc 0x%08x", pos, caller.getLo()); + pos = writeAttrAddress(caller.getLo(), buffer, pos); + log(context, " [0x%08x] hi_pc 0x%08x", pos, caller.getHi()); + pos = writeAttrAddress(caller.getHi(), buffer, pos); log(context, " [0x%08x] call_file %d", pos, fileIndex); pos = writeAttrData4(fileIndex, buffer, pos); log(context, " [0x%08x] call_line %d", pos, callLine); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java index a1a09fc2f3c8..55257cd4b84f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java @@ -114,6 +114,10 @@ public int compareTo(SourcePositionSupplier o) { } return 0; } + + public boolean isMethodStart() { + return false; + } } public static final class InfopointSourceWrapper extends SourcePositionSupplier { @@ -161,6 +165,11 @@ public int compareTo(SourcePositionSupplier o) { } return 1; /* make Infopoints go first */ } + + @Override + public boolean isMethodStart() { + return infopoint.reason.equals(InfopointReason.METHOD_START); + } } public static final class SourceMappingWrapper extends SourcePositionSupplier { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index c85747530c63..53023d0ff269 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -76,6 +76,7 @@ import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.nativeimage.ImageSingletons; import java.lang.reflect.Modifier; @@ -967,28 +968,305 @@ public Stream locationInfoProvider() { return Stream.empty(); } final List locationInfos = new ArrayList<>(); - final ResolvedJavaMethod rootMethod = root.frame.getMethod(); - if (SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue()) { - root.visitChildren(new Visitor() { - @Override - public void apply(FrameNode node, Object... args) { - if (node.frame.getCaller() == null && node.frame.getMethod() == rootMethod) { - locationInfos.add(new NativeImageDebugLocationInfo(node)); + final boolean omitInline = SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue(); + final Visitor visitor = (omitInline ? new TopLevelVisitor(locationInfos) : new MultiLevelVisitor(locationInfos)); + // arguments passed by visitor to apply are + // NativeImageDebugLocationInfo caller location info + // CallNode nodeToEmbed parent call node to convert to entry code leaf + // NativeImageDebugLocationInfo leaf into which current leaf may be merged + root.visitChildren(visitor, (Object) null, (Object) null, (Object) null); + return locationInfos.stream(); + } + + // indices for arguments passed to SingleLevelVisitor::apply + + protected static final int CALLER_INFO = 0; + protected static final int PARENT_NODE_TO_EMBED = 1; + protected static final int LAST_LEAF_INFO = 2; + + private abstract class SingleLevelVisitor implements Visitor { + + protected final List locationInfos; + + SingleLevelVisitor(List locationInfos) { + this.locationInfos = locationInfos; + } + + public NativeImageDebugLocationInfo process(FrameNode node, NativeImageDebugLocationInfo callerInfo) { + NativeImageDebugLocationInfo locationInfo; + if (node instanceof CallNode) { + // this node represents an inline call range so + // add a locationinfo to cover the range of the call + locationInfo = createCallLocationInfo((CallNode) node, callerInfo); + } else { + // this is leaf method code so add details of its range + locationInfo = createLeafLocationInfo(node, callerInfo); + } + return locationInfo; + } + } + + private class TopLevelVisitor extends SingleLevelVisitor { + TopLevelVisitor(List locationInfos) { + super(locationInfos); + } + + @Override + public void apply(FrameNode node, Object... args) { + if (skipNode(node)) { + // this is a bogus wrapper so skip it and transform the wrapped node instead + node.visitChildren(this, args); + } else { + NativeImageDebugLocationInfo locationInfo = process(node, null); + if (node instanceof CallNode) { + locationInfos.add(locationInfo); + // erase last leaf (if present) since there is an intervening call range + invalidateMerge(args); + } else { + locationInfo = tryMerge(locationInfo, args); + if (locationInfo != null) { + locationInfos.add(locationInfo); } } - }); - } else { - root.visitChildren(new Visitor() { - @Override - public void apply(FrameNode node, Object... args) { - NativeImageDebugLocationInfo callerInfo = (args.length == 0 ? null : (NativeImageDebugLocationInfo) args[0]); - NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(node, callerInfo); + } + } + } + + public class MultiLevelVisitor extends SingleLevelVisitor { + MultiLevelVisitor(List locationInfos) { + super(locationInfos); + } + + @Override + public void apply(FrameNode node, Object... args) { + if (skipNode(node)) { + // this is a bogus wrapper so skip it and transform the wrapped node instead + node.visitChildren(this, args); + } else { + NativeImageDebugLocationInfo callerInfo = (NativeImageDebugLocationInfo) args[CALLER_INFO]; + CallNode nodeToEmbed = (CallNode) args[PARENT_NODE_TO_EMBED]; + if (nodeToEmbed != null) { + if (embedWithChildren(nodeToEmbed, node)) { + // embed a leaf range for the method start that was included in the + // parent CallNode + // its end range is determined by the start of the first node at this + // level + NativeImageDebugLocationInfo embeddedLocationInfo = createEmbeddedParentLocationInfo(nodeToEmbed, node, callerInfo); + locationInfos.add(embeddedLocationInfo); + // since this is a leaf node we can merge later leafs into it + initMerge(embeddedLocationInfo, args); + } + // reset args so we only embed the parent node before the first node at + // this level + args[PARENT_NODE_TO_EMBED] = nodeToEmbed = null; + } + NativeImageDebugLocationInfo locationInfo = process(node, callerInfo); + if (node instanceof CallNode) { + CallNode callNode = (CallNode) node; locationInfos.add(locationInfo); - node.visitChildren(this, locationInfo); + // erase last leaf (if present) since there is an intervening call range + invalidateMerge(args); + if (hasChildren(callNode)) { + // a call node may include an initial leaf range for the call that must + // be + // embedded under the newly created location info so pass it as an + // argument + callNode.visitChildren(this, locationInfo, callNode, (Object) null); + } else { + // we need to embed a leaf node for the whole call range + locationInfo = createEmbeddedParentLocationInfo(callNode, null, locationInfo); + locationInfos.add(locationInfo); + } + } else { + locationInfo = tryMerge(locationInfo, args); + if (locationInfo != null) { + locationInfos.add(locationInfo); + } } - }); + } } - return locationInfos.stream(); + } + + /** + * Report whether a call node has any children. + * + * @param callNode the node to check + * @return true if it has any children otherwise false. + */ + private boolean hasChildren(CallNode callNode) { + Object[] result = new Object[]{false}; + callNode.visitChildren(new Visitor() { + @Override + public void apply(FrameNode node, Object... args) { + args[0] = true; + } + }, result); + return (boolean) result[0]; + } + + /** + * Create a location info record for a leaf subrange. + * + * @param node is a simple FrameNode + * @return the newly created location info record + */ + private NativeImageDebugLocationInfo createLeafLocationInfo(FrameNode node, NativeImageDebugLocationInfo callerInfo) { + assert !(node instanceof CallNode); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(node, callerInfo); + debugContext.log(DebugContext.DETAILED_LEVEL, "Create leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), + locationInfo.addressHi() - 1); + return locationInfo; + } + + /** + * Create a location info record for a subrange that encloses an inline call. + * + * @param callNode is the top level inlined call frame + * @return the newly created location info record + */ + private NativeImageDebugLocationInfo createCallLocationInfo(CallNode callNode, NativeImageDebugLocationInfo callerInfo) { + BytecodePosition callerPos = realCaller(callNode); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(callerPos, callNode.getStartPos(), callNode.getEndPos() + 1, callerInfo); + debugContext.log(DebugContext.DETAILED_LEVEL, "Create call Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), + locationInfo.addressHi() - 1); + return locationInfo; + } + + /** + * Create a location info record for the initial range associated with a parent call node + * whose position and start are defined by that call node and whose end is determined by the + * first child of the call node. + * + * @param parentToEmbed a parent call node which has already been processed to create the + * caller location info + * @param firstChild the first child of the call node + * @param callerLocation the location info created to represent the range for the call + * @return a location info to be embedded as the first child range of the caller location. + */ + private NativeImageDebugLocationInfo createEmbeddedParentLocationInfo(CallNode parentToEmbed, FrameNode firstChild, NativeImageDebugLocationInfo callerLocation) { + BytecodePosition pos = parentToEmbed.frame; + int startPos = parentToEmbed.getStartPos(); + int endPos = (firstChild != null ? firstChild.getStartPos() : parentToEmbed.getEndPos() + 1); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(pos, startPos, endPos, callerLocation, true); + debugContext.log(DebugContext.DETAILED_LEVEL, "Embed leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), + locationInfo.addressHi() - 1); + return locationInfo; + } + + /** + * Test whether a bytecode position represents a bogus frame added by the compiler when a + * substitution or snippet call is injected. + * + * @param pos the position to be tested + * @return true if the frame is bogus otherwise false + */ + private boolean skipPos(BytecodePosition pos) { + return (pos.getBCI() == -1 && pos instanceof NodeSourcePosition && ((NodeSourcePosition) pos).isSubstitution()); + } + + /** + * Skip caller nodes with bogus positions, as determined by + * {@link #skipPos(BytecodePosition)}, returning first caller node position that is not + * bogus. + * + * @param node the node whose callers are to be traversed + * @return the first non-bogus position in the caller chain. + */ + private BytecodePosition realCaller(CallNode node) { + BytecodePosition pos = node.frame.getCaller(); + while (skipPos(pos)) { + pos = pos.getCaller(); + } + return pos; + } + + /** + * Test whether the position associated with a child node should result in an entry in the + * inline tree. The test is for a call node with a bogus position as determined by + * {@link #skipPos(BytecodePosition)}. + * + * @param node A node associated with a child frame in the compilation result frame tree. + * @return True an entry should be included or false if it should be omitted. + */ + private boolean skipNode(FrameNode node) { + return node instanceof CallNode && skipPos(node.frame); + } + + /* + * Test whether the position associated with a call node frame should be embedded along with + * the locations generated for the node's children. This is needed because call frames + * include a valid source position that precedes the first child position. + * + * @param node A node associated with a frame in the compilation result frame tree. + * + * @return True if an inline frame should be included or false if it should be omitted. + */ + + /** + * Test whether the position associated with a call node frame should be embedded along with + * the locations generated for the node's children. This is needed because call frames may + * include a valid source position that precedes the first child position. + * + * @param parent The call node whose children are currently being visited + * @param firstChild The first child of that call node + * @return true if the node should be embedded otherwise false + */ + private boolean embedWithChildren(CallNode parent, FrameNode firstChild) { + // we only need to insert a range for the caller if it fills a gap + // at the start of the caller range before the first child + if (parent.getStartPos() < firstChild.getStartPos()) { + return true; + } + return false; + } + + /** + * Try merging a new location info for a leaf range into the location info for the last leaf + * range added at this level. + * + * @param newLeaf the new leaf location info + * @param args the visitor argument vector used to pass parameters from one child visit to + * the next possibly including the last leaf + * @return the new location info if it could not be merged or null to indicate that it was + * merged + */ + public NativeImageDebugLocationInfo tryMerge(NativeImageDebugLocationInfo newLeaf, Object[] args) { + // last leaf node added at this level is 3rd element of arg vector + NativeImageDebugLocationInfo lastLeaf = (NativeImageDebugLocationInfo) args[LAST_LEAF_INFO]; + + if (lastLeaf != null) { + // try merging new leaf into last one + lastLeaf = lastLeaf.merge(newLeaf); + if (lastLeaf != null) { + // null return indicates new leaf has been merged into last leaf + return null; + } + } + // update last leaf and return new leaf for addition to local info list + args[LAST_LEAF_INFO] = newLeaf; + return newLeaf; + } + + /** + * Set the last leaf node at the current level to the supplied leaf node. + * + * @param lastLeaf the last leaf node created at this level + * @param args the visitor argument vector used to pass parameters from one child visit to + * the next + */ + public void initMerge(NativeImageDebugLocationInfo lastLeaf, Object[] args) { + args[LAST_LEAF_INFO] = lastLeaf; + } + + /** + * Clear the last leaf node at the current level from the visitor arguments by setting the + * arg vector entry to null. + * + * @param args the visitor argument vector used to pass parameters from one child visit to + * the next + */ + public void invalidateMerge(Object[] args) { + args[LAST_LEAF_INFO] = null; } @Override @@ -1092,24 +1370,29 @@ private class NativeImageDebugLocationInfo implements DebugLocationInfo { private final int bci; private final ResolvedJavaMethod method; private final int lo; - private final int hi; + private int hi; private Path cachePath; private Path fullFilePath; private DebugLocationInfo callersLocationInfo; + private boolean isPrologueEnd; private List localInfoList; - NativeImageDebugLocationInfo(FrameNode frameNode) { - this(frameNode, null); + NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { + this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo, frameNode.sourcePos.isMethodStart()); } - NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { - BytecodePosition bcpos = frameNode.frame; + NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo) { + this(bcpos, lo, hi, callersLocationInfo, false); + } + + NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo, boolean isPrologueEnd) { this.bci = bcpos.getBCI(); this.method = bcpos.getMethod(); - this.lo = frameNode.getStartPos(); - this.hi = frameNode.getEndPos() + 1; + this.lo = lo; + this.hi = hi; this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); this.callersLocationInfo = callersLocationInfo; + this.isPrologueEnd = isPrologueEnd; computeFullFilePath(); this.localInfoList = initLocalInfoList(bcpos); } @@ -1301,6 +1584,11 @@ public DebugLocationInfo getCaller() { return callersLocationInfo; } + @Override + public boolean isPrologueEnd() { + return isPrologueEnd; + } + @Override public Stream localsProvider() { if (localInfoList != null) { @@ -1351,6 +1639,66 @@ public int vtableOffset() { /* TODO - convert index to offset (+ sizeof DynamicHub) */ return isVirtual() ? ((HostedMethod) method).getVTableIndex() : -1; } + + public int depth() { + int depth = 1; + DebugLocationInfo caller = getCaller(); + while (caller != null) { + depth++; + caller = caller.getCaller(); + } + return depth; + } + + private int localsSize() { + if (localInfoList != null) { + return localInfoList.size(); + } else { + return 0; + } + } + + /** + * Merge the supplied leaf location info into this leaf location info if they have + * contiguous ranges, the same method and line number and the same live local variables with + * the same values. + * + * @param that a leaf location info to be merged into this one + * @return this leaf location info if the merge was performed otherwise null + */ + NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { + assert callersLocationInfo == that.callersLocationInfo; + assert depth() == that.depth() : "should only compare sibling ranges"; + assert this.hi <= that.lo : "later nodes should not overlap earlier ones"; + if (this.hi != that.lo) { + return null; + } + if (method != that.method) { + return null; + } + if (this.isPrologueEnd != that.isPrologueEnd) { + return null; + } + if (line() != that.line()) { + return null; + } + int size = localsSize(); + if (size != that.localsSize()) { + return null; + } + for (int i = 0; i < size; i++) { + NativeImageDebugLocalInfo thisLocal = (NativeImageDebugLocalInfo) localInfoList.get(i); + NativeImageDebugLocalInfo thatLocal = (NativeImageDebugLocalInfo) that.localInfoList.get(i); + if (!thisLocal.sameAs(thatLocal)) { + return null; + } + } + debugContext.log(DebugContext.DETAILED_LEVEL, "Merge leaf Location Info : %s depth %d (%d, %d) into (%d, %d)", that.name(), that.depth(), that.lo, that.hi - 1, this.lo, this.hi - 1); + // merging just requires updating lo and hi range as everything else is equal + this.hi = that.hi; + + return this; + } } public class NativeImageDebugLocalInfo implements DebugLocalInfo { @@ -1426,6 +1774,25 @@ public int stackSlot() { public Constant constantValue() { return null; } + + public boolean sameAs(NativeImageDebugLocalInfo that) { + if (localKind == that.localKind) { + switch (localKind) { + case REGISTER: + return regIndex() != that.regIndex(); + case STACKSLOT: + return stackSlot() != that.stackSlot(); + case CONSTANT: { + Constant thisValue = (Constant) value; + Constant thatValue = (Constant) that.value; + return thisValue.equals(thatValue); + } + case UNDEFINED: + return true; + } + } + return false; + } } /** From 76b2e9800f6774d9b57a3b06c55a4ea399e802b2 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Wed, 23 Feb 2022 14:19:30 +0000 Subject: [PATCH 04/22] Refactor DebugMethodInfo implementation classes to remove duplicated case handling. --- .../debuginfo/DebugInfoProvider.java | 53 +- .../image/NativeImageDebugInfoProvider.java | 578 +++++++----------- 2 files changed, 237 insertions(+), 394 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 87fa65e82ca3..f9904a2dfb1c 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -241,39 +241,47 @@ interface DebugMethodInfo extends DebugMemberInfo { * @return true if this method is an override of another method. */ boolean isOverride(); - } - /** - * Access details of a compiled method producing the code in a specific - * {@link com.oracle.objectfile.debugentry.Range}. - */ - interface DebugRangeInfo extends DebugMethodInfo { + /* + * Return the unique type that owns this method. + *

+ * + * @return the unique type that owns this method + */ ResolvedJavaType ownerType(); } /** - * Access details of a specific compiled method. + * Access details of a compiled top level or inline method producing the code in a specific + * {@link com.oracle.objectfile.debugentry.Range}. */ - interface DebugCodeInfo extends DebugRangeInfo { - void debugContext(Consumer action); + interface DebugRangeInfo extends DebugMethodInfo { /** - * @return the lowest address containing code generated for the method represented as an - * offset into the code segment. + * @return the lowest address containing code generated for an outer or inlined code segment + * reported at this line represented as an offset into the code segment. */ int addressLo(); /** - * @return the first address above the code generated for the method represented as an - * offset into the code segment. + * @return the first address above the code generated for an outer or inlined code segment + * reported at this line represented as an offset into the code segment. */ int addressHi(); /** - * @return the starting line number for the method. + * @return the line number for the outer or inlined segment. */ int line(); + } + + /** + * Access details of a specific compiled method. + */ + interface DebugCodeInfo extends DebugRangeInfo { + void debugContext(Consumer action); + /** * @return a stream of records detailing source local var and line locations within the * compiled method. @@ -316,23 +324,6 @@ interface DebugDataInfo { * number. */ interface DebugLocationInfo extends DebugRangeInfo { - /** - * @return the lowest address containing code generated for an outer or inlined code segment - * reported at this line represented as an offset into the code segment. - */ - int addressLo(); - - /** - * @return the first address above the code generated for an outer or inlined code segment - * reported at this line represented as an offset into the code segment. - */ - int addressHi(); - - /** - * @return the line number for the outer or inlined segment. - */ - int line(); - /** * @return the {@link DebugLocationInfo} of the nested inline caller-line */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 53023d0ff269..0a178e68002a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -270,7 +270,7 @@ private static String toJavaName(JavaType javaType) { private final Path cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); private abstract class NativeImageDebugFileInfo implements DebugFileInfo { - private Path fullFilePath; + private final Path fullFilePath; @SuppressWarnings("try") NativeImageDebugFileInfo(HostedType hostedType) { @@ -285,12 +285,25 @@ private abstract class NativeImageDebugFileInfo implements DebugFileInfo { } @SuppressWarnings("try") - NativeImageDebugFileInfo(HostedMethod hostedMethod) { - ResolvedJavaType javaType = getDeclaringClass(hostedMethod, false); - HostedType hostedType = hostedMethod.getDeclaringClass(); - Class clazz = hostedType.getJavaClass(); + NativeImageDebugFileInfo(ResolvedJavaMethod method) { + /* + * Note that this constructor allows for any ResolvedJavaMethod, not just a + * HostedMethod, because it needs to provide common behaviour for DebugMethodInfo, + * DebugCodeInfo and DebugLocationInfo records. The former two are derived from a + * HostedMethod while the latter may be derived from an arbitrary ResolvedJavaMethod. + */ + ResolvedJavaType javaType; + if (method instanceof HostedMethod) { + javaType = getDeclaringClass((HostedMethod) method, false); + } else { + javaType = method.getDeclaringClass(); + } + Class clazz = null; + if (javaType instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) javaType).getJavaClass(); + } SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); - try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { + try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", javaType)) { fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); } catch (Throwable e) { throw debugContext.handle(e); @@ -584,19 +597,19 @@ public Stream interfaces() { return Arrays.stream(hostedType.getInterfaces()).map(this::toJavaName); } - protected NativeImageDebugFieldInfo createDebugFieldInfo(HostedField field) { + private NativeImageDebugFieldInfo createDebugFieldInfo(HostedField field) { return new NativeImageDebugFieldInfo(field); } - protected NativeImageDebugFieldInfo createDebugStaticFieldInfo(ResolvedJavaField field) { + private NativeImageDebugFieldInfo createDebugStaticFieldInfo(ResolvedJavaField field) { return new NativeImageDebugFieldInfo((HostedField) field); } - protected NativeImageDebugMethodInfo createDebugMethodInfo(HostedMethod method) { + private NativeImageDebugMethodInfo createDebugMethodInfo(HostedMethod method) { return new NativeImageDebugMethodInfo(method); } - protected class NativeImageDebugFieldInfo extends NativeImageDebugFileInfo implements DebugFieldInfo { + private class NativeImageDebugFieldInfo extends NativeImageDebugFileInfo implements DebugFieldInfo { private final HostedField field; NativeImageDebugFieldInfo(HostedField field) { @@ -655,99 +668,9 @@ private boolean isPrimitive() { } } - protected class NativeImageDebugMethodInfo extends NativeImageDebugFileInfo implements DebugMethodInfo { - private final HostedMethod hostedMethod; - + private class NativeImageDebugMethodInfo extends NativeImageDebugHostedMethodInfo implements DebugMethodInfo { NativeImageDebugMethodInfo(HostedMethod hostedMethod) { super(hostedMethod); - this.hostedMethod = hostedMethod; - } - - @Override - public String name() { - String name = hostedMethod.format("%n"); - if ("".equals(name)) { - name = getDeclaringClass(hostedMethod, true).toJavaName(); - if (name.indexOf('.') >= 0) { - name = name.substring(name.lastIndexOf('.') + 1); - } - if (name.indexOf('$') >= 0) { - name = name.substring(name.lastIndexOf('$') + 1); - } - } - return name; - } - - @Override - public String valueType() { - return toJavaName((HostedType) hostedMethod.getSignature().getReturnType(null)); - } - - @Override - public List paramTypes() { - Signature signature = hostedMethod.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramTypes = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - paramTypes.add(toJavaName((HostedType) signature.getParameterType(i, null))); - } - return paramTypes; - } - - @Override - public List paramNames() { - /* Can only provide blank names for now. */ - Signature signature = hostedMethod.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramNames = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - paramNames.add(""); - } - return paramNames; - } - - @Override - public String symbolNameForMethod() { - return NativeImage.localSymbolNameForMethod(hostedMethod); - } - - @Override - public boolean isDeoptTarget() { - return hostedMethod.isDeoptTarget(); - } - - @Override - public boolean isConstructor() { - return hostedMethod.isConstructor(); - } - - @Override - public int modifiers() { - return getOriginalModifiers(hostedMethod); - } - - @Override - public boolean isVirtual() { - return hostedMethod.hasVTableIndex(); - } - - @Override - public int vtableOffset() { - /* - * TODO - provide correct offset, not index. In Graal, the vtable is appended after - * the dynamicHub object, so can't just multiply by sizeof(pointer). - */ - return hostedMethod.hasVTableIndex() ? hostedMethod.getVTableIndex() : -1; - } - - /** - * Returns true if this is an override virtual method. Used in Windows CodeView output. - * - * @return true if this is a virtual method and overrides an existing method. - */ - @Override - public boolean isOverride() { - return allOverrides.contains(hostedMethod); } } } @@ -878,38 +801,70 @@ private NativeImageDebugTypeInfo createDebugTypeInfo(HostedType hostedType) { } } - /** - * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an - * ObjectFile when generation of debug info is enabled. - */ - private class NativeImageDebugCodeInfo extends NativeImageDebugFileInfo implements DebugCodeInfo { - private final HostedMethod hostedMethod; - private final CompilationResult compilation; + protected abstract class NativeImageDebugBaseMethodInfo extends NativeImageDebugFileInfo implements DebugMethodInfo { + protected final ResolvedJavaMethod method; - NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { + NativeImageDebugBaseMethodInfo(ResolvedJavaMethod method) { super(method); - this.hostedMethod = method; - this.compilation = compilation; - } - - @SuppressWarnings("try") - @Override - public void debugContext(Consumer action) { - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", hostedMethod)) { - action.accept(debugContext); - } catch (Throwable e) { - throw debugContext.handle(e); - } + this.method = method; } + /** + * Return the unique type that owns this method. + *

+ * In the absence of substitutions the returned type result is simply the original JVMCI + * implementation type that declares the associated Java method. Identifying this type may + * involve unwrapping from Hosted universe to Analysis universe to the original JVMCI + * universe. + *

+ * + * In the case where the method itself is either an (annotated) substitution or declared by + * a class that is a (annotated) substitution then the link from substitution to original is + * also 'unwrapped' to arrive at the original type. In cases where a substituted method has + * no original the class of the substitution is used, for want of anything better. + *

+ * + * This unwrapping strategy avoids two possible ambiguities that would compromise use of the + * returned value as a unique 'owner'. Firstly, if the same method is ever presented via + * both its HostedMethod incarnation and its original ResolvedJavaMethod incarnation then + * ownership will always be signalled via the original type. This ensures that views of the + * method presented via the list of included HostedTypes and via the list of CompiledMethod + * objects and their embedded substructure (such as inline caller hierarchies) are correctly + * identified. + *

+ * + * Secondly, when a substituted method or method of a substituted class is presented it ends + * up being collated with methods of the original class rather than the class which actually + * defines it, avoiding an artificial split of methods that belong to the same underlying + * runtime type into two distinct types in the debuginfo model. As an example, this ensures + * that all methods of substitution class DynamicHub are collated with methods of class + * java.lang.Class, the latter being the type whose name gets written into the debug info + * and gets used to name the method in the debugger. Note that this still allows the + * method's line info to be associated with the correct file i.e. it does not compromise + * breaking and stepping through the code. + * + * @return the unique type that owns this method + */ @Override public ResolvedJavaType ownerType() { - return getDeclaringClass(hostedMethod, true); + ResolvedJavaType result; + if (method instanceof HostedMethod) { + result = getDeclaringClass((HostedMethod) method, true); + } else { + result = method.getDeclaringClass(); + } + while (result instanceof WrappedJavaType) { + result = ((WrappedJavaType) result).getWrapped(); + } + return result; } @Override public String name() { - ResolvedJavaMethod targetMethod = hostedMethod.getWrapped().getWrapped(); + ResolvedJavaMethod targetMethod = method; + while (targetMethod instanceof WrappedJavaMethod) { + targetMethod = ((WrappedJavaMethod) targetMethod).getWrapped(); + } if (targetMethod instanceof SubstitutionMethod) { targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); } else if (targetMethod instanceof CustomSubstitutionMethod) { @@ -917,25 +872,148 @@ public String name() { } String name = targetMethod.getName(); if (name.equals("")) { - name = getDeclaringClass(hostedMethod, true).toJavaName(); - if (name.indexOf('.') >= 0) { - name = name.substring(name.lastIndexOf('.') + 1); - } - if (name.indexOf('$') >= 0) { - name = name.substring(name.lastIndexOf('$') + 1); + if (method instanceof HostedMethod) { + name = getDeclaringClass((HostedMethod) method, true).toJavaName(); + if (name.indexOf('.') >= 0) { + name = name.substring(name.lastIndexOf('.') + 1); + } + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } else { + name = targetMethod.format("%h"); + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } } } return name; } + @Override + public String valueType() { + return toJavaName(method.getSignature().getReturnType(null)); + } + @Override public String symbolNameForMethod() { - return NativeImage.localSymbolNameForMethod(hostedMethod); + return NativeImage.localSymbolNameForMethod(method); } @Override - public String valueType() { - return hostedMethod.format("%R"); + public boolean isDeoptTarget() { + if (method instanceof HostedMethod) { + return ((HostedMethod) method).isDeoptTarget(); + } + return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + } + + @Override + public List paramTypes() { + Signature signature = method.getSignature(); + int parameterCount = signature.getParameterCount(false); + List paramTypes = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { + JavaType parameterType = signature.getParameterType(i, null); + paramTypes.add(toJavaName(parameterType)); + } + return paramTypes; + } + + @Override + public List paramNames() { + /* Can only provide blank names for now. */ + Signature signature = method.getSignature(); + int parameterCount = signature.getParameterCount(false); + List paramNames = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { + paramNames.add(""); + } + return paramNames; + } + + @Override + public int modifiers() { + if (method instanceof HostedMethod) { + return getOriginalModifiers((HostedMethod) method); + } + return method.getModifiers(); + } + @Override + public boolean isConstructor() { + return method.isConstructor(); + } + + @Override + public boolean isVirtual() { + return method instanceof HostedMethod && ((HostedMethod) method).hasVTableIndex(); + } + + @Override + public boolean isOverride() { + return method instanceof HostedMethod && allOverrides.contains(method); + } + + @Override + public int vtableOffset() { + /* TODO - convert index to offset (+ sizeof DynamicHub) */ + return isVirtual() ? ((HostedMethod) method).getVTableIndex() : -1; + } + } + + protected abstract class NativeImageDebugHostedMethodInfo extends NativeImageDebugBaseMethodInfo { + protected final HostedMethod hostedMethod; + + NativeImageDebugHostedMethodInfo(HostedMethod method) { + super(method); + this.hostedMethod = method; + } + + @Override + public boolean isVirtual() { + return hostedMethod.hasVTableIndex(); + } + + @Override + public int vtableOffset() { + /* + * TODO - provide correct offset, not index. In Graal, the vtable is appended after + * the dynamicHub object, so can't just multiply by sizeof(pointer). + */ + return hostedMethod.hasVTableIndex() ? hostedMethod.getVTableIndex() : -1; + } + + /** + * Returns true if this is an override virtual method. Used in Windows CodeView output. + * + * @return true if this is a virtual method and overrides an existing method. + */ + @Override + public boolean isOverride() { + return allOverrides.contains(hostedMethod); + } + } + + /** + * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an + * ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugCodeInfo extends NativeImageDebugHostedMethodInfo implements DebugCodeInfo { + private final CompilationResult compilation; + + NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { + super(method); + this.compilation = compilation; + } + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", hostedMethod)) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } } @Override @@ -1230,7 +1308,7 @@ private boolean embedWithChildren(CallNode parent, FrameNode firstChild) { * @return the new location info if it could not be merged or null to indicate that it was * merged */ - public NativeImageDebugLocationInfo tryMerge(NativeImageDebugLocationInfo newLeaf, Object[] args) { + private NativeImageDebugLocationInfo tryMerge(NativeImageDebugLocationInfo newLeaf, Object[] args) { // last leaf node added at this level is 3rd element of arg vector NativeImageDebugLocationInfo lastLeaf = (NativeImageDebugLocationInfo) args[LAST_LEAF_INFO]; @@ -1254,7 +1332,7 @@ public NativeImageDebugLocationInfo tryMerge(NativeImageDebugLocationInfo newLea * @param args the visitor argument vector used to pass parameters from one child visit to * the next */ - public void initMerge(NativeImageDebugLocationInfo lastLeaf, Object[] args) { + private void initMerge(NativeImageDebugLocationInfo lastLeaf, Object[] args) { args[LAST_LEAF_INFO] = lastLeaf; } @@ -1265,7 +1343,7 @@ public void initMerge(NativeImageDebugLocationInfo lastLeaf, Object[] args) { * @param args the visitor argument vector used to pass parameters from one child visit to * the next */ - public void invalidateMerge(Object[] args) { + private void invalidateMerge(Object[] args) { args[LAST_LEAF_INFO] = null; } @@ -1297,82 +1375,16 @@ public List getFrameSizeChanges() { } return frameSizeChanges; } - - @Override - public boolean isDeoptTarget() { - return hostedMethod.isDeoptTarget(); - } - - @Override - public List paramTypes() { - Signature signature = hostedMethod.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramTypes = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - JavaType parameterType = signature.getParameterType(i, null); - paramTypes.add(toJavaName(parameterType)); - } - return paramTypes; - } - - @Override - public List paramNames() { - /* Can only provide blank names for now. */ - Signature signature = hostedMethod.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramNames = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - paramNames.add(""); - } - return paramNames; - } - - @Override - public int modifiers() { - return getOriginalModifiers(hostedMethod); - } - - @Override - public boolean isConstructor() { - return hostedMethod.isConstructor(); - } - - @Override - public boolean isVirtual() { - return hostedMethod.hasVTableIndex(); - } - - @Override - public int vtableOffset() { - /* - * TODO - provide correct offset, not index. In Graal, the vtable is appended after the - * dynamicHub object. - */ - return hostedMethod.hasVTableIndex() ? hostedMethod.getVTableIndex() : -1; - } - - /** - * Returns true if this is an override virtual method. Used in Windows CodeView output. - * - * @return true if this is a virtual method and overrides an existing method. - */ - @Override - public boolean isOverride() { - return allOverrides.contains(hostedMethod); - } } /** * Implementation of the DebugLocationInfo API interface that allows line number and local var * info to be passed to an ObjectFile when generation of debug info is enabled. */ - private class NativeImageDebugLocationInfo implements DebugLocationInfo { + private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInfo implements DebugLocationInfo { private final int bci; - private final ResolvedJavaMethod method; private final int lo; private int hi; - private Path cachePath; - private Path fullFilePath; private DebugLocationInfo callersLocationInfo; private boolean isPrologueEnd; private List localInfoList; @@ -1386,14 +1398,12 @@ private class NativeImageDebugLocationInfo implements DebugLocationInfo { } NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo, boolean isPrologueEnd) { + super(bcpos.getMethod()); this.bci = bcpos.getBCI(); - this.method = bcpos.getMethod(); this.lo = lo; this.hi = hi; - this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); this.callersLocationInfo = callersLocationInfo; this.isPrologueEnd = isPrologueEnd; - computeFullFilePath(); this.localInfoList = initLocalInfoList(bcpos); } @@ -1444,93 +1454,6 @@ private Local[] getLocalsBySlot() { return nonEmptySortedLocals; } - @Override - public String fileName() { - if (fullFilePath != null) { - Path fileName = fullFilePath.getFileName(); - if (fileName != null) { - return fileName.toString(); - } - } - return null; - } - - @Override - public Path filePath() { - if (fullFilePath != null) { - return fullFilePath.getParent(); - } - return null; - } - - @Override - public Path cachePath() { - return cachePath; - } - - @Override - public ResolvedJavaType ownerType() { - ResolvedJavaType result; - if (method instanceof HostedMethod) { - result = getDeclaringClass((HostedMethod) method, true); - } else { - result = method.getDeclaringClass(); - } - if (result instanceof WrappedJavaType) { - result = ((WrappedJavaType) result).getWrapped(); - } - return result; - } - - @Override - public String name() { - ResolvedJavaMethod targetMethod = method; - while (targetMethod instanceof WrappedJavaMethod) { - targetMethod = ((WrappedJavaMethod) targetMethod).getWrapped(); - } - if (targetMethod instanceof SubstitutionMethod) { - targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); - } else if (targetMethod instanceof CustomSubstitutionMethod) { - targetMethod = ((CustomSubstitutionMethod) targetMethod).getOriginal(); - } - String name = targetMethod.getName(); - if (name.equals("")) { - if (method instanceof HostedMethod) { - name = getDeclaringClass((HostedMethod) method, true).toJavaName(); - if (name.indexOf('.') >= 0) { - name = name.substring(name.lastIndexOf('.') + 1); - } - if (name.indexOf('$') >= 0) { - name = name.substring(name.lastIndexOf('$') + 1); - } - } else { - name = targetMethod.format("%h"); - if (name.indexOf('$') >= 0) { - name = name.substring(name.lastIndexOf('$') + 1); - } - } - } - return name; - } - - @Override - public String valueType() { - return method.format("%R"); - } - - @Override - public String symbolNameForMethod() { - return NativeImage.localSymbolNameForMethod(method); - } - - @Override - public boolean isDeoptTarget() { - if (method instanceof HostedMethod) { - return ((HostedMethod) method).isDeoptTarget(); - } - return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); - } - @Override public int addressLo() { return lo; @@ -1550,35 +1473,6 @@ public int line() { return -1; } - @Override - public List paramTypes() { - Signature signature = method.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramTypes = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - JavaType parameterType = signature.getParameterType(i, null); - paramTypes.add(toJavaName(parameterType)); - } - return paramTypes; - } - - @Override - public List paramNames() { - /* Can only provide blank names for now. */ - Signature signature = method.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramNames = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - paramNames.add(""); - } - return paramNames; - } - - @Override - public int modifiers() { - return method.getModifiers(); - } - @Override public DebugLocationInfo getCaller() { return callersLocationInfo; @@ -1598,48 +1492,6 @@ public Stream localsProvider() { } } - @SuppressWarnings("try") - private void computeFullFilePath() { - ResolvedJavaType declaringClass; - // if we have a HostedMethod then deal with substitutions - if (method instanceof HostedMethod) { - declaringClass = getDeclaringClass((HostedMethod) method, false); - } else { - declaringClass = method.getDeclaringClass(); - } - Class clazz = null; - if (declaringClass instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); - } - SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", declaringClass)) { - fullFilePath = sourceManager.findAndCacheSource(declaringClass, clazz, debugContext); - } catch (Throwable e) { - throw debugContext.handle(e); - } - } - - @Override - public boolean isConstructor() { - return method.isConstructor(); - } - - @Override - public boolean isVirtual() { - return method instanceof HostedMethod && ((HostedMethod) method).hasVTableIndex(); - } - - @Override - public boolean isOverride() { - return method instanceof HostedMethod && allOverrides.contains(method); - } - - @Override - public int vtableOffset() { - /* TODO - convert index to offset (+ sizeof DynamicHub) */ - return isVirtual() ? ((HostedMethod) method).getVTableIndex() : -1; - } - public int depth() { int depth = 1; DebugLocationInfo caller = getCaller(); From f22d3cb6554a4cbd5b156016ddd506e169be3beb Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 1 Mar 2022 10:48:26 +0000 Subject: [PATCH 05/22] Refactor DebugInfo presentation of of param and local variables. --- substratevm/mx.substratevm/testhello.py | 71 +++--- .../objectfile/debugentry/ClassEntry.java | 45 ++-- .../objectfile/debugentry/DebugInfoBase.java | 10 +- .../objectfile/debugentry/MethodEntry.java | 204 ++++++++++++++- .../oracle/objectfile/debugentry/Range.java | 151 ++++++++++- .../debuginfo/DebugInfoProvider.java | 42 +++- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 165 ++++++++++-- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 102 +++++++- .../elf/dwarf/DwarfInfoSectionImpl.java | 234 +++++++++++++++--- .../elf/dwarf/DwarfSectionImpl.java | 24 ++ .../core/code/CompilationResultFrameTree.java | 12 +- .../image/NativeImageDebugInfoProvider.java | 215 +++++++++++----- 12 files changed, 1059 insertions(+), 216 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 72b5c130ec8b..f9256b98ddf6 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -134,6 +134,7 @@ def test(): package_file_pattern = '[a-zA-Z0-9_/]+\\.java' varname_pattern = '[a-zA-Z0-9_]+' wildcard_pattern = '.*' + arg_values_pattern = "(.*=.*)" # obtain the gdb version # n.b. we can only test printing in gdb 10.1 upwards exec_string=execute("show version") @@ -548,7 +549,7 @@ def test(): rexp = [r"#0%shello\.Hello::inlineA \(\) at hello/Hello\.java:130"%spaces_pattern, r"#1%shello\.Hello::inlineIs \(\) at hello/Hello\.java:125"%spaces_pattern, r"#2%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:120"%spaces_pattern, - r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:93"%(spaces_pattern, address_pattern)] + r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace inlineMee', rexp) checker.check(exec_string, skip_fails=False) @@ -568,7 +569,7 @@ def test(): r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:130"%(spaces_pattern, address_pattern), r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:125"%(spaces_pattern), r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:120"%(spaces_pattern), - r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:93"%(spaces_pattern, address_pattern)] + r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace in inlineMethod', rexp) checker.check(exec_string, skip_fails=False) @@ -592,20 +593,20 @@ def test(): execute("continue 5") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:157"%(spaces_pattern), - r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), - r"#2%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), - r"#3%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), - r"#4%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), - r"#5%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), - r"#6%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), - r"#7%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), - r"#8%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), - r"#9%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), - r"#10%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), - r"#11%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, arg_values_pattern), + r"#1%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), + r"#6%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#7%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), + r"#8%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#9%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), + r"#10%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#11%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:141"%(spaces_pattern, address_pattern), - r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] + r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) @@ -617,20 +618,20 @@ def test(): execute("continue") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:170"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), - r"#2%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#3%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), - r"#4%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#5%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), - r"#6%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#7%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), - r"#8%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#9%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), - r"#10%shello\.Hello::inlineTo \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#11%shello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), + r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), + r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), + r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, arg_values_pattern), r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:143"%(spaces_pattern), - r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] + r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) @@ -642,14 +643,14 @@ def test(): execute("continue 5") exec_string = execute("backtrace 8") - rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:176"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), - r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), - r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), - r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), - r"#5%s%s in hello\.Hello::inlineTailRecursion \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:176"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:144"%(spaces_pattern), - r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] + r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index fc21a324bf03..5a2af5ebfbbf 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -37,6 +37,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugInstanceTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugRangeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; @@ -285,24 +286,15 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa String methodName = debugMethodInfo.name(); String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); int modifiers = debugMethodInfo.modifiers(); - List paramTypes = debugMethodInfo.paramTypes(); - List paramNames = debugMethodInfo.paramNames(); - assert paramTypes.size() == paramNames.size(); - int paramCount = paramTypes.size(); + DebugLocalInfo[] paramInfos = debugMethodInfo.getParamInfo(); + DebugLocalInfo thisParam = debugMethodInfo.getThisParamInfo(); + int paramCount = paramInfos.length; debugContext.log("typename %s adding %s method %s %s(%s)\n", - typeName, memberModifiers(modifiers), resultTypeName, methodName, formatParams(paramTypes, paramNames)); + typeName, memberModifiers(modifiers), resultTypeName, methodName, formatParams(paramInfos)); TypeEntry resultType = debugInfoBase.lookupTypeEntry(resultTypeName); - TypeEntry[] paramTypeArray = null; - String[] paramNameArray = null; - if (paramCount != 0) { - paramTypeArray = new TypeEntry[paramCount]; - paramNameArray = new String[paramCount]; - int idx = 0; - for (String paramTypeName : paramTypes) { - TypeEntry paramType = debugInfoBase.lookupTypeEntry(TypeEntry.canonicalize(paramTypeName)); - paramTypeArray[idx++] = paramType; - } - paramNameArray = paramNames.toArray(paramNameArray); + TypeEntry[] typeEntries = new TypeEntry[paramCount]; + for (int i = 0; i < paramCount; i++) { + typeEntries[i] = debugInfoBase.lookupTypeEntry(TypeEntry.canonicalize(paramInfos[i].typeName())); } /* * n.b. the method file may differ from the owning class file when the method is a @@ -310,7 +302,7 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(debugMethodInfo); MethodEntry methodEntry = new MethodEntry(debugInfoBase, debugMethodInfo, methodFileEntry, methodName, - this, resultType, paramTypeArray, paramNameArray); + this, resultType, typeEntries, paramInfos, thisParam); indexMethodEntry(methodEntry); return methodEntry; @@ -326,21 +318,18 @@ protected FieldEntry addField(DebugFieldInfo debugFieldInfo, DebugInfoBase debug return fieldEntry; } - private static String formatParams(List paramTypes, List paramNames) { - if (paramNames.size() == 0) { + private static String formatParams(DebugLocalInfo[] paramInfo) { + if (paramInfo.length == 0) { return ""; } StringBuilder builder = new StringBuilder(); - String separator = ""; - for (int i = 0; i < paramNames.size(); i++) { - builder.append(separator); - builder.append(paramTypes.get(i)); - String paramName = paramNames.get(i); - if (paramName.length() > 0) { - builder.append(' '); - builder.append(paramName); + for (int i = 0; i < paramInfo.length; i++) { + if (i > 0) { + builder.append(", "); } - separator = ", "; + builder.append(paramInfo[i].typeName()); + builder.append(' '); + builder.append(paramInfo[i].name()); } return builder.toString(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 3c1448ed68d1..c540fec7e561 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -40,6 +40,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocationInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; import com.oracle.objectfile.elf.dwarf.DwarfDebugInfo; @@ -383,9 +384,12 @@ private Range addSubrange(DebugLocationInfo locationInfo, Range primaryRange, Cl debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %d %s:%d [0x%x, 0x%x] (%d, %d)", ownerType.toJavaName(), methodName, subRange.getDepth(), fullPath, line, lo, hi, loOff, hiOff); assert (callerLocationInfo == null || (callerLocationInfo.addressLo() <= loOff && callerLocationInfo.addressHi() >= hiOff)) : "parent range should enclose subrange!"; - locationInfo.localsProvider().forEach(localInfo -> { - debugContext.log(DebugContext.DETAILED_LEVEL, " local %s:%s = %s", localInfo.name(), localInfo.typeName(), localInfo.valueString()); - }); + DebugLocalValueInfo[] localValueInfos = locationInfo.getLocalValueInfo(); + for (int i = 0; i < localValueInfos.length; i++) { + DebugLocalValueInfo localValueInfo = localValueInfos[i]; + debugContext.log(DebugContext.DETAILED_LEVEL, " locals[%d] %s:%s = %s", localValueInfo.slot(), localValueInfo.name(), localValueInfo.typeName(), localValueInfo.valueString()); + } + subRange.setLocalValueInfo(localValueInfos); return subRange; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index 6fb3d811c9f5..3ad3a7bd8a96 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -26,13 +26,24 @@ package com.oracle.objectfile.debugentry; +import jdk.vm.ci.meta.JavaKind; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocationInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; +import java.util.ArrayList; +import java.util.ListIterator; + public class MethodEntry extends MemberEntry { private final TypeEntry[] paramTypes; - private final String[] paramNames; + private final DebugLocalInfo thisParam; + private final DebugLocalInfo[] paramInfos; + private final int firstLocalSlot; + // local vars are accumulated as they are referenced sorted by slot, then name, then + // type name. we don't currently deal handle references to locals with no slot. + private final ArrayList locals; static final int DEOPT = 1 << 0; static final int IN_RANGE = 1 << 1; static final int INLINED = 1 << 2; @@ -44,12 +55,11 @@ public class MethodEntry extends MemberEntry { public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, FileEntry fileEntry, String methodName, ClassEntry ownerType, - TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames) { + TypeEntry valueType, TypeEntry[] paramTypes, DebugLocalInfo[] paramInfos, DebugLocalInfo thisParam) { super(fileEntry, methodName, ownerType, valueType, debugMethodInfo.modifiers()); - assert ((paramTypes == null && paramNames == null) || - (paramTypes != null && paramNames != null && paramTypes.length == paramNames.length)); this.paramTypes = paramTypes; - this.paramNames = paramNames; + this.paramInfos = paramInfos; + this.thisParam = thisParam; this.symbolName = debugMethodInfo.symbolNameForMethod(); this.flags = 0; if (debugMethodInfo.isDeoptTarget()) { @@ -62,6 +72,14 @@ public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, setIsOverride(); } vtableOffset = debugMethodInfo.vtableOffset(); + int paramCount = paramInfos.length; + if (paramCount > 0) { + DebugLocalInfo lastParam = paramInfos[paramCount - 1]; + firstLocalSlot = lastParam.slot() + lastParam.slotCount(); + } else { + firstLocalSlot = (thisParam == null ? 0 : thisParam.slotCount()); + } + locals = new ArrayList<>(); updateRangeInfo(debugInfoBase, debugMethodInfo); } @@ -76,12 +94,11 @@ public ClassEntry ownerType() { } public int getParamCount() { - return (paramTypes == null ? 0 : paramTypes.length); + return paramInfos.length; } public TypeEntry getParamType(int idx) { - assert paramTypes != null; - assert idx < paramTypes.length; + assert idx < paramInfos.length; return paramTypes[idx]; } @@ -90,17 +107,38 @@ public TypeEntry[] getParamTypes() { } public String getParamTypeName(int idx) { - assert paramTypes != null; assert idx < paramTypes.length; - assert paramTypes[idx] != null; return paramTypes[idx].getTypeName(); } public String getParamName(int idx) { - assert paramNames != null; - assert idx < paramNames.length; + assert idx < paramInfos.length; + /* N.b. param names may be null. */ + return paramInfos[idx].name(); + } + + public int getParamLine(int idx) { + assert idx < paramInfos.length; /* N.b. param names may be null. */ - return paramNames[idx]; + return paramInfos[idx].line(); + } + + public DebugLocalInfo getParam(int i) { + assert i >= 0 && i < paramInfos.length : "bad param index"; + return paramInfos[i]; + } + + public DebugLocalInfo getThisParam() { + return thisParam; + } + + public int getLocalCount() { + return locals.size(); + } + + public DebugLocalInfo getLocal(int i) { + assert i >= 0 && i < locals.size() : "bad param index"; + return locals.get(i); } private void setIsDeopt() { @@ -190,4 +228,144 @@ public int getVtableOffset() { public String getSymbolName() { return symbolName; } + + /** + * Return a unique local or parameter variable associated with the value, optionally recording + * it as a new local variable or fail, returning null, when the local value does not conform + * with existing recorded parameter or local variables. Values with invalid (negative) slots + * always fail. Values whose slot is associated with a parameter only conform if their name and + * type equal those of the parameter. Values whose slot is in the local range will always + * succeed,. either by matchign the slot and name of an existing local or by being recorded as a + * new local variable. + * + * @param localValueInfo + * @return the unique local variable with which this local value can be legitimately associated + * otherwise null. + */ + public DebugLocalInfo recordLocal(DebugLocalValueInfo localValueInfo) { + int slot = localValueInfo.slot(); + if (slot < 0) { + return null; + } else { + if (slot < firstLocalSlot) { + return matchParam(localValueInfo); + } else { + return matchLocal(localValueInfo); + } + } + } + + private DebugLocalInfo matchParam(DebugLocalValueInfo localValueInfo) { + if (thisParam != null) { + if (checkMatch(thisParam, localValueInfo)) { + return thisParam; + } + } + for (int i = 0; i < paramInfos.length; i++) { + DebugLocalInfo paramInfo = paramInfos[i]; + if (checkMatch(paramInfo, localValueInfo)) { + return paramInfo; + } + } + return null; + } + + /** + * wrapper class for a local value that stands in as a unique identifier for the associated + * local variable while allowing its line to be adjusted when earlier occurrences of the same + * local are identified. + */ + private static class DebugLocalInfoWrapper implements DebugLocalInfo { + DebugLocalValueInfo value; + int line; + + DebugLocalInfoWrapper(DebugLocalValueInfo value) { + this.value = value; + this.line = value.line(); + } + + @Override + public String name() { + return value.name(); + } + + @Override + public String typeName() { + return value.typeName(); + } + + @Override + public int slot() { + return value.slot(); + } + + @Override + public int slotCount() { + return value.slotCount(); + } + + @Override + public JavaKind javaKind() { + return value.javaKind(); + } + + @Override + public int line() { + return line; + } + + public void setLine(int line) { + this.line = line; + } + } + + private DebugLocalInfo matchLocal(DebugLocalValueInfo localValueInfo) { + ListIterator listIterator = locals.listIterator(); + while (listIterator.hasNext()) { + DebugLocalInfoWrapper next = (DebugLocalInfoWrapper) listIterator.next(); + if (checkMatch(next, localValueInfo)) { + int currentLine = next.line(); + int newLine = localValueInfo.line(); + if ((currentLine < 0 && newLine >= 0) || + (newLine >= 0 && newLine < currentLine)) { + next.setLine(newLine); + } + return next; + } else if (next.slot() > localValueInfo.slot()) { + // we have iterated just beyond the insertion point + // so wind cursor back one element + listIterator.previous(); + break; + } + } + DebugLocalInfoWrapper newLocal = new DebugLocalInfoWrapper(localValueInfo); + // add at the current cursor position + listIterator.add(newLocal); + return newLocal; + } + + boolean checkMatch(DebugLocalInfo local, DebugLocalValueInfo value) { + boolean isMatch = (local.slot() == value.slot() && + local.name().equals(value.name()) && + local.typeName().equals(value.typeName())); + assert !isMatch || verifyMatch(local, value) : "failed to verify matched var and value"; + return isMatch; + } + + private static boolean verifyMatch(DebugLocalInfo local, DebugLocalValueInfo value) { + // slot counts are normally expected to match + if (local.slotCount() == value.slotCount()) { + return true; + } + // we can have a zero count for the local or value if it is undefined + if (local.slotCount() == 0 || value.slotCount() == 0) { + return true; + } + // pseudo-object locals can appear as longs + if (local.javaKind() == JavaKind.Object && value.javaKind() == JavaKind.Long) { + return true; + } + // something is wrong + return false; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 6bb1eb1d42ed..2f5417118fbc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -26,14 +26,48 @@ package com.oracle.objectfile.debugentry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + /** * Details of a specific address range in a compiled method either a primary range identifying a - * whole method or a sub-range identifying a sequence of instructions that belong to an inlined - * method. Each sub-range is linked with its caller and its callees, forming a call tree. + * whole compiled method or a sub-range identifying a sub-sequence of the compiled instructions that + * may derive from top level or inlined code. Each sub-range is linked with its caller, (which may + * be the primary range) and its callees, forming a call tree. Subranges are either leaf nodes with + * no children or call nodes which have children. + * + *

    + *
  • A leaf node at the top level (depth 0) records the start and extent of a sequence of compiled + * code derived from the top level method. The leaf node reports itself as belonging to the top + * level method. + *
  • A leaf node at depth N records the start and extent of a sequence of compiled code derived + * from a leaf inlined method at a call depth of N. The leaf node reports itself as belonging to the + * leaf method. + *
  • A call node at level 0 records the start and extent of a sequence of compiled code that + * includes all compiled code derived from a top level call that has been inlined. All child nodes + * of the call node (direct or indirect) should model ranges that lie within the parent range. The + * call node reports itself as belonging to the top level method and its file and line information + * identify the location of the call. + *
  • A call node at level N records the start and extent of a sequence of compiled code that + * includes all compiled code derived from an inline call at depth N. All child nodes of the call + * node (direct or indirect) should model ranges that lie within the parent range. The call node + * reports itself as belonging to the caller method at depth N and its file and line information + * identify the location of the call. + *
      + * + * Ranges also record the location of local and parameter values that are valid for the range's + * extent. Each value maps to a corresponding parameter or local variable attached to the range's + * method. So, a leaf or call node at level 0 records local and parameter values for separate + * sub-extents of the top level method while a leaf or call node at level N+1 records local and + * parameter values for separate sub-extents of an inline called method whose full extent is + * represented by the parent call range at level N. */ public class Range { private static final String CLASS_DELIMITER = "."; - private Range caller; private final MethodEntry methodEntry; private final String fullMethodName; private final String fullMethodNameWithParams; @@ -48,12 +82,13 @@ public class Range { */ private final Range primary; - /* - * Support for tree of nested inline callee ranges + /** + * The range for the caller or the primary range when this range if for top level method code. */ - + private Range caller; /** - * The first direct callee whose range is wholly contained in this range. + * The first direct callee whose range is wholly contained in this range or null if this is a + * leaf range. */ private Range firstCallee; @@ -67,6 +102,40 @@ public class Range { */ private Range siblingCallee; + /** + * Values for the associated method's local and parameter variables that are available or, + * alternatively, marked as invalid in this range. + */ + private DebugLocalValueInfo[] localValueInfos; + + /** + * The set of local or parameter variables with which each corresponding local value in field + * localvalueInfos is associated. Local values which are associated with the same local or + * parameter variable will share the same reference in the corresponding array entries. Local + * values with which no local variable can be associated will have a null reference in the + * corresponding array. The latter case can happen when a local value has an invalid slot or + * when a local value that maps to a parameter slot has a different name or type to the + * parameter. + */ + private DebugLocalInfo[] localInfos; + + public int getLocalValueCount() { + assert !this.isPrimary() : "primary range does not have local values"; + return localValueInfos.length; + } + + public DebugLocalValueInfo getLocalValue(int i) { + assert !this.isPrimary() : "primary range does not have local values"; + assert i >= 0 && i < localValueInfos.length : "bad index"; + return localValueInfos[i]; + } + + public DebugLocalInfo getLocal(int i) { + assert !this.isPrimary() : "primary range does not have local vars"; + assert i >= 0 && i < localInfos.length : "bad index"; + return localInfos[i]; + } + /* * Create a primary range. */ @@ -246,10 +315,6 @@ public Range getSiblingCallee() { return siblingCallee; } - public Range getLastCallee() { - return lastCallee; - } - public boolean isLeaf() { return firstCallee == null; } @@ -270,4 +335,68 @@ public int getDepth() { public boolean isPrologueEnd() { return isPrologueEnd; } + + public void setLocalValueInfo(DebugLocalValueInfo[] localValueInfos) { + int len = localValueInfos.length; + this.localValueInfos = localValueInfos; + this.localInfos = new DebugLocalInfo[len]; + // set up mapping from local values to local variables + for (int i = 0; i < len; i++) { + localInfos[i] = methodEntry.recordLocal(localValueInfos[i]); + } + } + + public HashMap> getVarRangeMap() { + MethodEntry calleeMethod; + if (isPrimary()) { + calleeMethod = getMethodEntry(); + } else { + assert !isLeaf() : "should only be looking up var ranges for inlined calls"; + calleeMethod = firstCallee.getMethodEntry(); + } + HashMap> varRangeMap = new HashMap<>(); + varRangeMap.put(calleeMethod.getThisParam(), new ArrayList()); + for (int i = 0; i < calleeMethod.getParamCount(); i++) { + varRangeMap.put(calleeMethod.getParam(i), new ArrayList()); + } + for (int i = 0; i < calleeMethod.getLocalCount(); i++) { + varRangeMap.put(calleeMethod.getLocal(i), new ArrayList()); + } + return updateVarRangeMap(varRangeMap); + } + + public HashMap> updateVarRangeMap(HashMap> varRangeMap) { + // leaf subranges of the current range may provide values for param or local vars + // of this range's method. find them and index the range so that we can identify + // both the local/param and the associated range. + Range subRange = this.firstCallee; + while (subRange != null) { + addVarRanges(subRange, varRangeMap); + subRange = subRange.siblingCallee; + } + return varRangeMap; + } + + public void addVarRanges(Range subRange, HashMap> varRangeMap) { + int localValueCount = subRange.getLocalValueCount(); + for (int i = 0; i < localValueCount; i++) { + DebugLocalValueInfo localValueInfo = subRange.getLocalValue(i); + DebugLocalInfo local = subRange.getLocal(i); + if (local != null && localValueInfo.localKind() != DebugLocalValueInfo.LocalKind.UNDEFINED) { + List varRanges = varRangeMap.get(local); + assert varRanges != null : "local not present in var to ranges map!"; + varRanges.add(subRange); + } + } + } + + public DebugLocalValueInfo lookupValue(DebugLocalInfo local) { + int localValueCount = getLocalValueCount(); + for (int i = 0; i < localValueCount; i++) { + if (getLocal(i) == local) { + return getLocalValue(i); + } + } + return null; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index f9904a2dfb1c..6be34e1a002c 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -32,6 +32,7 @@ import java.util.stream.Stream; import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; @@ -202,14 +203,15 @@ interface DebugFieldInfo extends DebugMemberInfo { interface DebugMethodInfo extends DebugMemberInfo { /** - * @return an array of Strings identifying the method parameters. + * @return an array of DebugLocalInfo objects holding details of this method's parameters */ - List paramTypes(); + DebugLocalInfo[] getParamInfo(); /** - * @return an array of Strings with the method parameters' names. + * @return a DebugLocalInfo objects holding details of the target instance parameter this if + * the method is an instance method or null if it is a static method. */ - List paramNames(); + DebugLocalInfo getThisParamInfo(); /** * @return the symbolNameForMethod string @@ -338,18 +340,36 @@ interface DebugLocationInfo extends DebugRangeInfo { boolean isPrologueEnd(); /** - * @return a stream of {@link DebugLocalInfo} objects identifying local or parameter + * @return a stream of {@link DebugLocalValueInfo} objects identifying local or parameter * variables present in the frame of the current range. */ - Stream localsProvider(); + DebugLocalValueInfo[] getLocalValueInfo(); } /** - * A DebugLocalInfo identifies a local or parameter variable present in the current frame, which - * may be undefined. If not then the instance records its type and either its (constant) value - * or the register or slot location in which the value can be found. + * A DebugLocalInfo details a local or parameter variable recording its name and type, the + * (abstract machine) local slot index it resides in and the number of slots it occupies. */ interface DebugLocalInfo { + String name(); + + String typeName(); + + int slot(); + + int slotCount(); + + JavaKind javaKind(); + + int line(); + } + + /** + * A DebugLocalValueInfo details the value a local or parameter variable present in a specific + * frame. The value may be undefined. If not then the instance records its type and either its + * (constant) value or the register or stack location in which the value resides. + */ + interface DebugLocalValueInfo extends DebugLocalInfo { enum LocalKind { UNDEFINED, REGISTER, @@ -357,10 +377,6 @@ enum LocalKind { CONSTANT } - String name(); - - String typeName(); - String valueString(); LocalKind localKind(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index 90a4460cb2f9..896e28bf694d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -185,8 +185,16 @@ public void createContent() { *
    • Level 2/3 DIEs * *
    • code == method_parameter_declaration1/2/3, tag == formal_parameter, parent = - * method_declaration1/2, method_location, abstract_inline_method - details of method - * parameters + * method_declaration1/2, abstract_inline_method - details of method parameters + * + *
    • code == method_local_declaration1/2, tag == variable, parent = + * method_declaration1/2, abstract_inline_method - details of method parameters + * + *
    • Level 3 DIEs + * + *
    • code == method_local_location, tag == formal_parameter, parent = + * method_location, concrete_inline_method - details of method parameter or local + * locations * * Details of each specific DIE contents are as follows: * @@ -416,6 +424,25 @@ public void createContent() { *
    • Dw_AT_artificial : ... DW_FORM_flag n.b. only for * method_parameter_declaration1 used for this and access vars * + *
    • Dw_AT_declaration : ... DW_FORM_flag + * + *
    + * + *
  • abbrev_code == method_local_declaration1/2, tag == DW_TAG_variable, + * no_children + * + *
  • Dw_AT_name : ... DW_FORM_strp (may be empty string) + * + *
  • Dw_AT_file : ... DW_FORM_data1/2 n.b. only for + * method_parameter_declaration1 + * + *
  • Dw_AT_line : ... DW_FORM_data1/2 n.b. only for + * method_parameter_declaration1 + * + *
  • Dw_AT_type : ... DW_FORM_ref_addr + * + *
  • Dw_AT_declaration : ... DW_FORM_flag + * *
* * Indirect Instance Class Structure: The level 1 class layout DIE may be followed by a @@ -499,9 +526,10 @@ public void createContent() { * corresponding level 1 DIE providing details of the location of the compiled code for the * method. This DIE should inherit attributes from the method_definition DIE referenced from * its specification attribute without the need to repeat them, including attributes - * specified in child DIEs of the method_definition. However, it is actually necessary to - * replicate the method_parameter DIEs as children of this DIE because gdb does not carry - * these attributes across from the specification DIE. + * specified in child DIEs of the method_definition. It is actually necessary to provide the + * method_location DIE with method_parameter child DIES in order to ensure that gdb carries + * across parameter attributes across from the specification DIE. The local method_parameter + * DIEs refer to their originals using a specification attribute. * * Note that for methods which only occur as inlined code rather than as a top-level * compiles method the method location DIE will be omitted @@ -521,6 +549,27 @@ public void createContent() { * * * + * Method local locations: A method location is followed by zero or more + * method_local_location DIEs that refer back to the corresponding + * method_parameter_declaration or method_local_declaration that follows the method + * declaration. These DIEs also specify a location list which defines address ranges where + * the parameter or local is valid and provide details of where to find the value of the + * parameter or local in memory. Likewise, an inline concrete method DIE is followed by zero + * or more method_local_location DIEs providing details of where to find the specification + * of inlined parameters or locals and their value in memory. + * + *
    + * + *
  • abbrev_code == DW_ABBREV_CODE_method_local_location1/2, tag == + * DW_TAG_formal_parameter, no_children + * + *
  • DW_AT_specification : .......... DW_FORM_ref_addr + * + *
  • DW_AT_location: ................ DW_loc_list n.b. only for + * method_local_location2 + * + *
+ * * Abstract Inline Methods: For any method which has been inlined into another compiled * method there will be a corresponding level 1 DIE that identifies the method declaration * and serves as the target reference for concrete inlined method DIEs. This DIE should @@ -817,6 +866,10 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { } pos = writeParameterDeclarationAbbrevs(context, buffer, pos); + pos = writeLocalDeclarationAbbrevs(context, buffer, pos); + + pos = writeParameterLocationAbbrevs(context, buffer, pos); + pos = writeLocalLocationAbbrevs(context, buffer, pos); /* write a null abbrev to terminate the sequence */ pos = writeNullAbbrev(context, buffer, pos); @@ -1399,15 +1452,14 @@ private int writeParameterDeclarationAbbrev(@SuppressWarnings("unused") DebugCon pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_formal_parameter, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); - /* We don't yet have parameter names. */ - // pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); - // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2) { /* Line numbers for parameter declarations are not (yet?) available. */ pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); - // pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); - // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); } pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); @@ -1416,12 +1468,93 @@ private int writeParameterDeclarationAbbrev(@SuppressWarnings("unused") DebugCon pos = writeAttrType(DwarfDebugInfo.DW_AT_artificial, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); } - /*- - * We don't yet have locations for method parameters, - * not even at the start of the method. - */ - // pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); - // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeLocalDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeLocalDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration1, buffer, pos); + pos = writeLocalDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration2, buffer, pos); + return pos; + } + + private int writeLocalDeclarationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_variable, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration1) { + /* Line numbers for parameter declarations are not (yet?) available. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + } + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeParameterLocationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeParameterLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location1, buffer, pos); + pos = writeParameterLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location2, buffer, pos); + return pos; + } + + private int writeLocalLocationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeLocalLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_local_location1, buffer, pos); + pos = writeLocalLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2, buffer, pos); + return pos; + } + + private int writeParameterLocationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_formal_parameter, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location2) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + } + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeLocalLocationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_variable, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + } /* * Now terminate. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index f7239746df04..550402ea9600 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -32,8 +32,10 @@ import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.DebugInfoBase; +import com.oracle.objectfile.debugentry.MethodEntry; import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; import com.oracle.objectfile.elf.ELFMachine; /** @@ -101,10 +103,17 @@ public class DwarfDebugInfo extends DebugInfoBase { /* Level 2+K DIEs (where inline depth K >= 0) */ public static final int DW_ABBREV_CODE_inlined_subroutine = 31; public static final int DW_ABBREV_CODE_inlined_subroutine_with_children = 32; - /* Level 3 DIEs. */ + /* Level 2 DIEs. */ public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 33; public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 34; public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 35; + public static final int DW_ABBREV_CODE_method_local_declaration1 = 36; + public static final int DW_ABBREV_CODE_method_local_declaration2 = 37; + /* Level 3 DIEs. */ + public static final int DW_ABBREV_CODE_method_parameter_location1 = 38; + public static final int DW_ABBREV_CODE_method_parameter_location2 = 39; + public static final int DW_ABBREV_CODE_method_local_location1 = 40; + public static final int DW_ABBREV_CODE_method_local_location2 = 41; /* * Define all the Dwarf tags we need for our DIEs. @@ -330,6 +339,7 @@ public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { this.threadRegister = rthread_x86; } propertiesIndex = new HashMap<>(); + methodPropertiesIndex = new HashMap<>(); } public DwarfStrSectionImpl getStrSectionImpl() { @@ -753,4 +763,94 @@ public int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodName assert abstractInlineMethodIndex.get(methodName) != null : classEntry.getTypeName() + methodName; return abstractInlineMethodIndex.get(methodName); } + + /** + * A class used to associate properties with a specific primary or abstract inline method. This + * includes the index of each parameter or local declaration for the method in the info section, + * either for a top level or an inline version of the parameter/local. It also includes the + * index of each such parameter or local's location list in the loc section. Note, however, that + * in some cases a parameter or local may not have a corresponding location list. + */ + + static final class DwarfMethodProperties { + private HashMap locals; + private HashMap inlineLocals; + private HashMap locations; + private HashMap inlineLocations; + + private DwarfMethodProperties() { + locals = new HashMap<>(); + inlineLocals = new HashMap<>(); + locations = new HashMap<>(); + inlineLocations = new HashMap<>(); + } + + int getIndex(DebugLocalInfo localInfo, boolean isInline) { + HashMap localsIndex = (isInline ? inlineLocals : locals); + return localsIndex.get(localInfo); + } + + void setIndex(DebugLocalInfo localInfo, boolean isInline, int index) { + HashMap localsIndex = (isInline ? inlineLocals : locals); + if (localsIndex.get(localInfo) != null) { + assert localsIndex.get(localInfo) == index; + } else { + localsIndex.put(localInfo, index); + } + } + + int getLocationIndex(DebugLocalInfo localInfo, boolean isInline) { + HashMap locationsIndex = (isInline ? inlineLocations : locations); + return locationsIndex.get(localInfo); + } + + void setLocationIndex(DebugLocalInfo localInfo, boolean isInline, int index) { + HashMap locationsIndex = (isInline ? inlineLocations : locations); + if (locationsIndex.get(localInfo) != null) { + assert locationsIndex.get(localInfo) == index; + } else { + locationsIndex.put(localInfo, index); + } + } + } + + private HashMap methodPropertiesIndex; + + private DwarfMethodProperties addMethodProperties(MethodEntry methodEntry) { + DwarfMethodProperties methodProperties = new DwarfMethodProperties(); + methodPropertiesIndex.put(methodEntry, methodProperties); + return methodProperties; + } + + public void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, boolean isInline, int index) { + DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + if (methodProperties == null) { + methodProperties = addMethodProperties(methodEntry); + } + methodProperties.setIndex(localInfo, isInline, index); + } + + public int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localinfo, boolean isInline) { + DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + assert methodProperties != null : "get of non-existent local index"; + int index = methodProperties.getIndex(localinfo, isInline); + assert index > 0 : "get of local index before it was set"; + return index; + } + + public void setMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, boolean isInline, int index) { + DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + if (methodProperties == null) { + methodProperties = addMethodProperties(methodEntry); + } + methodProperties.setLocationIndex(localInfo, isInline, index); + } + + public int getMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo localinfo, boolean isInline) { + DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + assert methodProperties != null : "get of non-existent local index"; + int index = methodProperties.getLocationIndex(localinfo, isInline); + assert index > 0 : "get of local index before it was set"; + return index; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index f82112022f3f..77844d0e3081 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -27,12 +27,13 @@ package com.oracle.objectfile.elf.dwarf; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import com.oracle.objectfile.debugentry.MethodEntry; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.BuildDependency; @@ -45,11 +46,14 @@ import com.oracle.objectfile.debugentry.FileEntry; import com.oracle.objectfile.debugentry.HeaderTypeEntry; import com.oracle.objectfile.debugentry.InterfaceClassEntry; +import com.oracle.objectfile.debugentry.MethodEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.PrimitiveTypeEntry; import com.oracle.objectfile.debugentry.Range; import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo; import com.oracle.objectfile.elf.ELFObjectFile; @@ -725,48 +729,58 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, writeAttrRefAddr(pos, buffer, objectPointerIndex); } /* Write method parameter declarations. */ - pos = writeMethodParameterDeclarations(context, classEntry, method, true, buffer, pos); + pos = writeMethodParameterDeclarations(context, method, fileIdx, 3, false, buffer, pos); + /* write method local declarations */ + pos = writeMethodLocalDeclarations(context, method, fileIdx, 3, false, buffer, pos); /* * Write a terminating null attribute. */ return writeAttrNull(buffer, pos); } - private int writeMethodParameterDeclarations(DebugContext context, ClassEntry classEntry, MethodEntry method, boolean isSpecification, byte[] buffer, int p) { + private int writeMethodParameterDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, boolean isInline, byte[] buffer, int p) { int pos = p; + int refAddr; if (!Modifier.isStatic(method.getModifiers())) { - pos = writeMethodParameterDeclaration(context, "this", classEntry.getTypeName(), true, isSpecification, buffer, pos); - } - if (method.getParamTypes() == null) { - return pos; + refAddr = pos; + DebugLocalInfo paramInfo = method.getThisParam(); + setMethodLocalIndex(method, paramInfo, isInline, refAddr); + pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, true, level, buffer, pos); } - for (TypeEntry paramType : method.getParamTypes()) { - String paramTypeName = paramType.getTypeName(); - String paramName = uniqueDebugString(""); - FileEntry fileEntry = method.getFileEntry(); - if (fileEntry != null) { - pos = writeMethodParameterDeclaration(context, paramName, paramTypeName, false, isSpecification, buffer, pos); - } else { - pos = writeMethodParameterDeclaration(context, paramTypeName, paramTypeName, false, isSpecification, buffer, pos); - } + for (int i = 0; i < method.getParamCount(); i++) { + refAddr = pos; + DebugLocalInfo paramInfo = method.getParam(i); + setMethodLocalIndex(method, paramInfo, isInline, refAddr); + pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, false, level, buffer, pos); } return pos; } - private int writeMethodParameterDeclaration(DebugContext context, @SuppressWarnings("unused") String paramName, String paramTypeName, boolean artificial, boolean isSpecification, byte[] buffer, + private int writeMethodParameterDeclaration(DebugContext context, DebugLocalInfo paramInfo, int fileIdx, boolean artificial, int level, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] method parameter declaration", pos); int abbrevCode; - int level = (isSpecification ? 3 : 2); + String paramName = paramInfo.name(); + String paramTypeName = paramInfo.typeName(); + int line = paramInfo.line(); if (artificial) { abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; + } else if (line >= 0) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2; } else { abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; } log(context, " [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - /* We don't have parameter names at present. */ + log(context, " [0x%08x] name %s", pos, paramName); + pos = writeAttrStrp(uniqueDebugString(paramName), buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2) { + log(context, " [0x%08x] file 0x%x", pos, fileIdx); + pos = writeAttrData2((short) fileIdx, buffer, pos); + log(context, " [0x%08x] line 0x%x", pos, line); + pos = writeAttrData2((short) line, buffer, pos); + } int typeIdx = getTypeIndex(paramTypeName); log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, paramTypeName); pos = writeAttrRefAddr(typeIdx, buffer, pos); @@ -774,6 +788,51 @@ private int writeMethodParameterDeclaration(DebugContext context, @SuppressWarni log(context, " [0x%08x] artificial true", pos); pos = writeFlag((byte) 1, buffer, pos); } + log(context, " [0x%08x] declaration true", pos); + pos = writeFlag((byte) 1, buffer, pos); + return pos; + } + + private int writeMethodLocalDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, boolean isInline, byte[] buffer, int p) { + int pos = p; + int refAddr; + for (int i = 0; i < method.getLocalCount(); i++) { + refAddr = pos; + DebugLocalInfo localInfo = method.getLocal(i); + setMethodLocalIndex(method, localInfo, isInline, refAddr); + pos = writeMethodLocalDeclaration(context, localInfo, fileIdx, level, buffer, pos); + } + return pos; + } + + private int writeMethodLocalDeclaration(DebugContext context, DebugLocalInfo paramInfo, int fileIdx, int level, byte[] buffer, + int p) { + int pos = p; + log(context, " [0x%08x] method local declaration", pos); + int abbrevCode; + String paramName = paramInfo.name(); + String paramTypeName = paramInfo.typeName(); + int line = paramInfo.line(); + if (line >= 0) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration1; + } else { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration2; + } + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name %s", pos, paramName); + pos = writeAttrStrp(uniqueDebugString(paramName), buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_declaration1) { + log(context, " [0x%08x] file 0x%x", pos, fileIdx); + pos = writeAttrData2((short) fileIdx, buffer, pos); + log(context, " [0x%08x] line 0x%x", pos, line); + pos = writeAttrData2((short) line, buffer, pos); + } + int typeIdx = getTypeIndex(paramTypeName); + log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, paramTypeName); + pos = writeAttrRefAddr(typeIdx, buffer, pos); + log(context, " [0x%08x] declaration true", pos); + pos = writeFlag((byte) 1, buffer, pos); return pos; } @@ -980,7 +1039,12 @@ private int generateConcreteInlinedMethods(DebugContext context, PrimaryEntry pr depth--; } depth = subrange.getDepth(); - pos = writeInlineSubroutine(context, subrange, depth, buffer, pos); + pos = writeInlineSubroutine(context, subrange, depth + 2, buffer, pos); + HashMap> varRangeMap = subrange.getVarRangeMap(); + // increment depth to account for parameter and method locations + depth++; + pos = writeMethodParameterLocations(context, varRangeMap, subrange, depth + 2, true, buffer, pos); + pos = writeMethodLocalLocations(context, varRangeMap, subrange, depth + 2, true, buffer, pos); } // if we just stepped out of a child range write nulls for each step up while (depth > 0) { @@ -1301,8 +1365,10 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri int methodSpecOffset = getMethodDeclarationIndex(classEntry, methodKey); log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); pos = writeAttrRefAddr(methodSpecOffset, buffer, pos); - pos = writeMethodParameterDeclarations(context, classEntry, primary.getMethodEntry(), false, buffer, pos); - if (!primary.isLeaf()) { + HashMap> varRangeMap = primary.getVarRangeMap(); + pos = writeMethodParameterLocations(context, varRangeMap, primary, 2, false, buffer, pos); + pos = writeMethodLocalLocations(context, varRangeMap, primary, 2, false, buffer, pos); + if (primary.includesInlineRanges()) { /* * the method has inlined ranges so write concrete inlined method entries as its * children @@ -1315,6 +1381,110 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri return writeAttrNull(buffer, pos); } + private int writeMethodParameterLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, boolean isInline, byte[] buffer, int p) { + int pos = p; + MethodEntry methodEntry; + if (range.isPrimary()) { + methodEntry = range.getMethodEntry(); + } else { + assert !range.isLeaf() : "should only be looking up var ranges for inlined calls"; + methodEntry = range.getFirstCallee().getMethodEntry(); + } + if (!Modifier.isStatic(methodEntry.getModifiers())) { + DebugLocalInfo thisParamInfo = methodEntry.getThisParam(); + int refAddr = getMethodLocalIndex(methodEntry, thisParamInfo, isInline); + // int locRefAddr = getMethodLocalIndex(methodEntry, thisParamInfo, isInline);; + List ranges = varRangeMap.get(thisParamInfo); + pos = writeMethodLocalLocation(context, refAddr, ranges, thisParamInfo, depth, true, buffer, pos); + } + for (int i = 0; i < methodEntry.getParamCount(); i++) { + DebugLocalInfo paramInfo = methodEntry.getParam(i); + int refAddr = getMethodLocalIndex(methodEntry, paramInfo, isInline); + // int locRefAddr = getMethodLocalIndex(methodEntry, paramInfo, isInline); + List ranges = varRangeMap.get(paramInfo); + pos = writeMethodLocalLocation(context, refAddr, ranges, paramInfo, depth, true, buffer, pos); + } + return pos; + } + + private int writeMethodLocalLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, boolean isInline, byte[] buffer, int p) { + int pos = p; + MethodEntry methodEntry; + if (range.isPrimary()) { + methodEntry = range.getMethodEntry(); + } else { + assert !range.isLeaf() : "should only be looking up var ranges for inlined calls"; + methodEntry = range.getFirstCallee().getMethodEntry(); + } + int count = methodEntry.getLocalCount(); + for (int i = 0; i < count; i++) { + DebugLocalInfo localInfo = methodEntry.getLocal(i); + int refAddr = getMethodLocalIndex(methodEntry, localInfo, isInline); + // int locRefAddr = getMethodLocationIndex(methodEntry, localInfo, isInline); + List ranges = varRangeMap.get(localInfo); + pos = writeMethodLocalLocation(context, refAddr, ranges, localInfo, depth, false, buffer, pos); + } + return pos; + } + + @SuppressWarnings("unused") + private int writeMethodLocalLocation(DebugContext context, int refAddr, List ranges, DebugLocalInfo localInfo, int depth, boolean isParam, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] method %s location %s:%s", pos, (isParam ? "parameter" : "local"), localInfo.name(), localInfo.typeName()); + List localValues = new ArrayList<>(); + /*- + for (Range range : ranges) { + DebugLocalValueInfo value = range.lookupValue(localInfo); + if (value != null) { + log(context, " [0x%08x] local %s:%s [0x%x, 0x%x] = %s", pos, value.name(), value.typeName(), range.getLo(), range.getHi(), formatValue(value)); + switch (value.localKind()) { + case REGISTER: + case STACKSLOT: + localValues.add(value); + break; + case CONSTANT: + // cannot handle constants just yet + default: + } + } + } + */ + int abbrevCode = (isParam ? DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location1 : DwarfDebugInfo.DW_ABBREV_CODE_method_local_location1); + /*- + int abbrevCode; + if (localValues.isEmpty()) { + abbrevCode = (isParam ? DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location1 : DwarfDebugInfo.DW_ABBREV_CODE_method_local_location1); + } else { + abbrevCode = (isParam ? DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location2 : DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2); + }; + */ + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] specification 0x%x", pos, refAddr); + pos = writeAttrRefAddr(refAddr, buffer, pos); + /*- + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2) { + pos = writeAttrRefAddr(locRefAddr, buffer, pos); + } + */ + return pos; + } + + @SuppressWarnings("unused") + private static String formatValue(DebugLocalValueInfo value) { + switch (value.localKind()) { + case REGISTER: + return "REG:" + value.regIndex(); + case STACKSLOT: + return "STACK:" + value.stackSlot(); + case CONSTANT: + return "CONST:" + value.constantValue(); + case UNDEFINED: + default: + return "-"; + } + } + private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) { int pos = p; String methodKey = method.getSymbolName(); @@ -1331,8 +1501,15 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); int methodSpecOffset = getMethodDeclarationIndex(classEntry, methodKey); log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); + FileEntry fileEntry = method.getFileEntry(); + if (fileEntry == null) { + fileEntry = classEntry.getFileEntry(); + } + assert fileEntry != null; + int fileIdx = classEntry.localFilesIdx(fileEntry); pos = writeAttrRefAddr(methodSpecOffset, buffer, pos); - pos = writeMethodParameterDeclarations(context, classEntry, method, false, buffer, pos); + pos = writeMethodParameterDeclarations(context, method, fileIdx, 2, true, buffer, pos); + pos = writeMethodLocalDeclarations(context, method, fileIdx, 2, true, buffer, pos); /* * Write a terminating null attribute. */ @@ -1341,6 +1518,9 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr private int writeInlineSubroutine(DebugContext context, Range caller, int depth, byte[] buffer, int p) { assert !caller.isLeaf(); + // the supplied range covers an inline call and references the caller method entry. its + // child ranges all reference the same inlined called method. leaf children cover code for + // that inlined method. non-leaf children cover code for recursively inlined methods. // identify the inlined method by looking at the first callee Range callee = caller.getFirstCallee(); MethodEntry methodEntry = callee.getMethodEntry(); @@ -1367,12 +1547,8 @@ private int writeInlineSubroutine(DebugContext context, Range caller, int depth, assert fileIndex != null; } final int code; - if (caller.includesInlineRanges()) { - code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; - } else { - code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; - } - log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 2, code); + code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth, code); pos = writeAbbrevCode(code, buffer, pos); log(context, " [0x%08x] abstract_origin 0x%x", pos, specificationIndex); pos = writeAttrRef4(specificationIndex, buffer, pos); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 20310ef6666e..9c1e5d7f46cd 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -32,8 +32,10 @@ import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.MethodEntry; import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; import com.oracle.objectfile.elf.ELFMachine; import com.oracle.objectfile.elf.ELFObjectFile; import org.graalvm.compiler.debug.DebugContext; @@ -613,4 +615,26 @@ protected int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodN } return dwarfSections.getAbstractInlineMethodIndex(classEntry, methodName); } + + protected void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline, int index) { + dwarfSections.setMethodLocalIndex(methodEntry, paramInfo, isInline, index); + } + + protected int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getMethodLocalIndex(methodEntry, paramInfo, isInline); + } + + protected void setMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline, int index) { + dwarfSections.setMethodLocationIndex(methodEntry, paramInfo, isInline, index); + } + + protected int getMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getMethodLocationIndex(methodEntry, paramInfo, isInline); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java index 55257cd4b84f..36c1f685655b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java @@ -464,7 +464,7 @@ public Builder(DebugContext debug, int targetCodeSize, boolean verify) { @SuppressWarnings("try") public CallNode build(CompilationResult compilationResult) { try (DebugContext.Scope s = debug.scope("FrameTree.Builder", compilationResult)) { - debug.log("Building FrameTree for %s", compilationResult); + debug.log(DebugContext.VERBOSE_LEVEL, "Building FrameTree for %s", compilationResult); List infopoints = compilationResult.getInfopoints(); List sourceMappings = compilationResult.getSourceMappings(); @@ -476,21 +476,21 @@ public CallNode build(CompilationResult compilationResult) { sourcePosData.add(wrapper); infopointForRoot = wrapper; } else { - debug.log(" Discard Infopoint without BytecodePosition %s", infopoint); + debug.log(DebugContext.DETAILED_LEVEL, " Discard Infopoint without BytecodePosition %s", infopoint); } } for (SourceMapping sourceMapping : sourceMappings) { SourceMappingWrapper wrapper = SourceMappingWrapper.create(sourceMapping); if (wrapper != null) { if (wrapper.getStartOffset() > targetCodeSize - 1) { - if (debug.isLogEnabled()) { + if (debug.isLogEnabled(DebugContext.DETAILED_LEVEL)) { debug.log(" Discard SourceMapping outside code-range %s", SourceMappingWrapper.getSourceMappingString(sourceMapping)); } continue; } sourcePosData.add(wrapper); } else { - if (debug.isLogEnabled()) { + if (debug.isLogEnabled(DebugContext.DETAILED_LEVEL)) { debug.log(" Discard SourceMapping without NodeSourcePosition %s", SourceMappingWrapper.getSourceMappingString(sourceMapping)); } } @@ -500,7 +500,7 @@ public CallNode build(CompilationResult compilationResult) { nullifyOverlappingSourcePositions(sourcePosData); - if (debug.isLogEnabled()) { + if (debug.isLogEnabled(DebugContext.DETAILED_LEVEL)) { debug.log("Sorted input data:"); for (SourcePositionSupplier sourcePositionSupplier : sourcePosData) { if (sourcePositionSupplier != null) { @@ -593,7 +593,7 @@ void nullifyOverlappingSourcePositions(List sourcePosDat SourcePositionSupplier rightPos = sourcePosData.get(indexRight); if (overlappingSourcePosition(leftPos, rightPos)) { - debug.log("Handle Overlapping SourcePositions: %s | %s", leftPos, rightPos); + debug.log(DebugContext.DETAILED_LEVEL, "Handle Overlapping SourcePositions: %s | %s", leftPos, rightPos); /* Handle infopoint overlapping */ if (leftPos instanceof InfopointSourceWrapper && rightPos instanceof InfopointSourceWrapper) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 0a178e68002a..8d5e2309d25e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -73,6 +73,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.Signature; +import jdk.vm.ci.meta.Value; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.debug.DebugContext; @@ -83,6 +84,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -111,6 +113,9 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { int primitiveStartOffset; int referenceStartOffset; private final Set allOverrides; + HostedType wordBaseType; + + private static final String GRAAL_WORDBASE_TYPE_NAME = "Lorg/graalvm/word/WordBase;"; NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap) { super(); @@ -141,6 +146,7 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { .flatMap(m -> Arrays.stream(m.getImplementations()) .filter(Predicate.not(m::equals))) .collect(Collectors.toSet()); + wordBaseType = heap.getUniverse().getTypes().stream().filter(type -> type.getName().equals(GRAAL_WORDBASE_TYPE_NAME)).findFirst().get(); } @Override @@ -221,11 +227,7 @@ protected static ResolvedJavaType getDeclaringClass(HostedMethod hostedMethod, b } // we want a substituted target if there is one. if there is a substitution at the end of // the method chain fetch the annotated target class - ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); - if (javaMethod instanceof SubstitutionMethod) { - SubstitutionMethod substitutionMethod = (SubstitutionMethod) javaMethod; - return substitutionMethod.getAnnotated().getDeclaringClass(); - } + ResolvedJavaMethod javaMethod = getOriginal(hostedMethod); return javaMethod.getDeclaringClass(); } @@ -250,14 +252,19 @@ private static ResolvedJavaType getOriginal(HostedType hostedType) { return javaType; } - private static int getOriginalModifiers(HostedMethod hostedMethod) { - ResolvedJavaMethod targetMethod = hostedMethod.getWrapped().getWrapped(); - if (targetMethod instanceof SubstitutionMethod) { - targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); - } else if (targetMethod instanceof CustomSubstitutionMethod) { - targetMethod = ((CustomSubstitutionMethod) targetMethod).getOriginal(); + private static ResolvedJavaMethod getOriginal(HostedMethod hostedMethod) { + ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); + if (javaMethod instanceof SubstitutionMethod) { + SubstitutionMethod substitutionMethod = (SubstitutionMethod) javaMethod; + javaMethod = substitutionMethod.getAnnotated(); + } else if (javaMethod instanceof CustomSubstitutionMethod) { + javaMethod = ((CustomSubstitutionMethod) javaMethod).getOriginal(); } - return targetMethod.getModifiers(); + return javaMethod; + } + + private static int getOriginalModifiers(HostedMethod hostedMethod) { + return getOriginal(hostedMethod).getModifiers(); } private static String toJavaName(JavaType javaType) { @@ -803,10 +810,25 @@ private NativeImageDebugTypeInfo createDebugTypeInfo(HostedType hostedType) { protected abstract class NativeImageDebugBaseMethodInfo extends NativeImageDebugFileInfo implements DebugMethodInfo { protected final ResolvedJavaMethod method; + protected final List paramInfo; + protected final DebugLocalInfo thisParamInfo; NativeImageDebugBaseMethodInfo(ResolvedJavaMethod method) { super(method); this.method = method; + this.paramInfo = createParamInfo(method); + // We use the target modifiers to decide where to install any first param + // even though we may have added it according to whether method is static. + // That's because in a few special cases method is static but the original + // DebugFrameLocals + // from which it is derived is an instance method. This appears to happen + // when a C function pointer masquerades as a method. Whatever parameters + // we pass through need to match the definition of the original. + if (Modifier.isStatic(modifiers())) { + this.thisParamInfo = null; + } else { + this.thisParamInfo = paramInfo.remove(0); + } } /** @@ -896,40 +918,26 @@ public String valueType() { } @Override - public String symbolNameForMethod() { - return NativeImage.localSymbolNameForMethod(method); + public DebugLocalInfo[] getParamInfo() { + return paramInfo.toArray(new DebugLocalInfo[paramInfo.size()]); } @Override - public boolean isDeoptTarget() { - if (method instanceof HostedMethod) { - return ((HostedMethod) method).isDeoptTarget(); - } - return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + public DebugLocalInfo getThisParamInfo() { + return thisParamInfo; } @Override - public List paramTypes() { - Signature signature = method.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramTypes = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - JavaType parameterType = signature.getParameterType(i, null); - paramTypes.add(toJavaName(parameterType)); - } - return paramTypes; + public String symbolNameForMethod() { + return NativeImage.localSymbolNameForMethod(method); } @Override - public List paramNames() { - /* Can only provide blank names for now. */ - Signature signature = method.getSignature(); - int parameterCount = signature.getParameterCount(false); - List paramNames = new ArrayList<>(parameterCount); - for (int i = 0; i < parameterCount; i++) { - paramNames.add(""); + public boolean isDeoptTarget() { + if (method instanceof HostedMethod) { + return ((HostedMethod) method).isDeoptTarget(); } - return paramNames; + return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); } @Override @@ -961,6 +969,51 @@ public int vtableOffset() { } } + private List createParamInfo(ResolvedJavaMethod method) { + Signature signature = method.getSignature(); + int parameterCount = signature.getParameterCount(false); + List paramInfos = new ArrayList<>(parameterCount); + LocalVariableTable table = method.getLocalVariableTable(); + LineNumberTable lineNumberTable = method.getLineNumberTable(); + int line = (lineNumberTable != null ? lineNumberTable.getLineNumber(0) : -1); + int slot = 0; + if (!method.isStatic()) { + ResolvedJavaType ownerType = method.getDeclaringClass(); + JavaKind kind = ownerType.getJavaKind(); + JavaKind storageKind = isPseudoObjectType(ownerType) ? JavaKind.Long : kind; + assert kind == JavaKind.Object : "must be an object"; + paramInfos.add(new NativeImageDebugLocalValueInfo("this", storageKind, ownerType, slot, line)); + slot += kind.getSlotCount(); + } + for (int i = 0; i < parameterCount; i++) { + JavaType type = signature.getParameterType(i, null); + JavaKind kind = type.getJavaKind(); + JavaKind storageKind = isPseudoObjectType(type) ? JavaKind.Long : kind; + Local local = (table == null ? null : table.getLocal(slot, 0)); + String name = (local != null ? local.getName() : "__" + i); + paramInfos.add(new NativeImageDebugLocalValueInfo(name, storageKind, type.resolve(null), slot, line)); + slot += kind.getSlotCount(); + } + return paramInfos; + } + + /** + * Identify a pseudo-object Java type which is used only to model a memory word, pointer or + * foreign opaque type. + * + * @param type the type to be tested + * @return true if the type is a pseudo object type + */ + private boolean isPseudoObjectType(JavaType type) { + ResolvedJavaType resolvedJavaType = type.resolve(null); + return (wordBaseType.isAssignableFrom(resolvedJavaType)); + } + + private static boolean isIntegralKindPromotion(JavaKind promoted, JavaKind original) { + return (promoted == JavaKind.Int && + (original == JavaKind.Boolean || original == JavaKind.Byte || original == JavaKind.Short || original == JavaKind.Char)); + } + protected abstract class NativeImageDebugHostedMethodInfo extends NativeImageDebugBaseMethodInfo { protected final HostedMethod hostedMethod; @@ -1387,7 +1440,7 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf private int hi; private DebugLocationInfo callersLocationInfo; private boolean isPrologueEnd; - private List localInfoList; + private List localInfoList; NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo, frameNode.sourcePos.isMethodStart()); @@ -1407,7 +1460,7 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf this.localInfoList = initLocalInfoList(bcpos); } - private List initLocalInfoList(BytecodePosition bcpos) { + private List initLocalInfoList(BytecodePosition bcpos) { if (!(bcpos instanceof BytecodeFrame)) { return null; } @@ -1416,27 +1469,39 @@ private List initLocalInfoList(BytecodePosition bcpos) { if (frame.numLocals == 0) { return null; } + // deal with any inconsistencies in the layout of the frame locals + // NativeImageDebugFrameInfo debugFrameInfo = new NativeImageDebugFrameInfo(frame); + + LineNumberTable lineNumberTable = frame.getMethod().getLineNumberTable(); Local[] localsBySlot = getLocalsBySlot(); if (localsBySlot == null) { - // TODO perhaps try synthesising locals info here - return null; + return Collections.emptyList(); } int count = Integer.min(localsBySlot.length, frame.numLocals); - ArrayList localInfos = new ArrayList<>(count); + ArrayList localInfos = new ArrayList<>(count); for (int i = 0; i < count; i++) { - JavaValue value = frame.getLocalValue(i); - JavaKind kind = frame.getLocalValueKind(i); Local l = localsBySlot[i]; - String name; - ResolvedJavaType type; if (l != null) { - name = l.getName(); - type = l.getType().resolve(method.getDeclaringClass()); - } else { - name = "_" + i; - type = null; + // we have a local with a known name, type and slot + String name = l.getName(); + ResolvedJavaType type = l.getType().resolve(method.getDeclaringClass()); + JavaKind kind = type.getJavaKind(); + int slot = l.getSlot(); + debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", i, name, type.getName(), slot); + JavaValue value = (slot < frame.numLocals ? frame.getLocalValue(slot) : Value.ILLEGAL); + JavaKind storageKind = (slot < frame.numLocals ? frame.getLocalValueKind(slot) : JavaKind.Illegal); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + int bciStart = l.getStartBCI(); + int line = (lineNumberTable != null ? lineNumberTable.getLineNumber(bciStart) : -1); + // only add the local if the kinds match + if ((storageKind == kind) || + isIntegralKindPromotion(storageKind, kind) || + (isPseudoObjectType(type) && kind == JavaKind.Object && storageKind == JavaKind.Long)) { + localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, type, slot, line)); + } else if (storageKind != JavaKind.Illegal) { + debugContext.log(DebugContext.DETAILED_LEVEL, " value kind incompatible with var kind %s!", type.getJavaKind().toString()); + } } - localInfos.add(new NativeImageDebugLocalInfo(name, value, kind, type)); } return localInfos; } @@ -1445,7 +1510,7 @@ private Local[] getLocalsBySlot() { LocalVariableTable lvt = method.getLocalVariableTable(); Local[] nonEmptySortedLocals = null; if (lvt != null) { - Local[] locals = lvt.getLocals(); + Local[] locals = lvt.getLocalsAt(bci); if (locals != null && locals.length > 0) { nonEmptySortedLocals = Arrays.copyOf(locals, locals.length); Arrays.sort(nonEmptySortedLocals, (Local l1, Local l2) -> l1.getSlot() - l2.getSlot()); @@ -1484,11 +1549,11 @@ public boolean isPrologueEnd() { } @Override - public Stream localsProvider() { + public DebugLocalValueInfo[] getLocalValueInfo() { if (localInfoList != null) { - return localInfoList.stream(); + return localInfoList.toArray(new DebugLocalValueInfo[localInfoList.size()]); } else { - return Stream.empty(); + return new DebugLocalValueInfo[0]; } } @@ -1539,8 +1604,8 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { return null; } for (int i = 0; i < size; i++) { - NativeImageDebugLocalInfo thisLocal = (NativeImageDebugLocalInfo) localInfoList.get(i); - NativeImageDebugLocalInfo thatLocal = (NativeImageDebugLocalInfo) that.localInfoList.get(i); + NativeImageDebugLocalValueInfo thisLocal = (NativeImageDebugLocalValueInfo) localInfoList.get(i); + NativeImageDebugLocalValueInfo thatLocal = (NativeImageDebugLocalValueInfo) that.localInfoList.get(i); if (!thisLocal.sameAs(thatLocal)) { return null; } @@ -1553,18 +1618,26 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { } } - public class NativeImageDebugLocalInfo implements DebugLocalInfo { + public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { private final String name; private ResolvedJavaType type; private final JavaValue value; private final JavaKind kind; + private int slot; + private int line; private LocalKind localKind; - NativeImageDebugLocalInfo(String name, JavaValue value, JavaKind kind, ResolvedJavaType type) { + NativeImageDebugLocalValueInfo(String name, JavaKind kind, ResolvedJavaType type, int slot, int line) { + this(name, Value.ILLEGAL, kind, type, slot, line); + } + + NativeImageDebugLocalValueInfo(String name, JavaValue value, JavaKind kind, ResolvedJavaType type, int slot, int line) { this.name = name; this.value = value; this.kind = kind; this.type = type; + this.slot = slot; + this.line = line; if (value instanceof RegisterValue) { this.localKind = LocalKind.REGISTER; } else if (value instanceof StackSlot) { @@ -1593,6 +1666,26 @@ public String typeName() { } } + @Override + public int slot() { + return slot; + } + + @Override + public int slotCount() { + return kind.getSlotCount(); + } + + @Override + public JavaKind javaKind() { + return kind; + } + + @Override + public int line() { + return line; + } + @Override public String valueString() { switch (localKind) { @@ -1609,7 +1702,7 @@ public String valueString() { @Override public LocalKind localKind() { - return null; + return localKind; } @Override @@ -1627,7 +1720,7 @@ public Constant constantValue() { return null; } - public boolean sameAs(NativeImageDebugLocalInfo that) { + public boolean sameAs(NativeImageDebugLocalValueInfo that) { if (localKind == that.localKind) { switch (localKind) { case REGISTER: From cb2e3b595389226f3836c7fa3fdbeca960800b4b Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Wed, 6 Apr 2022 15:06:11 +0100 Subject: [PATCH 06/22] First cut of local var info for stack and reg slots. --- .../oracle/objectfile/debugentry/Range.java | 19 +- .../oracle/objectfile/elf/ELFObjectFile.java | 4 + .../elf/dwarf/DwarfAbbrevSectionImpl.java | 8 +- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 112 ++- .../elf/dwarf/DwarfInfoSectionImpl.java | 118 +-- .../elf/dwarf/DwarfLineSectionImpl.java | 105 ++- .../elf/dwarf/DwarfLocSectionImpl.java | 671 ++++++++++++++++++ .../elf/dwarf/DwarfSectionImpl.java | 50 +- .../image/NativeImageDebugInfoProvider.java | 27 +- 9 files changed, 915 insertions(+), 199 deletions(-) create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 2f5417118fbc..326973855b7f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -355,7 +355,9 @@ public HashMap> getVarRangeMap() { calleeMethod = firstCallee.getMethodEntry(); } HashMap> varRangeMap = new HashMap<>(); - varRangeMap.put(calleeMethod.getThisParam(), new ArrayList()); + if (calleeMethod.getThisParam() != null) { + varRangeMap.put(calleeMethod.getThisParam(), new ArrayList()); + } for (int i = 0; i < calleeMethod.getParamCount(); i++) { varRangeMap.put(calleeMethod.getParam(i), new ArrayList()); } @@ -383,9 +385,18 @@ public void addVarRanges(Range subRange, HashMap> va DebugLocalValueInfo localValueInfo = subRange.getLocalValue(i); DebugLocalInfo local = subRange.getLocal(i); if (local != null && localValueInfo.localKind() != DebugLocalValueInfo.LocalKind.UNDEFINED) { - List varRanges = varRangeMap.get(local); - assert varRanges != null : "local not present in var to ranges map!"; - varRanges.add(subRange); + switch (localValueInfo.localKind()) { + case REGISTER: + case STACKSLOT: + List varRanges = varRangeMap.get(local); + assert varRanges != null : "local not present in var to ranges map!"; + varRanges.add(subRange); + break; + case CONSTANT: + // drop through cannot handle constants for now + case UNDEFINED: + break; + } } } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index c48115c3c024..65f6dc17e4cc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; +import com.oracle.objectfile.elf.dwarf.DwarfLocSectionImpl; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -1172,6 +1173,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); DwarfFrameSectionImpl frameSectionImpl = dwarfSections.getFrameSectionImpl(); + DwarfLocSectionImpl elfLocSectionImpl = dwarfSections.getLocSectionImpl(); DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); @@ -1179,6 +1181,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { newUserDefinedSection(elfStrSectionImpl.getSectionName(), elfStrSectionImpl); newUserDefinedSection(elfAbbrevSectionImpl.getSectionName(), elfAbbrevSectionImpl); newUserDefinedSection(frameSectionImpl.getSectionName(), frameSectionImpl); + newUserDefinedSection(elfLocSectionImpl.getSectionName(), elfLocSectionImpl); newUserDefinedSection(elfInfoSectionImpl.getSectionName(), elfInfoSectionImpl); newUserDefinedSection(elfARangesSectionImpl.getSectionName(), elfARangesSectionImpl); newUserDefinedSection(elfLineSectionImpl.getSectionName(), elfLineSectionImpl); @@ -1195,6 +1198,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { elfAbbrevSectionImpl.getOrCreateRelocationElement(0); frameSectionImpl.getOrCreateRelocationElement(0); elfInfoSectionImpl.getOrCreateRelocationElement(0); + elfLocSectionImpl.getOrCreateRelocationElement(0); elfARangesSectionImpl.getOrCreateRelocationElement(0); elfLineSectionImpl.getOrCreateRelocationElement(0); /* Ok now we can populate the debug info model. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index 896e28bf694d..6732ac619fd8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -565,7 +565,7 @@ public void createContent() { * *
  • DW_AT_specification : .......... DW_FORM_ref_addr * - *
  • DW_AT_location: ................ DW_loc_list n.b. only for + *
  • DW_AT_location: ................ DW_FORM_sec_offset n.b. only for * method_local_location2 * * @@ -740,8 +740,6 @@ public void createContent() { * *
  • Dw_AT_name : ....... DW_FORM_strp * - *
  • DW_AT_location : ... DW_FORM_expr_loc - * * * * A second level 1 DIE provides an indirect_layout that wraps the interface layout as its @@ -1534,7 +1532,7 @@ private int writeParameterLocationAbbrev(@SuppressWarnings("unused") DebugContex pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location2) { pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_sec_offset, buffer, pos); } /* * Now terminate. @@ -1553,7 +1551,7 @@ private int writeLocalLocationAbbrev(@SuppressWarnings("unused") DebugContext co pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2) { pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_sec_offset, buffer, pos); } /* * Now terminate. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index 550402ea9600..5df996e44547 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -33,6 +33,7 @@ import com.oracle.objectfile.debugentry.DebugInfoBase; import com.oracle.objectfile.debugentry.MethodEntry; +import com.oracle.objectfile.debugentry.Range; import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; @@ -55,6 +56,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final String DW_FRAME_SECTION_NAME = ".debug_frame"; public static final String DW_ABBREV_SECTION_NAME = ".debug_abbrev"; public static final String DW_INFO_SECTION_NAME = ".debug_info"; + public static final String DW_LOC_SECTION_NAME = ".debug_loc"; public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; /** @@ -277,7 +279,10 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte DW_OP_bra = 0x28; public static final byte DW_OP_eq = 0x29; public static final byte DW_OP_lit0 = 0x30; + public static final byte DW_OP_reg0 = 0x50; public static final byte DW_OP_breg0 = 0x70; + public static final byte DW_OP_regx = (byte) 0x90; + public static final byte DW_OP_bregx = (byte) 0x92; public static final byte DW_OP_push_object_address = (byte) 0x97; /* Register constants for AArch64. */ @@ -301,6 +306,7 @@ public class DwarfDebugInfo extends DebugInfoBase { private DwarfStrSectionImpl dwarfStrSection; private DwarfAbbrevSectionImpl dwarfAbbrevSection; private DwarfInfoSectionImpl dwarfInfoSection; + private DwarfLocSectionImpl dwarfLocSection; private DwarfARangesSectionImpl dwarfARangesSection; private DwarfLineSectionImpl dwarfLineSection; private DwarfFrameSectionImpl dwarfFameSection; @@ -327,8 +333,10 @@ public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { dwarfStrSection = new DwarfStrSectionImpl(this); dwarfAbbrevSection = new DwarfAbbrevSectionImpl(this); dwarfInfoSection = new DwarfInfoSectionImpl(this); + dwarfLocSection = new DwarfLocSectionImpl(this); dwarfARangesSection = new DwarfARangesSectionImpl(this); dwarfLineSection = new DwarfLineSectionImpl(this); + if (elfMachine == ELFMachine.AArch64) { dwarfFameSection = new DwarfFrameSectionImplAArch64(this); this.heapbaseRegister = rheapbase_aarch64; @@ -339,7 +347,8 @@ public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { this.threadRegister = rthread_x86; } propertiesIndex = new HashMap<>(); - methodPropertiesIndex = new HashMap<>(); + methodLocalPropertiesIndex = new HashMap<>(); + rangeLocalPropertiesIndex = new HashMap<>(); } public DwarfStrSectionImpl getStrSectionImpl() { @@ -358,6 +367,10 @@ public DwarfInfoSectionImpl getInfoSectionImpl() { return dwarfInfoSection; } + public DwarfLocSectionImpl getLocSectionImpl() { + return dwarfLocSection; + } + public DwarfARangesSectionImpl getARangesSectionImpl() { return dwarfARangesSection; } @@ -765,92 +778,75 @@ public int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodName } /** - * A class used to associate properties with a specific primary or abstract inline method. This - * includes the index of each parameter or local declaration for the method in the info section, - * either for a top level or an inline version of the parameter/local. It also includes the - * index of each such parameter or local's location list in the loc section. Note, however, that - * in some cases a parameter or local may not have a corresponding location list. + * A class used to associate properties with a specific param or local whether top level or + * inline. */ - static final class DwarfMethodProperties { + static final class DwarfLocalProperties { private HashMap locals; - private HashMap inlineLocals; - private HashMap locations; - private HashMap inlineLocations; - private DwarfMethodProperties() { + private DwarfLocalProperties() { locals = new HashMap<>(); - inlineLocals = new HashMap<>(); - locations = new HashMap<>(); - inlineLocations = new HashMap<>(); } - int getIndex(DebugLocalInfo localInfo, boolean isInline) { - HashMap localsIndex = (isInline ? inlineLocals : locals); - return localsIndex.get(localInfo); + int getIndex(DebugLocalInfo localInfo) { + return locals.get(localInfo); } - void setIndex(DebugLocalInfo localInfo, boolean isInline, int index) { - HashMap localsIndex = (isInline ? inlineLocals : locals); - if (localsIndex.get(localInfo) != null) { - assert localsIndex.get(localInfo) == index; + void setIndex(DebugLocalInfo localInfo, int index) { + if (locals.get(localInfo) != null) { + assert locals.get(localInfo) == index; } else { - localsIndex.put(localInfo, index); + locals.put(localInfo, index); } } + } - int getLocationIndex(DebugLocalInfo localInfo, boolean isInline) { - HashMap locationsIndex = (isInline ? inlineLocations : locations); - return locationsIndex.get(localInfo); - } + private HashMap methodLocalPropertiesIndex; - void setLocationIndex(DebugLocalInfo localInfo, boolean isInline, int index) { - HashMap locationsIndex = (isInline ? inlineLocations : locations); - if (locationsIndex.get(localInfo) != null) { - assert locationsIndex.get(localInfo) == index; - } else { - locationsIndex.put(localInfo, index); - } - } - } + private HashMap rangeLocalPropertiesIndex; - private HashMap methodPropertiesIndex; + private DwarfLocalProperties addMethodLocalProperties(MethodEntry methodEntry) { + DwarfLocalProperties localProperties = new DwarfLocalProperties(); + methodLocalPropertiesIndex.put(methodEntry, localProperties); + return localProperties; + } - private DwarfMethodProperties addMethodProperties(MethodEntry methodEntry) { - DwarfMethodProperties methodProperties = new DwarfMethodProperties(); - methodPropertiesIndex.put(methodEntry, methodProperties); - return methodProperties; + private DwarfLocalProperties addRangeLocalProperties(Range range) { + DwarfLocalProperties localProperties = new DwarfLocalProperties(); + rangeLocalPropertiesIndex.put(range, localProperties); + return localProperties; } - public void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, boolean isInline, int index) { - DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + public void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { + DwarfLocalProperties methodProperties = methodLocalPropertiesIndex.get(methodEntry); if (methodProperties == null) { - methodProperties = addMethodProperties(methodEntry); + methodProperties = addMethodLocalProperties(methodEntry); } - methodProperties.setIndex(localInfo, isInline, index); + methodProperties.setIndex(localInfo, index); } - public int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localinfo, boolean isInline) { - DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); + public int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localinfo) { + DwarfLocalProperties methodProperties = methodLocalPropertiesIndex.get(methodEntry); assert methodProperties != null : "get of non-existent local index"; - int index = methodProperties.getIndex(localinfo, isInline); - assert index > 0 : "get of local index before it was set"; + int index = methodProperties.getIndex(localinfo); + assert index >= 0 : "get of local index before it was set"; return index; } - public void setMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, boolean isInline, int index) { - DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); - if (methodProperties == null) { - methodProperties = addMethodProperties(methodEntry); + public void setRangeLocalIndex(Range range, DebugLocalInfo localInfo, int index) { + DwarfLocalProperties rangeProperties = rangeLocalPropertiesIndex.get(range); + if (rangeProperties == null) { + rangeProperties = addRangeLocalProperties(range); } - methodProperties.setLocationIndex(localInfo, isInline, index); + rangeProperties.setIndex(localInfo, index); } - public int getMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo localinfo, boolean isInline) { - DwarfMethodProperties methodProperties = methodPropertiesIndex.get(methodEntry); - assert methodProperties != null : "get of non-existent local index"; - int index = methodProperties.getLocationIndex(localinfo, isInline); - assert index > 0 : "get of local index before it was set"; + public int getRangeLocalIndex(Range range, DebugLocalInfo localinfo) { + DwarfLocalProperties rangeProperties = rangeLocalPropertiesIndex.get(range); + assert rangeProperties != null : "get of non-existent local index"; + int index = rangeProperties.getIndex(localinfo); + assert index >= 0 : "get of local index before it was set"; return index; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 77844d0e3081..706e3c8c9901 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -31,15 +31,10 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.Set; import org.graalvm.compiler.debug.DebugContext; -import com.oracle.objectfile.BuildDependency; import com.oracle.objectfile.LayoutDecision; -import com.oracle.objectfile.LayoutDecisionMap; -import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ArrayTypeEntry; import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.FieldEntry; @@ -55,7 +50,6 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo; -import com.oracle.objectfile.elf.ELFObjectFile; /** * Section generator for debug_info section. @@ -80,21 +74,6 @@ public String getSectionName() { return DwarfDebugInfo.DW_INFO_SECTION_NAME; } - @Override - public Set getDependencies(Map decisions) { - Set deps = super.getDependencies(decisions); - LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); - /* - * Order all content decisions after all size decisions by making info section content - * depend on abbrev section size. - */ - String abbrevSectionName = dwarfSections.getAbbrevSectionImpl().getSectionName(); - ELFObjectFile.ELFSection abbrevSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(abbrevSectionName); - LayoutDecision sizeDecision = decisions.get(abbrevSection).getDecision(LayoutDecision.Kind.SIZE); - deps.add(BuildDependency.createOrGet(ourContent, sizeDecision)); - return deps; - } - @Override public void createContent() { assert !contentByteArrayCreated(); @@ -729,28 +708,28 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, writeAttrRefAddr(pos, buffer, objectPointerIndex); } /* Write method parameter declarations. */ - pos = writeMethodParameterDeclarations(context, method, fileIdx, 3, false, buffer, pos); + pos = writeMethodParameterDeclarations(context, method, fileIdx, 3, buffer, pos); /* write method local declarations */ - pos = writeMethodLocalDeclarations(context, method, fileIdx, 3, false, buffer, pos); + pos = writeMethodLocalDeclarations(context, method, fileIdx, 3, buffer, pos); /* * Write a terminating null attribute. */ return writeAttrNull(buffer, pos); } - private int writeMethodParameterDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, boolean isInline, byte[] buffer, int p) { + private int writeMethodParameterDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { int pos = p; int refAddr; if (!Modifier.isStatic(method.getModifiers())) { refAddr = pos; DebugLocalInfo paramInfo = method.getThisParam(); - setMethodLocalIndex(method, paramInfo, isInline, refAddr); + setMethodLocalIndex(method, paramInfo, refAddr); pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, true, level, buffer, pos); } for (int i = 0; i < method.getParamCount(); i++) { refAddr = pos; DebugLocalInfo paramInfo = method.getParam(i); - setMethodLocalIndex(method, paramInfo, isInline, refAddr); + setMethodLocalIndex(method, paramInfo, refAddr); pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, false, level, buffer, pos); } return pos; @@ -793,13 +772,13 @@ private int writeMethodParameterDeclaration(DebugContext context, DebugLocalInfo return pos; } - private int writeMethodLocalDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, boolean isInline, byte[] buffer, int p) { + private int writeMethodLocalDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { int pos = p; int refAddr; for (int i = 0; i < method.getLocalCount(); i++) { refAddr = pos; DebugLocalInfo localInfo = method.getLocal(i); - setMethodLocalIndex(method, localInfo, isInline, refAddr); + setMethodLocalIndex(method, localInfo, refAddr); pos = writeMethodLocalDeclaration(context, localInfo, fileIdx, level, buffer, pos); } return pos; @@ -1043,8 +1022,8 @@ private int generateConcreteInlinedMethods(DebugContext context, PrimaryEntry pr HashMap> varRangeMap = subrange.getVarRangeMap(); // increment depth to account for parameter and method locations depth++; - pos = writeMethodParameterLocations(context, varRangeMap, subrange, depth + 2, true, buffer, pos); - pos = writeMethodLocalLocations(context, varRangeMap, subrange, depth + 2, true, buffer, pos); + pos = writeMethodParameterLocations(context, varRangeMap, subrange, depth + 2, buffer, pos); + pos = writeMethodLocalLocations(context, varRangeMap, subrange, depth + 2, buffer, pos); } // if we just stepped out of a child range write nulls for each step up while (depth > 0) { @@ -1366,8 +1345,8 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); pos = writeAttrRefAddr(methodSpecOffset, buffer, pos); HashMap> varRangeMap = primary.getVarRangeMap(); - pos = writeMethodParameterLocations(context, varRangeMap, primary, 2, false, buffer, pos); - pos = writeMethodLocalLocations(context, varRangeMap, primary, 2, false, buffer, pos); + pos = writeMethodParameterLocations(context, varRangeMap, primary, 2, buffer, pos); + pos = writeMethodLocalLocations(context, varRangeMap, primary, 2, buffer, pos); if (primary.includesInlineRanges()) { /* * the method has inlined ranges so write concrete inlined method entries as its @@ -1381,7 +1360,7 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri return writeAttrNull(buffer, pos); } - private int writeMethodParameterLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, boolean isInline, byte[] buffer, int p) { + private int writeMethodParameterLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { int pos = p; MethodEntry methodEntry; if (range.isPrimary()) { @@ -1392,22 +1371,20 @@ private int writeMethodParameterLocations(DebugContext context, HashMap ranges = varRangeMap.get(thisParamInfo); - pos = writeMethodLocalLocation(context, refAddr, ranges, thisParamInfo, depth, true, buffer, pos); + pos = writeMethodLocalLocation(context, range, thisParamInfo, refAddr, ranges, depth, true, buffer, pos); } for (int i = 0; i < methodEntry.getParamCount(); i++) { DebugLocalInfo paramInfo = methodEntry.getParam(i); - int refAddr = getMethodLocalIndex(methodEntry, paramInfo, isInline); - // int locRefAddr = getMethodLocalIndex(methodEntry, paramInfo, isInline); + int refAddr = getMethodLocalIndex(methodEntry, paramInfo); List ranges = varRangeMap.get(paramInfo); - pos = writeMethodLocalLocation(context, refAddr, ranges, paramInfo, depth, true, buffer, pos); + pos = writeMethodLocalLocation(context, range, paramInfo, refAddr, ranges, depth, true, buffer, pos); } return pos; } - private int writeMethodLocalLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, boolean isInline, byte[] buffer, int p) { + private int writeMethodLocalLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { int pos = p; MethodEntry methodEntry; if (range.isPrimary()) { @@ -1419,24 +1396,22 @@ private int writeMethodLocalLocations(DebugContext context, HashMap ranges = varRangeMap.get(localInfo); - pos = writeMethodLocalLocation(context, refAddr, ranges, localInfo, depth, false, buffer, pos); + pos = writeMethodLocalLocation(context, range, localInfo, refAddr, ranges, depth, false, buffer, pos); } return pos; } - @SuppressWarnings("unused") - private int writeMethodLocalLocation(DebugContext context, int refAddr, List ranges, DebugLocalInfo localInfo, int depth, boolean isParam, byte[] buffer, int p) { + private int writeMethodLocalLocation(DebugContext context, Range range, DebugLocalInfo localInfo, int refAddr, List ranges, int depth, boolean isParam, byte[] buffer, + int p) { int pos = p; log(context, " [0x%08x] method %s location %s:%s", pos, (isParam ? "parameter" : "local"), localInfo.name(), localInfo.typeName()); List localValues = new ArrayList<>(); - /*- - for (Range range : ranges) { - DebugLocalValueInfo value = range.lookupValue(localInfo); + for (Range subrange : ranges) { + DebugLocalValueInfo value = subrange.lookupValue(localInfo); if (value != null) { - log(context, " [0x%08x] local %s:%s [0x%x, 0x%x] = %s", pos, value.name(), value.typeName(), range.getLo(), range.getHi(), formatValue(value)); + log(context, " [0x%08x] local %s:%s [0x%x, 0x%x] = %s", pos, value.name(), value.typeName(), subrange.getLo(), subrange.getHi(), formatValue(value)); switch (value.localKind()) { case REGISTER: case STACKSLOT: @@ -1448,43 +1423,24 @@ private int writeMethodLocalLocation(DebugContext context, int refAddr, List Abbrev Number %d", pos, depth, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] specification 0x%x", pos, refAddr); pos = writeAttrRefAddr(refAddr, buffer, pos); - /*- - if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2) { - pos = writeAttrRefAddr(locRefAddr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2 || + abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_location2) { + int locRefAddr = getRangeLocalIndex(range, localInfo); + pos = writeAttrLocList(locRefAddr, buffer, pos); } - */ return pos; } - @SuppressWarnings("unused") - private static String formatValue(DebugLocalValueInfo value) { - switch (value.localKind()) { - case REGISTER: - return "REG:" + value.regIndex(); - case STACKSLOT: - return "STACK:" + value.stackSlot(); - case CONSTANT: - return "CONST:" + value.constantValue(); - case UNDEFINED: - default: - return "-"; - } - } - private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) { int pos = p; String methodKey = method.getSymbolName(); @@ -1501,15 +1457,7 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); int methodSpecOffset = getMethodDeclarationIndex(classEntry, methodKey); log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); - FileEntry fileEntry = method.getFileEntry(); - if (fileEntry == null) { - fileEntry = classEntry.getFileEntry(); - } - assert fileEntry != null; - int fileIdx = classEntry.localFilesIdx(fileEntry); pos = writeAttrRefAddr(methodSpecOffset, buffer, pos); - pos = writeMethodParameterDeclarations(context, method, fileIdx, 2, true, buffer, pos); - pos = writeMethodLocalDeclarations(context, method, fileIdx, 2, true, buffer, pos); /* * Write a terminating null attribute. */ @@ -1812,9 +1760,9 @@ public int writeIndirectOopConversionExpression(boolean isHub, byte[] buffer, in } /** - * The debug_info section depends on abbrev section. + * The debug_info section depends on loc section. */ - protected static final String TARGET_SECTION_NAME = DwarfDebugInfo.TEXT_SECTION_NAME; + protected static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_LOC_SECTION_NAME; @Override public String targetSectionName() { @@ -1823,9 +1771,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.SIZE, - /* Add this so we can use the text section base address for debug. */ - LayoutDecision.Kind.VADDR + LayoutDecision.Kind.SIZE }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index c7792db05541..8de854641fe1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -34,7 +34,6 @@ import com.oracle.objectfile.debugentry.FileEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.Range; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import org.graalvm.compiler.debug.DebugContext; import java.util.Iterator; @@ -47,7 +46,7 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { /** * Line header section always contains fixed number of bytes. */ - private static final int DW_LN_HEADER_SIZE = 27; + private static final int DW_LN_HEADER_SIZE = 28; /** * Current generator follows C++ with line base -5. */ @@ -57,7 +56,7 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final int DW_LN_LINE_RANGE = 14; /** - * Current generator uses opcode base of 13 which must equal DW_LNS_define_file + 1. + * Current generator uses opcode base of 13 which must equal DW_LNS_set_isa + 1. */ private static final int DW_LN_OPCODE_BASE = 13; @@ -109,6 +108,16 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final byte DW_LNS_fixed_advance_pc = 9; + /* + * Increment address 1 ushort arg. + */ + @SuppressWarnings("unused") private static final byte DW_LNS_set_prologue_end = 10; + + /* + * Increment address 1 ushort arg. + */ + @SuppressWarnings("unused") private static final byte DW_LNS_set_epilogue_begin = 11; + /* * Extended opcodes defined by DWARF 2. */ @@ -180,10 +189,12 @@ private static int headerSize() { * *
  • uint16 version * - *
  • uint32 prologue_length + *
  • uint32 header_length * *
  • uint8 min_insn_length * + *
  • uint8 max_operations_per_instruction + * *
  • uint8 default_is_stmt * *
  • int8 line_base @@ -192,8 +203,6 @@ private static int headerSize() { * *
  • uint8 opcode_base * - *
  • uint8 li_opcode_base - * *
  • uint8[opcode_base-1] standard_opcode_lengths * * @@ -318,7 +327,7 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { /* * 2 ubyte version is always 2. */ - pos = putShort(DwarfDebugInfo.DW_VERSION_2, buffer, pos); + pos = putShort(DwarfDebugInfo.DW_VERSION_4, buffer, pos); /* * 4 ubyte prologue length includes rest of header and dir + file table section. */ @@ -328,6 +337,10 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { * 1 ubyte min instruction length is always 1. */ pos = putByte((byte) 1, buffer, pos); + /* + * 1 ubyte max operations per instruction is always 1. + */ + pos = putByte((byte) 1, buffer, pos); /* * 1 byte default is_stmt is always 1. */ @@ -365,11 +378,11 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { putByte((byte) 0, buffer, pos + 7); /* DW_LNS_fixed_advance_pc */ putByte((byte) 1, buffer, pos + 8); - /* DW_LNS_end_sequence */ + /* DW_LNS_set_prologue_end */ putByte((byte) 0, buffer, pos + 9); - /* DW_LNS_set_address */ + /* DW_LNS_set_epilogue_begin */ putByte((byte) 0, buffer, pos + 10); - /* DW_LNS_define_file */ + /* DW_LNS_set_isa */ pos = putByte((byte) 1, buffer, pos + 11); return pos; } @@ -439,6 +452,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by log(context, " [0x%08x] primary class %s", pos, primaryClassName); log(context, " [0x%08x] primary class file %s", pos, primaryFileName); for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + Range primaryRange = primaryEntry.getPrimary(); // the primary method might be a substitution and not in the primary class file FileEntry fileEntry = primaryRange.getFileEntry(); @@ -450,32 +464,29 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by String file = fileEntry.getFileName(); int fileIdx = classEntry.localFilesIdx(fileEntry); /* - * Each primary represents a method i.e. a contiguous sequence of subranges. we write - * the default state at the start of each sequence because we always post an - * end_sequence when we finish all the subranges in the method. + * Each primary represents a method i.e. a contiguous sequence of subranges. For normal + * methods we expect the first leaf range to start at offset 0 covering the method + * prologue. In that case we can rely on it to set the initial file, line and address + * for the state machine. Otherwise we need to default the initial state and copy it to + * the file. */ long line = primaryRange.getLine(); - if (line < 0) { - Iterator iterator = primaryEntry.leafRangeIterator(); - if (iterator.hasNext()) { - final Range subRange = iterator.next(); - line = subRange.getLine(); - /* - * If line gets successfully retrieved from subrange get file index from there - * since the line might be from a different file for inlined methods - */ - if (line > 0) { - FileEntry subFileEntry = subRange.getFileEntry(); - if (subFileEntry != null) { - fileIdx = classEntry.localFilesIdx(subFileEntry); - } + long address = primaryRange.getLo(); + Range prologueRange = prologueLeafRange(primaryEntry); + if (prologueRange != null) { + // use the line for the range and use its file if available + line = prologueRange.getLine(); + if (line > 0) { + FileEntry firstFileEntry = prologueRange.getFileEntry(); + if (firstFileEntry != null) { + fileIdx = classEntry.localFilesIdx(firstFileEntry); } } } if (line < 0) { + // never emit a negative line line = 0; } - long address = primaryRange.getLo(); /* * Set state for primary. @@ -490,7 +501,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by pos = writeSetFileOp(context, file, fileIdx, buffer, pos); pos = writeSetBasicBlockOp(context, buffer, pos); /* - * Address is currently 0. + * Address is currently at offset 0. */ pos = writeSetAddressOp(context, address, buffer, pos); /* @@ -501,7 +512,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by } pos = writeCopyOp(context, buffer, pos); - /* + /*- * On AArch64 gdb expects to see a line record at the start of the method and a second * one at the end of the prologue marking the point where the method code begins for * real. If we don't provide it then gdb will skip to the second line record when we @@ -511,15 +522,20 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by * frame height is first adjusted. This should normally be no more a few instructions in * total. */ + /*- + * Disabled modulo further testing -- as we are now adding DWARF4 prologue start and end markers if (isAArch64() && !primaryEntry.getFrameSizeInfos().isEmpty()) { DebugFrameSizeChange frameSizeChange = primaryEntry.getFrameSizeInfos().get(0); assert frameSizeChange.getType() == DebugFrameSizeChange.Type.EXTEND; long addressDelta = frameSizeChange.getOffset(); if (addressDelta < 16 && (primaryRange.getLo() + addressDelta) < primaryRange.getHi()) { - /* - * we should be able to write this with a special opcode as the prologue should - * only be a few instructions - */ + + */ + /*- + * we should be able to write this with a special opcode as the prologue should + * only be a few instructions + */ + /*- byte opcode = isSpecialOpcode(addressDelta, 0); assert opcode != DW_LNS_undefined; pos = writeSpecialOpcode(context, opcode, buffer, pos); @@ -527,10 +543,16 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by address += addressDelta; } } + */ /* * Now write a row for each subrange lo and hi. */ Iterator iterator = primaryEntry.leafRangeIterator(); + if (prologueRange != null) { + // skip already processed range + Range first = iterator.next(); + assert first == prologueRange; + } while (iterator.hasNext()) { Range subrange = iterator.next(); assert subrange.getLo() >= primaryRange.getLo(); @@ -573,11 +595,11 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by file = subfile; fileIdx = subFileIdx; } + long lineDelta = subLine - line; + long addressDelta = subAddressLo - address; /* * Check if we can advance line and/or address in one byte with a special opcode. */ - long lineDelta = subLine - line; - long addressDelta = subAddressLo - address; byte opcode = isSpecialOpcode(addressDelta, lineDelta); if (opcode != DW_LNS_undefined) { /* @@ -660,6 +682,17 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by return pos; } + private static Range prologueLeafRange(PrimaryEntry primaryEntry) { + Iterator iterator = primaryEntry.leafRangeIterator(); + if (iterator.hasNext()) { + Range range = iterator.next(); + if (range.getLo() == primaryEntry.getPrimary().getLo()) { + return range; + } + } + return null; + } + private int writeCopyOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNS_copy; int pos = p; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java new file mode 100644 index 000000000000..469e5cda9014 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Red Hat Inc. 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.objectfile.elf.dwarf; + +import com.oracle.objectfile.BuildDependency; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; +import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalValueInfo; +import com.oracle.objectfile.elf.ELFMachine; +import com.oracle.objectfile.elf.ELFObjectFile; +import org.graalvm.compiler.debug.DebugContext; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Section generator for debug_loc section. + */ +public class DwarfLocSectionImpl extends DwarfSectionImpl { + + /* + * array used to map compiler register indices to the indices expected by DWARF + */ + private int[] dwarfRegMap; + + /* + * index used by DWARF for the stack pointer register + */ + private int dwarfStackRegister; + + public DwarfLocSectionImpl(DwarfDebugInfo dwarfSections) { + super(dwarfSections); + initDwarfRegMap(); + } + + @Override + public String getSectionName() { + return DwarfDebugInfo.DW_LOC_SECTION_NAME; + } + + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + /* + * Order all content decisions after all size decisions by making loc section content depend + * on abbrev section size. + */ + String abbrevSectionName = dwarfSections.getAbbrevSectionImpl().getSectionName(); + ELFObjectFile.ELFSection abbrevSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(abbrevSectionName); + LayoutDecision sizeDecision = decisions.get(abbrevSection).getDecision(LayoutDecision.Kind.SIZE); + deps.add(BuildDependency.createOrGet(ourContent, sizeDecision)); + return deps; + } + + @Override + public void createContent() { + assert !contentByteArrayCreated(); + + byte[] buffer = null; + int len = generateContent(null, buffer); + + buffer = new byte[len]; + super.setContent(buffer); + } + + @Override + public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + enableLog(context, pos); + log(context, " [0x%08x] DEBUG_LOC", pos); + log(context, " [0x%08x] size = 0x%08x", pos, size); + + pos = generateContent(context, buffer); + assert pos == size; + } + + private int generateContent(DebugContext context, byte[] buffer) { + int pos = 0; + + pos = writePrimaryClassLocations(context, buffer, pos); + pos = writeDeoptClassLocations(context, buffer, pos); + + return pos; + } + + private int writePrimaryClassLocations(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] primary class locations", pos); + return getTypes().filter(TypeEntry::isClass).reduce(pos, + (p, typeEntry) -> { + ClassEntry classEntry = (ClassEntry) typeEntry; + return (classEntry.isPrimary() ? writeMethodLocations(context, classEntry, false, buffer, p) : p); + }, + (oldpos, newpos) -> newpos); + } + + private int writeDeoptClassLocations(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] deopt class locations", pos); + return getTypes().filter(TypeEntry::isClass).reduce(pos, + (p, typeEntry) -> { + ClassEntry classEntry = (ClassEntry) typeEntry; + return (classEntry.isPrimary() && classEntry.includesDeoptTarget() ? writeMethodLocations(context, classEntry, true, buffer, p) : p); + }, + (oldpos, newpos) -> newpos); + } + + private int writeMethodLocations(DebugContext context, ClassEntry classEntry, boolean isDeopt, byte[] buffer, int p) { + int pos = p; + List classPrimaryEntries = classEntry.getPrimaryEntries(); + + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + if (primaryEntry.getPrimary().isDeoptTarget() != isDeopt) { + continue; + } + pos = writePrimaryRangeLocations(context, primaryEntry, buffer, pos); + } + return pos; + } + + private int writePrimaryRangeLocations(DebugContext context, PrimaryEntry primaryEntry, byte[] buffer, int p) { + int pos = p; + pos = writeTopLevelLocations(context, primaryEntry, buffer, pos); + pos = writeInlineLocations(context, primaryEntry, buffer, pos); + return pos; + } + + private int writeTopLevelLocations(DebugContext context, PrimaryEntry primaryEntry, byte[] buffer, int p) { + int pos = p; + Range primary = primaryEntry.getPrimary(); + long base = primary.getLo(); + log(context, " [0x%08x] top level locations [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodNameWithParams()); + HashMap> varRangeMap = primary.getVarRangeMap(); + for (DebugLocalInfo local : varRangeMap.keySet()) { + List rangeList = varRangeMap.get(local); + if (!rangeList.isEmpty()) { + setRangeLocalIndex(primary, local, pos); + pos = writeVarLocations(context, local, base, rangeList, buffer, pos); + } + } + return pos; + } + + private int writeInlineLocations(DebugContext context, PrimaryEntry primaryEntry, byte[] buffer, int p) { + int pos = p; + Range primary = primaryEntry.getPrimary(); + if (primary.isLeaf()) { + return p; + } + long base = primary.getLo(); + log(context, " [0x%08x] inline locations [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodNameWithParams()); + Iterator iterator = primaryEntry.topDownRangeIterator(); + while (iterator.hasNext()) { + Range subrange = iterator.next(); + if (subrange.isLeaf()) { + continue; + } + HashMap> varRangeMap = subrange.getVarRangeMap(); + for (DebugLocalInfo local : varRangeMap.keySet()) { + List rangeList = varRangeMap.get(local); + if (!rangeList.isEmpty()) { + setRangeLocalIndex(subrange, local, pos); + pos = writeVarLocations(context, local, base, rangeList, buffer, pos); + } + } + } + return pos; + } + + private int writeVarLocations(DebugContext context, DebugLocalInfo local, long base, List rangeList, byte[] buffer, int p) { + assert !rangeList.isEmpty(); + int pos = p; + // collect ranges and values, merging adjacent ranges that have equal value + List extents = LocalValueExtent.coalesce(local, rangeList); + + // TODO - currently we use the start address of the range's primary method as + // a base from which to write location offsets. This means we need to explicitly + // write a (relocatable) base address as a prefix to each location list. If instead + // we rebased relative to the start of the enclosing compilation unit then we + // could omit writing the base adress prefix, saving two long words per location list. + + // write the base address + pos = writeAttrData8(-1L, buffer, pos); + pos = writeAttrAddress(base, buffer, pos); + // now write ranges as offsets from base + for (LocalValueExtent extent : extents) { + DebugLocalValueInfo value = extent.value; + assert (value != null); + log(context, " [0x%08x] local %s:%s [0x%x, 0x%x] = %s", pos, value.name(), value.typeName(), extent.getLo(), extent.getHi(), formatValue(value)); + pos = writeAttrData8(extent.getLo() - base, buffer, pos); + pos = writeAttrData8(extent.getHi() - base, buffer, pos); + switch (value.localKind()) { + case REGISTER: + pos = writeRegisterLocation(context, value.regIndex(), buffer, pos); + break; + case STACKSLOT: + pos = writeStackLocation(context, value.stackSlot(), buffer, pos); + break; + case CONSTANT: + default: + assert false : "Should not reach here!"; + break; + } + } + // write list terminator + pos = writeAttrData8(0, buffer, pos); + pos = writeAttrData8(0, buffer, pos); + + return pos; + } + + private int writeRegisterLocation(DebugContext context, int regIndex, byte[] buffer, int p) { + int targetIdx = mapToDwarfReg(regIndex); + int pos = p; + if (targetIdx < 32) { + // can write using DW_OP_reg + short byteCount = 1; + byte regOp = (byte) (DwarfDebugInfo.DW_OP_reg0 + targetIdx); + if (buffer == null) { + pos += putShort(byteCount, scratch, 0); + pos += putByte(regOp, scratch, 0); + } else { + pos = putShort(byteCount, buffer, pos); + pos = putByte(regOp, buffer, pos); + verboseLog(context, " [0x%08x] REGOP count %d op 0x%x", pos, byteCount, regOp); + } + } else { + // have to write using DW_OP_regx + LEB operand + assert targetIdx < 128 : "unexpectedly high reg index!"; + short byteCount = 2; + byte regOp = DwarfDebugInfo.DW_OP_regx; + if (buffer == null) { + pos += putShort(byteCount, scratch, 0); + pos += putByte(regOp, scratch, 0); + pos += putULEB(targetIdx, scratch, 0); + } else { + pos = putShort(byteCount, buffer, pos); + pos = putByte(regOp, buffer, pos); + pos = putULEB(targetIdx, buffer, pos); + verboseLog(context, " [0x%08x] REGOP count %d op 0x%x reg %d", pos, byteCount, regOp, targetIdx); + } + // target idx written as ULEB should fit in one byte + assert pos == p + 4 : "wrote the wrong number of bytes!"; + } + return pos; + } + + private int writeStackLocation(DebugContext context, int offset, byte[] buffer, int p) { + int pos = p; + short byteCount = 0; + int sp = getDwarfStackRegister(); + byte stackOp; + if (sp < 32) { + // fold the base reg index into the op + stackOp = DwarfDebugInfo.DW_OP_breg0; + stackOp += sp; + } else { + // pass base reg index as a ULEB operand + stackOp = DwarfDebugInfo.DW_OP_bregx; + } + if (buffer == null) { + pos += putShort(byteCount, scratch, 0); + pos += putByte(stackOp, scratch, 0); + if (stackOp == DwarfDebugInfo.DW_OP_bregx) { + // need to pass base reg index as a ULEB operand + pos += putULEB(sp, scratch, 0); + } + pos += putSLEB(0L - offset, scratch, 0); + } else { + int patchPos = pos; + pos = putShort(byteCount, buffer, pos); + int zeroPos = pos; + pos = putByte(stackOp, buffer, pos); + if (stackOp == DwarfDebugInfo.DW_OP_bregx) { + // need to pass base reg index as a ULEB operand + pos = putULEB(sp, buffer, pos); + } + pos = putSLEB(0L - offset, buffer, pos); + // now backpatch the byte count + byteCount = (byte) (pos - zeroPos); + putShort(byteCount, buffer, patchPos); + if (stackOp == DwarfDebugInfo.DW_OP_bregx) { + verboseLog(context, " [0x%08x] STACKOP count %d op 0x%x offset %d", pos, byteCount, stackOp, 0 - offset); + } else { + verboseLog(context, " [0x%08x] STACKOP count %d op 0x%x reg %d offset %d", pos, byteCount, stackOp, sp, 0 - offset); + } + } + return pos; + } + + // auxiliary class used to collect per-range locations for a given local + // merging adjacent ranges with the same location + static class LocalValueExtent { + long lo; + long hi; + DebugLocalValueInfo value; + + LocalValueExtent(long lo, long hi, DebugLocalValueInfo value) { + this.lo = lo; + this.hi = hi; + this.value = value; + } + + @SuppressWarnings("unused") + boolean shouldMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { + // ranges need to be contiguous to merge + if (hi != otherLo) { + return false; + } + // values need to be for the same line + if (value.line() != otherValue.line()) { + return false; + } + // location kinds must match + if (value.localKind() != otherValue.localKind()) { + return false; + } + // locations must match + switch (value.localKind()) { + case REGISTER: + if (value.regIndex() != otherValue.regIndex()) { + return false; + } + break; + case STACKSLOT: + if (value.stackSlot() != otherValue.stackSlot()) { + return false; + } + break; + case CONSTANT: // should not get here + case UNDEFINED: + assert false : "unexpected local value type"; + break; + } + return true; + } + + LocalValueExtent maybeMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { + if (shouldMerge(otherLo, otherHi, otherValue)) { + // We can extend the current extent to cover the next one. + this.hi = otherHi; + return null; + } else { + // we need a new extent + return new LocalValueExtent(otherLo, otherHi, otherValue); + } + } + + public long getLo() { + return lo; + } + + public long getHi() { + return hi; + } + + public DebugLocalValueInfo getValue() { + return value; + } + + public static List coalesce(DebugLocalInfo local, List rangeList) { + List extents = new ArrayList<>(); + LocalValueExtent current = null; + for (Range range : rangeList) { + if (current == null) { + current = new LocalValueExtent(range.getLo(), range.getHi(), range.lookupValue(local)); + extents.add(current); + } else { + LocalValueExtent toAdd = current.maybeMerge(range.getLo(), range.getHi(), range.lookupValue(local)); + if (toAdd != null) { + extents.add(toAdd); + current = toAdd; + } + } + } + return extents; + } + } + + /** + * The debug_loc section depends on text section. + */ + protected static final String TARGET_SECTION_NAME = DwarfDebugInfo.TEXT_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + private final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.SIZE, + /* Add this so we can use the text section base address for debug. */ + LayoutDecision.Kind.VADDR + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + + private int getDwarfStackRegister() { + return dwarfStackRegister; + } + + private int mapToDwarfReg(int regIdx) { + assert regIdx >= 0 : "negative register index!"; + assert regIdx < dwarfRegMap.length : String.format("register index %d exceeds map range %d", regIdx, dwarfRegMap.length); + return dwarfRegMap[regIdx]; + } + + private void initDwarfRegMap() { + if (dwarfSections.elfMachine == ELFMachine.AArch64) { + dwarfRegMap = AARCH64_TO_DWARF_REG_MAP; + dwarfStackRegister = DWARF_REG_AARCH64_SP; + } else { + assert dwarfSections.elfMachine == ELFMachine.X86_64 : "must be"; + dwarfRegMap = X86_64_TO_DWARF_REG_MAP; + dwarfStackRegister = DWARF_REG_X86_64_RSP; + } + } + + // register numbers used by DWARF for AArch64 registers + private static final int DWARF_REG_AARCH64_R0 = 0; + private static final int DWARF_REG_AARCH64_R1 = 1; + private static final int DWARF_REG_AARCH64_R2 = 2; + private static final int DWARF_REG_AARCH64_R3 = 3; + private static final int DWARF_REG_AARCH64_R4 = 4; + private static final int DWARF_REG_AARCH64_R5 = 5; + private static final int DWARF_REG_AARCH64_R6 = 6; + private static final int DWARF_REG_AARCH64_R7 = 7; + private static final int DWARF_REG_AARCH64_R8 = 8; + private static final int DWARF_REG_AARCH64_R9 = 9; + private static final int DWARF_REG_AARCH64_R10 = 10; + private static final int DWARF_REG_AARCH64_R11 = 11; + private static final int DWARF_REG_AARCH64_R12 = 12; + private static final int DWARF_REG_AARCH64_R13 = 13; + private static final int DWARF_REG_AARCH64_R14 = 14; + private static final int DWARF_REG_AARCH64_R15 = 15; + private static final int DWARF_REG_AARCH64_R16 = 16; + private static final int DWARF_REG_AARCH64_R17 = 17; + private static final int DWARF_REG_AARCH64_R18 = 18; + private static final int DWARF_REG_AARCH64_R19 = 19; + private static final int DWARF_REG_AARCH64_R20 = 20; + private static final int DWARF_REG_AARCH64_R21 = 21; + private static final int DWARF_REG_AARCH64_R22 = 22; + private static final int DWARF_REG_AARCH64_R23 = 23; + private static final int DWARF_REG_AARCH64_R24 = 24; + private static final int DWARF_REG_AARCH64_R25 = 25; + private static final int DWARF_REG_AARCH64_R26 = 26; + private static final int DWARF_REG_AARCH64_R27 = 27; + private static final int DWARF_REG_AARCH64_R28 = 28; + private static final int DWARF_REG_AARCH64_R29 = 29; + private static final int DWARF_REG_AARCH64_R30 = 30; + private static final int DWARF_REG_AARCH64_R31 = 31; + private static final int DWARF_REG_AARCH64_ZR = 96; + private static final int DWARF_REG_AARCH64_SP = 31; + private static final int DWARF_REG_AARCH64_V0 = 64; + private static final int DWARF_REG_AARCH64_V1 = 65; + private static final int DWARF_REG_AARCH64_V2 = 66; + private static final int DWARF_REG_AARCH64_V3 = 67; + private static final int DWARF_REG_AARCH64_V4 = 68; + private static final int DWARF_REG_AARCH64_V5 = 69; + private static final int DWARF_REG_AARCH64_V6 = 70; + private static final int DWARF_REG_AARCH64_V7 = 71; + private static final int DWARF_REG_AARCH64_V8 = 72; + private static final int DWARF_REG_AARCH64_V9 = 73; + private static final int DWARF_REG_AARCH64_V10 = 74; + private static final int DWARF_REG_AARCH64_V11 = 75; + private static final int DWARF_REG_AARCH64_V12 = 76; + private static final int DWARF_REG_AARCH64_V13 = 77; + private static final int DWARF_REG_AARCH64_V14 = 78; + private static final int DWARF_REG_AARCH64_V15 = 79; + private static final int DWARF_REG_AARCH64_V16 = 80; + private static final int DWARF_REG_AARCH64_V17 = 81; + private static final int DWARF_REG_AARCH64_V18 = 82; + private static final int DWARF_REG_AARCH64_V19 = 83; + private static final int DWARF_REG_AARCH64_V20 = 84; + private static final int DWARF_REG_AARCH64_V21 = 85; + private static final int DWARF_REG_AARCH64_V22 = 86; + private static final int DWARF_REG_AARCH64_V23 = 87; + private static final int DWARF_REG_AARCH64_V24 = 88; + private static final int DWARF_REG_AARCH64_V25 = 89; + private static final int DWARF_REG_AARCH64_V26 = 90; + private static final int DWARF_REG_AARCH64_V27 = 91; + private static final int DWARF_REG_AARCH64_V28 = 92; + private static final int DWARF_REG_AARCH64_V29 = 93; + private static final int DWARF_REG_AARCH64_V30 = 94; + private static final int DWARF_REG_AARCH64_V31 = 95; + + // map from compiler register indices to corresponding dwarf register + private static final int[] AARCH64_TO_DWARF_REG_MAP = { + DWARF_REG_AARCH64_R0, // 0 + DWARF_REG_AARCH64_R1, // 1 + DWARF_REG_AARCH64_R2, // ... + DWARF_REG_AARCH64_R3, + DWARF_REG_AARCH64_R4, + DWARF_REG_AARCH64_R5, + DWARF_REG_AARCH64_R6, + DWARF_REG_AARCH64_R7, + DWARF_REG_AARCH64_R8, + DWARF_REG_AARCH64_R9, + DWARF_REG_AARCH64_R10, + DWARF_REG_AARCH64_R11, + DWARF_REG_AARCH64_R12, + DWARF_REG_AARCH64_R13, + DWARF_REG_AARCH64_R14, + DWARF_REG_AARCH64_R15, + DWARF_REG_AARCH64_R16, + DWARF_REG_AARCH64_R17, + DWARF_REG_AARCH64_R18, + DWARF_REG_AARCH64_R19, + DWARF_REG_AARCH64_R20, + DWARF_REG_AARCH64_R21, + DWARF_REG_AARCH64_R22, + DWARF_REG_AARCH64_R23, + DWARF_REG_AARCH64_R24, + DWARF_REG_AARCH64_R25, + DWARF_REG_AARCH64_R26, + DWARF_REG_AARCH64_R27, + DWARF_REG_AARCH64_R28, + DWARF_REG_AARCH64_R29, + DWARF_REG_AARCH64_R30, + DWARF_REG_AARCH64_R31, + DWARF_REG_AARCH64_ZR, // 32 + DWARF_REG_AARCH64_SP, // 33 + DWARF_REG_AARCH64_V0, // 34 + DWARF_REG_AARCH64_V1, // ... + DWARF_REG_AARCH64_V2, + DWARF_REG_AARCH64_V3, + DWARF_REG_AARCH64_V4, + DWARF_REG_AARCH64_V5, + DWARF_REG_AARCH64_V6, + DWARF_REG_AARCH64_V7, + DWARF_REG_AARCH64_V8, + DWARF_REG_AARCH64_V9, + DWARF_REG_AARCH64_V10, + DWARF_REG_AARCH64_V11, + DWARF_REG_AARCH64_V12, + DWARF_REG_AARCH64_V13, + DWARF_REG_AARCH64_V14, + DWARF_REG_AARCH64_V15, + DWARF_REG_AARCH64_V16, + DWARF_REG_AARCH64_V17, + DWARF_REG_AARCH64_V18, + DWARF_REG_AARCH64_V19, + DWARF_REG_AARCH64_V20, + DWARF_REG_AARCH64_V21, + DWARF_REG_AARCH64_V22, + DWARF_REG_AARCH64_V23, + DWARF_REG_AARCH64_V24, + DWARF_REG_AARCH64_V25, + DWARF_REG_AARCH64_V26, + DWARF_REG_AARCH64_V27, + DWARF_REG_AARCH64_V28, + DWARF_REG_AARCH64_V29, + DWARF_REG_AARCH64_V30, + DWARF_REG_AARCH64_V31 // 65 + }; + + // register numbers used by DWARF for X86_64 registers + private static final int DWARF_REG_X86_64_RAX = 0; + private static final int DWARF_REG_X86_64_RBX = 1; + private static final int DWARF_REG_X86_64_RCX = 2; + private static final int DWARF_REG_X86_64_RDX = 3; + private static final int DWARF_REG_X86_64_RSI = 4; + private static final int DWARF_REG_X86_64_RDI = 5; + private static final int DWARF_REG_X86_64_RBP = 6; + private static final int DWARF_REG_X86_64_RSP = 7; + private static final int DWARF_REG_X86_64_R8 = 8; + private static final int DWARF_REG_X86_64_R9 = 9; + private static final int DWARF_REG_X86_64_R10 = 10; + private static final int DWARF_REG_X86_64_R11 = 11; + private static final int DWARF_REG_X86_64_R12 = 12; + private static final int DWARF_REG_X86_64_R13 = 13; + private static final int DWARF_REG_X86_64_R14 = 14; + private static final int DWARF_REG_X86_64_R15 = 15; + private static final int DWARF_REG_X86_64_XMM0 = 40; + @SuppressWarnings("unused") private static final int DWARF_REG_X86_64_XMM15 = 55; + private static final int DWARF_REG_X86_64_XMM16 = 79; + @SuppressWarnings("unused") private static final int DWARF_REG_X86_64_XMM31 = 94; + + private static final int[] X86_64_TO_DWARF_REG_MAP = { + DWARF_REG_X86_64_RAX, + DWARF_REG_X86_64_RCX, + DWARF_REG_X86_64_RDX, + DWARF_REG_X86_64_RBX, + DWARF_REG_X86_64_RSP, + DWARF_REG_X86_64_RBP, + DWARF_REG_X86_64_RSI, + DWARF_REG_X86_64_RDI, + DWARF_REG_X86_64_R8, + DWARF_REG_X86_64_R9, + DWARF_REG_X86_64_R10, + DWARF_REG_X86_64_R11, + DWARF_REG_X86_64_R12, + DWARF_REG_X86_64_R13, + DWARF_REG_X86_64_R14, + DWARF_REG_X86_64_R15, + DWARF_REG_X86_64_XMM0, + DWARF_REG_X86_64_XMM0 + 1, + DWARF_REG_X86_64_XMM0 + 2, + DWARF_REG_X86_64_XMM0 + 3, + DWARF_REG_X86_64_XMM0 + 4, + DWARF_REG_X86_64_XMM0 + 5, + DWARF_REG_X86_64_XMM0 + 6, + DWARF_REG_X86_64_XMM0 + 7, + DWARF_REG_X86_64_XMM0 + 8, + DWARF_REG_X86_64_XMM0 + 9, + DWARF_REG_X86_64_XMM0 + 10, + DWARF_REG_X86_64_XMM0 + 11, + DWARF_REG_X86_64_XMM0 + 12, + DWARF_REG_X86_64_XMM0 + 13, + DWARF_REG_X86_64_XMM0 + 14, + DWARF_REG_X86_64_XMM0 + 15, + DWARF_REG_X86_64_XMM16, + DWARF_REG_X86_64_XMM16 + 1, + DWARF_REG_X86_64_XMM16 + 2, + DWARF_REG_X86_64_XMM16 + 3, + DWARF_REG_X86_64_XMM16 + 4, + DWARF_REG_X86_64_XMM16 + 5, + DWARF_REG_X86_64_XMM16 + 6, + DWARF_REG_X86_64_XMM16 + 7, + DWARF_REG_X86_64_XMM16 + 8, + DWARF_REG_X86_64_XMM16 + 9, + DWARF_REG_X86_64_XMM16 + 10, + DWARF_REG_X86_64_XMM16 + 11, + DWARF_REG_X86_64_XMM16 + 12, + DWARF_REG_X86_64_XMM16 + 13, + DWARF_REG_X86_64_XMM16 + 14, + DWARF_REG_X86_64_XMM16 + 15, + }; +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 9c1e5d7f46cd..504a19ad6edf 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -33,8 +33,10 @@ import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.MethodEntry; +import com.oracle.objectfile.debugentry.Range; import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLocalInfo; import com.oracle.objectfile.elf.ELFMachine; import com.oracle.objectfile.elf.ELFObjectFile; @@ -213,6 +215,16 @@ protected int putRelocatableCodeOffset(long l, byte[] buffer, int p) { return pos; } + protected int putRelocatableInfoOffset(long l, byte[] buffer, int p) { + int pos = p; + /* + * Mark address so it is relocated relative to the start of the text segment. + */ + markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, DwarfDebugInfo.DW_INFO_SECTION_NAME, l); + pos = putLong(0, buffer, pos); + return pos; + } + protected int putRelocatableHeapOffset(long l, byte[] buffer, int p) { int pos = p; /* @@ -325,6 +337,14 @@ protected int writeAttrAddress(long address, byte[] buffer, int pos) { } } + protected int writeAttrLocList(int offset, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putInt(offset, scratch, 0); + } else { + return putInt(offset, buffer, pos); + } + } + @SuppressWarnings("unused") protected int writeAttrData8(long value, byte[] buffer, int pos) { if (buffer == null) { @@ -379,6 +399,20 @@ protected int writeAttrNull(byte[] buffer, int pos) { } } + protected static String formatValue(DebugInfoProvider.DebugLocalValueInfo value) { + switch (value.localKind()) { + case REGISTER: + return "REG:" + value.regIndex(); + case STACKSLOT: + return "STACK:" + value.stackSlot(); + case CONSTANT: + return "CONST:" + value.constantValue(); + case UNDEFINED: + default: + return "-"; + } + } + /** * Identify the section after which this debug section needs to be ordered when sizing and * creating content. @@ -616,25 +650,25 @@ protected int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodN return dwarfSections.getAbstractInlineMethodIndex(classEntry, methodName); } - protected void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline, int index) { - dwarfSections.setMethodLocalIndex(methodEntry, paramInfo, isInline, index); + protected void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { + dwarfSections.setMethodLocalIndex(methodEntry, localInfo, index); } - protected int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline) { + protected int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo) { if (!contentByteArrayCreated()) { return 0; } - return dwarfSections.getMethodLocalIndex(methodEntry, paramInfo, isInline); + return dwarfSections.getMethodLocalIndex(methodEntry, localInfo); } - protected void setMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline, int index) { - dwarfSections.setMethodLocationIndex(methodEntry, paramInfo, isInline, index); + protected void setRangeLocalIndex(Range range, DebugLocalInfo localInfo, int index) { + dwarfSections.setRangeLocalIndex(range, localInfo, index); } - protected int getMethodLocationIndex(MethodEntry methodEntry, DebugLocalInfo paramInfo, boolean isInline) { + protected int getRangeLocalIndex(Range range, DebugLocalInfo localInfo) { if (!contentByteArrayCreated()) { return 0; } - return dwarfSections.getMethodLocationIndex(methodEntry, paramInfo, isInline); + return dwarfSections.getRangeLocalIndex(range, localInfo); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 8d5e2309d25e..d233e39e0721 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1106,9 +1106,32 @@ public Stream locationInfoProvider() { // CallNode nodeToEmbed parent call node to convert to entry code leaf // NativeImageDebugLocationInfo leaf into which current leaf may be merged root.visitChildren(visitor, (Object) null, (Object) null, (Object) null); + // try to add a location record for offset zero + updateInitialLocation(compilation, locationInfos); return locationInfos.stream(); } + private void updateInitialLocation(CompilationResult compilationResult, List locationInfos) { + if (locationInfos.isEmpty()) { + // no info available anyway so give up + } + int prologueEnd = -1; + for (CompilationResult.CodeMark mark : compilationResult.getMarks()) { + if (mark.id.equals(SubstrateBackend.SubstrateMarkId.PROLOGUE_END)) { + prologueEnd = mark.pcOffset; + break; + } + } + if (prologueEnd < 0) { + // this is not a normal compiled method so give up + } + NativeImageDebugLocationInfo locationInfo = (NativeImageDebugLocationInfo) locationInfos.get(0); + if (locationInfo.lo < 24) { + // we can safely wind this record back to offset zero + locationInfo.lo = 0; + } + } + // indices for arguments passed to SingleLevelVisitor::apply protected static final int CALLER_INFO = 0; @@ -1436,7 +1459,7 @@ public List getFrameSizeChanges() { */ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInfo implements DebugLocationInfo { private final int bci; - private final int lo; + private int lo; private int hi; private DebugLocationInfo callersLocationInfo; private boolean isPrologueEnd; @@ -1707,7 +1730,7 @@ public LocalKind localKind() { @Override public int regIndex() { - return ((RegisterValue) value).getRegister().encoding(); + return ((RegisterValue) value).getRegister().number; } @Override From 93867a358383835002b334613e5e1b394f542a48 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 19 Apr 2022 13:32:55 +0100 Subject: [PATCH 07/22] Add synthetic debug location info at start of compiled Java method. --- substratevm/mx.substratevm/testhello.py | 226 ++++++--- .../objectfile/debugentry/DebugInfoBase.java | 3 +- .../oracle/objectfile/debugentry/Range.java | 11 +- .../debuginfo/DebugInfoProvider.java | 8 - .../elf/dwarf/DwarfLocSectionImpl.java | 45 +- .../core/code/CompilationResultFrameTree.java | 9 - .../src/com/oracle/svm/hosted/SVMHost.java | 7 +- .../image/NativeImageDebugInfoProvider.java | 433 +++++++++++++++--- .../com.oracle.svm.test/src/hello/Hello.java | 26 ++ 9 files changed, 585 insertions(+), 183 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index f9256b98ddf6..d7b8e2fe9eec 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -212,9 +212,19 @@ def test(): ]) checker.check(exec_string, skip_fails=False) + # check input argument args is known + exec_string = execute("info args") + rexp = [r"args = %s"%(address_pattern)] + checker = Checker("info args", rexp) + + # check local var greeter is not known + exec_string = execute("info locals") + rexp = [r"greeter = "] + checker = Checker("info locals", rexp) + if can_print_data: # print the contents of the arguments array which will be in rdi - exec_string = execute("print /x *(('java.lang.String[]' *)$rdi)") + exec_string = execute("print /x *args") rexp = [r"%s = {"%(wildcard_pattern), r"%s = {"%(spaces_pattern), r"%s<_objhdr> = {"%(spaces_pattern), @@ -231,7 +241,7 @@ def test(): checker.check(exec_string, skip_fails=False) # print the hub of the array and check it has a name field - exec_string = execute("print /x *(('java.lang.String[]' *)$rdi)->hub") + exec_string = execute("print /x *args->hub") if isolates: rexp = [r"%s = {"%(wildcard_pattern), r"%s = {"%(spaces_pattern), @@ -261,7 +271,7 @@ def test(): # print the hub name field and check it is String[] # n.b. the expected String text is not necessarily null terminated # so we need a wild card before the final quote - exec_string = execute("x/s (('java.lang.String[]' *)$rdi)->hub->name->value->data") + exec_string = execute("x/s args->hub->name->value->data") checker = Checker("print String[] hub name", r"%s:%s\"\[Ljava.lang.String;%s\""%(address_pattern, spaces_pattern, wildcard_pattern)) checker.check(exec_string, skip_fails=False) @@ -427,6 +437,21 @@ def test(): checker.check(exec_string, skip_fails=False) execute("delete breakpoints") + # step out of the call to Greeter.greeter and then step forward + # so the return value is assigned to local var greeter + exec_string = execute("finish"); + exec_string = execute("step"); + + # check argument args is not known + exec_string = execute("info args") + rexp = [r"args = "] + checker = Checker("info args 2", rexp) + + # check local var greeter is known + exec_string = execute("info locals") + rexp = [r"greeter = %s"%(address_pattern)] + checker = Checker("info locals 2", rexp) + # set a break point at standard library PrintStream.println. Ideally we would like to break only at println(String) # however in Java 17 and GraalVM >21.3.0 this method ends up getting inlined and we can't (yet?!) set a breakpoint # only to a specific override of a method by specifying the parameter types when that method gets inlined. @@ -439,9 +464,20 @@ def test(): execute("continue") + exec_string = execute("info args") + rexp = [r"this = %s"%(address_pattern), + r"%s = %s"%(varname_pattern, address_pattern)] + checker = Checker("info args println", rexp) + checker.check(exec_string) + + exec_string = execute("ptype this"); + rexp = [r"type = class java\.io\.PrintStream : public java\.io\.FilterOutputStream {"] + checker = Checker("ptype this", rexp) + checker.check(exec_string); + if can_print_data: # print the java.io.PrintStream instance and check its type - exec_string = execute("print /x *(('java.io.PrintStream' *)$rdi)") + exec_string = execute("print /x *this") rexp = [r"%s = {"%(wildcard_pattern), r"%s = {"%(spaces_pattern), r"%s = {"%(spaces_pattern), @@ -451,7 +487,7 @@ def test(): r"%sidHash = %s"%(spaces_pattern, address_pattern), r"%s}, }, },"%(spaces_pattern), r"%smembers of java.io.FilterOutputStream:"%(spaces_pattern), - r"%sclosed = 0x0,"%(spaces_pattern), + r"%sclosed = 0x0"%(spaces_pattern), r"%sout = %s,"%(spaces_pattern, address_pattern), r"%scloseLock = %s"%(spaces_pattern, address_pattern), r"%s},"%(spaces_pattern), @@ -469,7 +505,7 @@ def test(): # print the hub name field and check it is java.io.PrintStream # n.b. the expected String text is not necessarily null terminated # so we need a wild card before the final quote - exec_string = execute("x/s (('java.io.PrintStream' *)$rdi)->hub->name->value->data") + exec_string = execute("x/s this->hub->name->value->data") checker = Checker("print PrintStream hub name", r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) checker.check(exec_string, skip_fails=False) @@ -498,6 +534,7 @@ def test(): r"%sprivate:"%spaces_pattern, r"%sstatic void noInlineFoo\(void\);"%spaces_pattern, r"%sstatic void noInlineHere\(int\);"%spaces_pattern, + r"%sstatic void noInlineManyArgs\(int, int, int, int, int, int, int, int, int, float, float, float, float, float, float, float, float, float\);"%spaces_pattern, r"%sstatic void noInlineTest\(void\);"%spaces_pattern, r"%sstatic void noInlineThis\(void\);"%spaces_pattern, r"}"] @@ -513,18 +550,18 @@ def test(): r"%svoid hello\.Hello::noInlineHere\(int\);"%spaces_pattern, r"%svoid hello\.Hello::noInlineTest\(void\);"%spaces_pattern, r"%svoid hello\.Hello::noInlineThis\(void\);"%spaces_pattern] - checker = Checker('ptype info func nline', rexp) + checker = Checker('info func nline', rexp) checker.check(exec_string) # list inlineIs and inlineA and check that the listing maps to the inlined code instead of the actual code, # although not ideal this is how GDB treats inlined code in C/C++ as well - rexp = [r"125%sinlineA\(\);"%spaces_pattern] + rexp = [r"127%sinlineA\(\);"%spaces_pattern] checker = Checker('list inlineIs', rexp) checker.check(execute("list inlineIs")) - rexp = [r'file: "hello/Hello\.java", line number: 130, symbol: "hello\.Hello::inlineA"', - r"130%snoInlineTest\(\);"%spaces_pattern, - r'file: "hello/Hello\.java", line number: 131, symbol: "hello\.Hello::inlineA"', - r"131%s}"%spaces_pattern] + rexp = [r'file: "hello/Hello\.java", line number: 132, symbol: "hello\.Hello::inlineA"', + r"132%snoInlineTest\(\);"%spaces_pattern, + r'file: "hello/Hello\.java", line number: 133, symbol: "hello\.Hello::inlineA"', + r"133%s}"%spaces_pattern] checker = Checker('list inlineA', rexp) checker.check(execute("list inlineA")) @@ -537,38 +574,38 @@ def test(): execute("continue") exec_string = execute("list") - rexp = [r"125%sinlineA\(\);"%spaces_pattern] + rexp = [r"127%sinlineA\(\);"%spaces_pattern] checker = Checker('hit break at inlineIs', rexp) checker.check(exec_string, skip_fails=False) execute("step") exec_string = execute("list") - rexp = [r"130%snoInlineTest\(\);"%spaces_pattern] + rexp = [r"132%snoInlineTest\(\);"%spaces_pattern] checker = Checker('step in inlineA', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 4") - rexp = [r"#0%shello\.Hello::inlineA \(\) at hello/Hello\.java:130"%spaces_pattern, - r"#1%shello\.Hello::inlineIs \(\) at hello/Hello\.java:125"%spaces_pattern, - r"#2%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:120"%spaces_pattern, + rexp = [r"#0%shello\.Hello::inlineA \(\) at hello/Hello\.java:132"%spaces_pattern, + r"#1%shello\.Hello::inlineIs \(\) at hello/Hello\.java:127"%spaces_pattern, + r"#2%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:122"%spaces_pattern, r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace inlineMee', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") exec_string = execute("break hello.Hello::noInlineTest") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 135\."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 137\."%(digits_pattern, address_pattern) checker = Checker('break noInlineTest', rexp) checker.check(exec_string, skip_fails=False) execute("continue") exec_string = execute("list") - rexp = r"135%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern + rexp = r"137%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern checker = Checker('hit breakpoint in noInlineTest', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 5") - rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:135"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:130"%(spaces_pattern, address_pattern), - r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:125"%(spaces_pattern), - r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:120"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:137"%(spaces_pattern), + r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:132"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:127"%(spaces_pattern), + r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:122"%(spaces_pattern), r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace in inlineMethod', rexp) checker.check(exec_string, skip_fails=False) @@ -576,84 +613,141 @@ def test(): execute("delete breakpoints") # Set breakpoint at method with inline and not-inlined invocation in same line exec_string = execute("break hello.Hello::inlineFrom") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 141."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 143."%(digits_pattern, address_pattern) checker = Checker('break inlineFrom', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("info break 6") - rexp = [r"6%sbreakpoint%skeep%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:141"%(spaces_pattern, spaces_pattern, spaces_pattern, spaces_pattern, address_pattern)] + rexp = [r"6%sbreakpoint%skeep%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:143"%(spaces_pattern, spaces_pattern, spaces_pattern, spaces_pattern, address_pattern)] checker = Checker('info break inlineFrom', rexp) checker.check(exec_string) execute("delete breakpoints") - exec_string = execute("break Hello.java:157") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 157\."%(digits_pattern, address_pattern) + exec_string = execute("break Hello.java:159") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 159\."%(digits_pattern, address_pattern) checker = Checker('break Hello.java:157', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, arg_values_pattern), - r"#1%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#2%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#3%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#4%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#5%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#6%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#7%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#8%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#9%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#10%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:155"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#11%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:149"%(spaces_pattern, arg_values_pattern), - r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:141"%(spaces_pattern, address_pattern), + rexp = [r"#0%shello\.Hello::inlineMixTo %s at hello/Hello\.java:159"%(spaces_pattern, arg_values_pattern), + r"#1%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#6%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#7%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#8%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#9%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#10%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#11%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), + r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:143"%(spaces_pattern, address_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:170") - rexp = r"Breakpoint %s at %s: Hello\.java:170\. \(2 locations\)"%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:170', rexp) + exec_string = execute("break Hello.java:172") + rexp = r"Breakpoint %s at %s: Hello\.java:172\. \(2 locations\)"%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:172', rexp) checker.check(exec_string) execute("continue") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), - r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), - r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:168"%(spaces_pattern, arg_values_pattern), - r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:162"%(spaces_pattern, arg_values_pattern), - r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:143"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:172"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), + r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, arg_values_pattern), + r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:145"%(spaces_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:176") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 176\."%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:176', rexp) + exec_string = execute("break Hello.java:178") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 178\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:178', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 8") - rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:176"%(spaces_pattern, arg_values_pattern), - r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:179"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:144"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:178"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:146"%(spaces_pattern), r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) + exec_string = execute("break hello.Hello::noInlineManyArgs") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 187\."%(digits_pattern, address_pattern) + checker = Checker('break hello.Hello::noInlineManyArgs', rexp) + checker.check(exec_string) + + execute("continue") + exec_string = execute("info args") + rexp =[r"i0 = 0", + r"i1 = 1", + r"i2 = 2", + r"i3 = 3", + r"i4 = 4", + r"i5 = 5", + r"i6 = 6", + r"i7 = 7", + r"i8 = 8", + r"f0 = 0", + r"f1 = 1.125", + r"f2 = 2.25", + r"f3 = 3.375", + r"f4 = 4.5", + r"f5 = 5.625", + r"f6 = 6.75", + r"f7 = 7.875", + r"f8 = 9"] + checker = Checker('info args', rexp) + checker.check(exec_string) + + exec_string = execute("x/i $pc") + rexp = r"=> 0x542100 : sub $0x68,%rsp" + rexp = r".*sub %s\$0x%s,%%rsp"%(spaces_pattern, hex_digits_pattern) + checker = Checker('x/i $pc', rexp) + checker.check(exec_string) + + execute("stepi") + exec_string = execute("info args") + rexp =[r"i0 = 0", + r"i1 = 1", + r"i2 = 2", + r"i3 = 3", + r"i4 = 4", + r"i5 = 5", + r"i6 = 6", + r"i7 = 7", + r"i8 = 8", + r"f0 = 0", + r"f1 = 1.125", + r"f2 = 2.25", + r"f3 = 3.375", + r"f4 = 4.5", + r"f5 = 5.625", + r"f6 = 6.75", + r"f7 = 7.875", + r"f8 = 9"] + checker = Checker('info args 2', rexp) + checker.check(exec_string) + print(execute("quit 0")) test() diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index c540fec7e561..5df444cdd363 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -375,10 +375,9 @@ private Range addSubrange(DebugLocationInfo locationInfo, Range primaryRange, Cl final int lo = primaryRange.getLo() + locationInfo.addressLo(); final int hi = primaryRange.getLo() + locationInfo.addressHi(); final int line = locationInfo.line(); - final boolean isPrologueEnd = locationInfo.isPrologueEnd(); ClassEntry subRangeClassEntry = ensureClassEntry(ownerType); MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(locationInfo, this, debugContext); - Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isTopLevel, caller, isPrologueEnd); + Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isTopLevel, caller); classEntry.indexSubRange(subRange); subRangeIndex.put(locationInfo, subRange); debugContext.log(DebugContext.DETAILED_LEVEL, "SubRange %s.%s %d %s:%d [0x%x, 0x%x] (%d, %d)", diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 326973855b7f..6ba4e9aeacc0 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -74,7 +74,6 @@ public class Range { private final int lo; private int hi; private final int line; - private boolean isPrologueEnd; private final int depth; /** * This is null for a primary range. For sub ranges it holds the root of the call tree they @@ -140,13 +139,13 @@ public DebugLocalInfo getLocal(int i) { * Create a primary range. */ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line) { - this(stringTable, methodEntry, lo, hi, line, null, false, null, false); + this(stringTable, methodEntry, lo, hi, line, null, false, null); } /* * Create a primary or secondary range. */ - public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isTopLevel, Range caller, boolean isPrologueEnd) { + public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isTopLevel, Range caller) { assert methodEntry != null; if (methodEntry.fileEntry != null) { stringTable.uniqueDebugString(methodEntry.fileEntry.getFileName()); @@ -163,7 +162,6 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i this.lastCallee = null; this.siblingCallee = null; this.caller = caller; - this.isPrologueEnd = isPrologueEnd; if (caller != null) { caller.addCallee(this); } @@ -331,11 +329,6 @@ public int getDepth() { return depth; } - @SuppressWarnings("unused") - public boolean isPrologueEnd() { - return isPrologueEnd; - } - public void setLocalValueInfo(DebugLocalValueInfo[] localValueInfos) { int len = localValueInfos.length; this.localValueInfos = localValueInfos; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 6be34e1a002c..b8dcf34cf98b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -331,14 +331,6 @@ interface DebugLocationInfo extends DebugRangeInfo { */ DebugLocationInfo getCaller(); - /** - * Indicates whether the end of this leaf range corresponds to the end of a method prologue - * for either a top level of inline method. - * - * @return true if this range is a prologue end otherwise false - */ - boolean isPrologueEnd(); - /** * @return a stream of {@link DebugLocalValueInfo} objects identifying local or parameter * variables present in the frame of the current range. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index 469e5cda9014..e4cd5ae80fa1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -449,11 +449,11 @@ private int mapToDwarfReg(int regIdx) { private void initDwarfRegMap() { if (dwarfSections.elfMachine == ELFMachine.AArch64) { - dwarfRegMap = AARCH64_TO_DWARF_REG_MAP; + dwarfRegMap = GRAAL_AARCH64_TO_DWARF_REG_MAP; dwarfStackRegister = DWARF_REG_AARCH64_SP; } else { assert dwarfSections.elfMachine == ELFMachine.X86_64 : "must be"; - dwarfRegMap = X86_64_TO_DWARF_REG_MAP; + dwarfRegMap = GRAAL_X86_64_TO_DWARF_REG_MAP; dwarfStackRegister = DWARF_REG_X86_64_RSP; } } @@ -527,7 +527,7 @@ private void initDwarfRegMap() { private static final int DWARF_REG_AARCH64_V31 = 95; // map from compiler register indices to corresponding dwarf register - private static final int[] AARCH64_TO_DWARF_REG_MAP = { + private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = { DWARF_REG_AARCH64_R0, // 0 DWARF_REG_AARCH64_R1, // 1 DWARF_REG_AARCH64_R2, // ... @@ -598,9 +598,9 @@ private void initDwarfRegMap() { // register numbers used by DWARF for X86_64 registers private static final int DWARF_REG_X86_64_RAX = 0; - private static final int DWARF_REG_X86_64_RBX = 1; + private static final int DWARF_REG_X86_64_RDX = 1; private static final int DWARF_REG_X86_64_RCX = 2; - private static final int DWARF_REG_X86_64_RDX = 3; + private static final int DWARF_REG_X86_64_RBX = 3; private static final int DWARF_REG_X86_64_RSI = 4; private static final int DWARF_REG_X86_64_RDI = 5; private static final int DWARF_REG_X86_64_RBP = 6; @@ -613,14 +613,11 @@ private void initDwarfRegMap() { private static final int DWARF_REG_X86_64_R13 = 13; private static final int DWARF_REG_X86_64_R14 = 14; private static final int DWARF_REG_X86_64_R15 = 15; - private static final int DWARF_REG_X86_64_XMM0 = 40; - @SuppressWarnings("unused") private static final int DWARF_REG_X86_64_XMM15 = 55; - private static final int DWARF_REG_X86_64_XMM16 = 79; - @SuppressWarnings("unused") private static final int DWARF_REG_X86_64_XMM31 = 94; - - private static final int[] X86_64_TO_DWARF_REG_MAP = { - DWARF_REG_X86_64_RAX, - DWARF_REG_X86_64_RCX, + private static final int DWARF_REG_X86_64_XMM0 = 17; + + private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = { + DWARF_REG_X86_64_RAX, // 0 + DWARF_REG_X86_64_RCX, // 1 DWARF_REG_X86_64_RDX, DWARF_REG_X86_64_RBX, DWARF_REG_X86_64_RSP, @@ -634,8 +631,8 @@ private void initDwarfRegMap() { DWARF_REG_X86_64_R12, DWARF_REG_X86_64_R13, DWARF_REG_X86_64_R14, - DWARF_REG_X86_64_R15, - DWARF_REG_X86_64_XMM0, + DWARF_REG_X86_64_R15, // 15 + DWARF_REG_X86_64_XMM0, // 16 DWARF_REG_X86_64_XMM0 + 1, DWARF_REG_X86_64_XMM0 + 2, DWARF_REG_X86_64_XMM0 + 3, @@ -650,22 +647,6 @@ private void initDwarfRegMap() { DWARF_REG_X86_64_XMM0 + 12, DWARF_REG_X86_64_XMM0 + 13, DWARF_REG_X86_64_XMM0 + 14, - DWARF_REG_X86_64_XMM0 + 15, - DWARF_REG_X86_64_XMM16, - DWARF_REG_X86_64_XMM16 + 1, - DWARF_REG_X86_64_XMM16 + 2, - DWARF_REG_X86_64_XMM16 + 3, - DWARF_REG_X86_64_XMM16 + 4, - DWARF_REG_X86_64_XMM16 + 5, - DWARF_REG_X86_64_XMM16 + 6, - DWARF_REG_X86_64_XMM16 + 7, - DWARF_REG_X86_64_XMM16 + 8, - DWARF_REG_X86_64_XMM16 + 9, - DWARF_REG_X86_64_XMM16 + 10, - DWARF_REG_X86_64_XMM16 + 11, - DWARF_REG_X86_64_XMM16 + 12, - DWARF_REG_X86_64_XMM16 + 13, - DWARF_REG_X86_64_XMM16 + 14, - DWARF_REG_X86_64_XMM16 + 15, + DWARF_REG_X86_64_XMM0 + 15 }; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java index 36c1f685655b..deab01bcdeed 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CompilationResultFrameTree.java @@ -114,10 +114,6 @@ public int compareTo(SourcePositionSupplier o) { } return 0; } - - public boolean isMethodStart() { - return false; - } } public static final class InfopointSourceWrapper extends SourcePositionSupplier { @@ -165,11 +161,6 @@ public int compareTo(SourcePositionSupplier o) { } return 1; /* make Infopoints go first */ } - - @Override - public boolean isMethodStart() { - return infopoint.reason.equals(InfopointReason.METHOD_START); - } } public static final class SourceMappingWrapper extends SourcePositionSupplier { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 6741ce43e236..e205b4b72248 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -293,12 +293,9 @@ public boolean isInitialized(AnalysisType type) { @Override public GraphBuilderConfiguration updateGraphBuilderConfiguration(GraphBuilderConfiguration config, AnalysisMethod method) { -<<<<<<< HEAD return config.withRetainLocalVariables(retainLocalVariables()) - .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())); -======= - return config.withRetainLocalVariables(retainLocalVariables()).withFullInfopoints(SubstrateOptions.GenerateDebugInfo.getValue() > 0); ->>>>>>> 30e67accfc64 (Reimplement inline method and line debug info generation using CompilationResultFrameTree) + .withUnresolvedIsError(linkAtBuildTimeSupport.linkAtBuildTime(method.getDeclaringClass())) + .withFullInfopoints(SubstrateOptions.GenerateDebugInfo.getValue() > 0); } private boolean retainLocalVariables() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index d233e39e0721..b4699622b730 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -37,7 +37,7 @@ import com.oracle.svm.core.code.CompilationResultFrameTree.Visitor; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; -import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.code.SubstrateBackend.SubstrateMarkId; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.image.ImageHeapPartition; @@ -58,8 +58,12 @@ import com.oracle.svm.hosted.substitute.SubstitutionField; import com.oracle.svm.hosted.substitute.SubstitutionMethod; import com.oracle.svm.hosted.substitute.SubstitutionType; +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.amd64.AMD64; +import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Constant; @@ -977,21 +981,21 @@ private List createParamInfo(ResolvedJavaMethod method) { LineNumberTable lineNumberTable = method.getLineNumberTable(); int line = (lineNumberTable != null ? lineNumberTable.getLineNumber(0) : -1); int slot = 0; + ResolvedJavaType ownerType = method.getDeclaringClass(); if (!method.isStatic()) { - ResolvedJavaType ownerType = method.getDeclaringClass(); JavaKind kind = ownerType.getJavaKind(); - JavaKind storageKind = isPseudoObjectType(ownerType) ? JavaKind.Long : kind; + JavaKind storageKind = isPseudoObjectType(ownerType, ownerType) ? JavaKind.Long : kind; assert kind == JavaKind.Object : "must be an object"; paramInfos.add(new NativeImageDebugLocalValueInfo("this", storageKind, ownerType, slot, line)); slot += kind.getSlotCount(); } for (int i = 0; i < parameterCount; i++) { - JavaType type = signature.getParameterType(i, null); - JavaKind kind = type.getJavaKind(); - JavaKind storageKind = isPseudoObjectType(type) ? JavaKind.Long : kind; Local local = (table == null ? null : table.getLocal(slot, 0)); String name = (local != null ? local.getName() : "__" + i); - paramInfos.add(new NativeImageDebugLocalValueInfo(name, storageKind, type.resolve(null), slot, line)); + ResolvedJavaType paramType = (ResolvedJavaType) signature.getParameterType(i, null); + JavaKind kind = paramType.getJavaKind(); + JavaKind storageKind = isPseudoObjectType(paramType, ownerType) ? JavaKind.Long : kind; + paramInfos.add(new NativeImageDebugLocalValueInfo(name, storageKind, paramType, slot, line)); slot += kind.getSlotCount(); } return paramInfos; @@ -1002,10 +1006,11 @@ private List createParamInfo(ResolvedJavaMethod method) { * foreign opaque type. * * @param type the type to be tested + * @param accessingType another type relative to which the first type may need to be resolved * @return true if the type is a pseudo object type */ - private boolean isPseudoObjectType(JavaType type) { - ResolvedJavaType resolvedJavaType = type.resolve(null); + private boolean isPseudoObjectType(JavaType type, ResolvedJavaType accessingType) { + ResolvedJavaType resolvedJavaType = type.resolve(accessingType); return (wordBaseType.isAssignableFrom(resolvedJavaType)); } @@ -1107,29 +1112,73 @@ public Stream locationInfoProvider() { // NativeImageDebugLocationInfo leaf into which current leaf may be merged root.visitChildren(visitor, (Object) null, (Object) null, (Object) null); // try to add a location record for offset zero - updateInitialLocation(compilation, locationInfos); + updateInitialLocation(locationInfos); return locationInfos.stream(); } - private void updateInitialLocation(CompilationResult compilationResult, List locationInfos) { - if (locationInfos.isEmpty()) { - // no info available anyway so give up - } - int prologueEnd = -1; - for (CompilationResult.CodeMark mark : compilationResult.getMarks()) { - if (mark.id.equals(SubstrateBackend.SubstrateMarkId.PROLOGUE_END)) { - prologueEnd = mark.pcOffset; - break; + private int findMarkOffset(SubstrateMarkId markId) { + for (CompilationResult.CodeMark mark : compilation.getMarks()) { + if (mark.id.equals(markId)) { + return mark.pcOffset; } } + return -1; + } + + private void updateInitialLocation(List locationInfos) { + int prologueEnd = findMarkOffset(SubstrateMarkId.PROLOGUE_END); if (prologueEnd < 0) { // this is not a normal compiled method so give up + return; + } + int stackDecrement = findMarkOffset(SubstrateMarkId.PROLOGUE_DECD_RSP); + if (stackDecrement < 0) { + // this is not a normal compiled method so give up + return; + } + // if there are any location info records then the first one will be for + // a nop which follows the stack decrement, stack range check and pushes + // of arguments into the stack frame. + // + // We can construct synthetic location info covering the first instruction + // based on the method arguments and the calling convention and that will + // normally be valid right up to the nop. In exceptional cases a call + // might pass arguments on the stack, in which case the stack decrement will + // invalidate the original stack locations. Providing location info for that + // case requires adding two locations, one for initial instruction that does + // the stack decrement and another for the range up to the nop. They will + // be essentially the same but the stack locations will be adjusted to account + // for the different value of the stack pointer. + + if (locationInfos.isEmpty()) { + // this is not a normal compiled method so give up + return; + } + NativeImageDebugLocationInfo firstLocation = (NativeImageDebugLocationInfo) locationInfos.get(0); + int firstLocationOffset = firstLocation.addressLo(); + + if (firstLocationOffset == 0) { + // this is not a normal compiled method so give up + return; + } + if (firstLocationOffset < prologueEnd) { + // this is not a normal compiled method so give up + return; } - NativeImageDebugLocationInfo locationInfo = (NativeImageDebugLocationInfo) locationInfos.get(0); - if (locationInfo.lo < 24) { - // we can safely wind this record back to offset zero - locationInfo.lo = 0; + // create a synthetic location record including details of passed arguments + ParamLocationProducer locProducer = new ParamLocationProducer(method); + debugContext.log(DebugContext.DETAILED_LEVEL, "Add synthetic Location Info : %s (0, %d)", method.getName(), firstLocationOffset - 1); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(method, firstLocationOffset, locProducer); + // if the prologue extends beyond the stack extend and uses the stack then the info + // needs + // splitting at the extend point with the stack offsets adjusted in the new info + if (locProducer.usesStack() && firstLocationOffset > stackDecrement) { + NativeImageDebugLocationInfo splitLocationInfo = locationInfo.split(stackDecrement, getFrameSize()); + debugContext.log(DebugContext.DETAILED_LEVEL, "Split synthetic Location Info : %s (%d, %d) (%d, %d)", locationInfo.name(), 0, + locationInfo.addressLo() - 1, locationInfo.addressLo(), locationInfo.addressHi() - 1); + locationInfos.add(0, splitLocationInfo); } + locationInfos.add(0, locationInfo); } // indices for arguments passed to SingleLevelVisitor::apply @@ -1301,7 +1350,7 @@ private NativeImageDebugLocationInfo createEmbeddedParentLocationInfo(CallNode p BytecodePosition pos = parentToEmbed.frame; int startPos = parentToEmbed.getStartPos(); int endPos = (firstChild != null ? firstChild.getStartPos() : parentToEmbed.getEndPos() + 1); - NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(pos, startPos, endPos, callerLocation, true); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(pos, startPos, endPos, callerLocation); debugContext.log(DebugContext.DETAILED_LEVEL, "Embed leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), locationInfo.addressHi() - 1); return locationInfo; @@ -1433,17 +1482,17 @@ public List getFrameSizeChanges() { List frameSizeChanges = new LinkedList<>(); for (CompilationResult.CodeMark mark : compilation.getMarks()) { /* We only need to observe stack increment or decrement points. */ - if (mark.id.equals(SubstrateBackend.SubstrateMarkId.PROLOGUE_DECD_RSP)) { + if (mark.id.equals(SubstrateMarkId.PROLOGUE_DECD_RSP)) { NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); frameSizeChanges.add(sizeChange); // } else if (mark.id.equals("PROLOGUE_END")) { // can ignore these // } else if (mark.id.equals("EPILOGUE_START")) { // can ignore these - } else if (mark.id.equals(SubstrateBackend.SubstrateMarkId.EPILOGUE_INCD_RSP)) { + } else if (mark.id.equals(SubstrateMarkId.EPILOGUE_INCD_RSP)) { NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, CONTRACT); frameSizeChanges.add(sizeChange); - } else if (mark.id.equals(SubstrateBackend.SubstrateMarkId.EPILOGUE_END) && mark.pcOffset < compilation.getTargetCodeSize()) { + } else if (mark.id.equals(SubstrateMarkId.EPILOGUE_END) && mark.pcOffset < compilation.getTargetCodeSize()) { /* There is code after this return point so notify a stack extend again. */ NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); frameSizeChanges.add(sizeChange); @@ -1462,27 +1511,59 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf private int lo; private int hi; private DebugLocationInfo callersLocationInfo; - private boolean isPrologueEnd; private List localInfoList; NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { - this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo, frameNode.sourcePos.isMethodStart()); + this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo); } NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo) { - this(bcpos, lo, hi, callersLocationInfo, false); - } - - NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo, boolean isPrologueEnd) { super(bcpos.getMethod()); this.bci = bcpos.getBCI(); this.lo = lo; this.hi = hi; this.callersLocationInfo = callersLocationInfo; - this.isPrologueEnd = isPrologueEnd; this.localInfoList = initLocalInfoList(bcpos); } + // special constructor for synthetic lcoation info added at start of method + NativeImageDebugLocationInfo(ResolvedJavaMethod method, int hi, ParamLocationProducer locProducer) { + super(method); + // bci is always 0 and lo is always 0. + this.bci = 0; + this.lo = 0; + this.hi = hi; + // this is always going to be a top-level leaf range. + this.callersLocationInfo = null; + // location info is synthesized off the method signature + this.localInfoList = initSyntheticInfoList(locProducer); + } + + NativeImageDebugLocationInfo(NativeImageDebugLocationInfo toSplit, int stackDecrement, int frameSize) { + super(toSplit.method); + this.lo = stackDecrement; + this.hi = toSplit.hi; + toSplit.hi = this.lo; + this.bci = toSplit.bci; + this.callersLocationInfo = toSplit.callersLocationInfo; + this.localInfoList = new ArrayList<>(toSplit.localInfoList.size()); + for (DebugLocalValueInfo localInfo : toSplit.localInfoList) { + if (localInfo.localKind() == DebugLocalValueInfo.LocalKind.STACKSLOT) { + NativeImageDebugLocalValue value = new NativeImageDebugStackValue(localInfo.stackSlot() - (frameSize - 8)); + NativeImageDebugLocalValueInfo nativeLocalInfo = (NativeImageDebugLocalValueInfo) localInfo; + NativeImageDebugLocalValueInfo newLocalinfo = new NativeImageDebugLocalValueInfo(nativeLocalInfo.name, + value, + nativeLocalInfo.kind, + nativeLocalInfo.type, + nativeLocalInfo.slot, + nativeLocalInfo.line); + localInfoList.add(newLocalinfo); + } else { + localInfoList.add(localInfo); + } + } + } + private List initLocalInfoList(BytecodePosition bcpos) { if (!(bcpos instanceof BytecodeFrame)) { return null; @@ -1507,7 +1588,8 @@ private List initLocalInfoList(BytecodePosition bcpos) { if (l != null) { // we have a local with a known name, type and slot String name = l.getName(); - ResolvedJavaType type = l.getType().resolve(method.getDeclaringClass()); + ResolvedJavaType ownerType = method.getDeclaringClass(); + ResolvedJavaType type = l.getType().resolve(ownerType); JavaKind kind = type.getJavaKind(); int slot = l.getSlot(); debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", i, name, type.getName(), slot); @@ -1519,7 +1601,7 @@ private List initLocalInfoList(BytecodePosition bcpos) { // only add the local if the kinds match if ((storageKind == kind) || isIntegralKindPromotion(storageKind, kind) || - (isPseudoObjectType(type) && kind == JavaKind.Object && storageKind == JavaKind.Long)) { + (isPseudoObjectType(type, ownerType) && kind == JavaKind.Object && storageKind == JavaKind.Long)) { localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, type, slot, line)); } else if (storageKind != JavaKind.Illegal) { debugContext.log(DebugContext.DETAILED_LEVEL, " value kind incompatible with var kind %s!", type.getJavaKind().toString()); @@ -1529,6 +1611,44 @@ private List initLocalInfoList(BytecodePosition bcpos) { return localInfos; } + private List initSyntheticInfoList(ParamLocationProducer locProducer) { + Signature signature = method.getSignature(); + int parameterCount = signature.getParameterCount(false); + ArrayList localInfos = new ArrayList<>(); + LocalVariableTable table = method.getLocalVariableTable(); + LineNumberTable lineNumberTable = method.getLineNumberTable(); + int line = (lineNumberTable != null ? lineNumberTable.getLineNumber(0) : -1); + int slot = 0; + int localIdx = 0; + ResolvedJavaType ownerType = method.getDeclaringClass(); + if (!method.isStatic()) { + String name = "this"; + JavaKind kind = ownerType.getJavaKind(); + JavaKind storageKind = isPseudoObjectType(ownerType, ownerType) ? JavaKind.Long : kind; + assert kind == JavaKind.Object : "must be an object"; + NativeImageDebugLocalValue value = locProducer.nextLocation(kind); + debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", localIdx, name, ownerType.getName(), slot); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, ownerType, slot, line)); + slot += storageKind.getSlotCount(); + localIdx++; + } + for (int i = 0; i < parameterCount; i++) { + Local local = (table == null ? null : table.getLocal(slot, 0)); + String name = (local != null ? local.getName() : "__" + i); + ResolvedJavaType paramType = (ResolvedJavaType) signature.getParameterType(i, ownerType); + JavaKind kind = paramType.getJavaKind(); + JavaKind storageKind = isPseudoObjectType(paramType, ownerType) ? JavaKind.Long : kind; + NativeImageDebugLocalValue value = locProducer.nextLocation(kind); + debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", localIdx, name, ownerType.getName(), slot); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, paramType, slot, line)); + slot += storageKind.getSlotCount(); + localIdx++; + } + return localInfos; + } + private Local[] getLocalsBySlot() { LocalVariableTable lvt = method.getLocalVariableTable(); Local[] nonEmptySortedLocals = null; @@ -1566,11 +1686,6 @@ public DebugLocationInfo getCaller() { return callersLocationInfo; } - @Override - public boolean isPrologueEnd() { - return isPrologueEnd; - } - @Override public DebugLocalValueInfo[] getLocalValueInfo() { if (localInfoList != null) { @@ -1616,9 +1731,6 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { if (method != that.method) { return null; } - if (this.isPrologueEnd != that.isPrologueEnd) { - return null; - } if (line() != that.line()) { return null; } @@ -1639,12 +1751,161 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { return this; } + + public NativeImageDebugLocationInfo split(int stackDecrement, int frameSize) { + // this should be for an initial range extending beyond the stack decrement + assert lo == 0 && lo < stackDecrement && stackDecrement < hi : "invalid split request"; + return new NativeImageDebugLocationInfo(this, stackDecrement, frameSize); + } + + } + + static final Register[] AARCH64_GPREG = { + AArch64.r0, + AArch64.r1, + AArch64.r2, + AArch64.r3, + AArch64.r4, + AArch64.r5, + AArch64.r6, + AArch64.r7 + }; + static final Register[] AARCH64_FREG = { + AArch64.v0, + AArch64.v1, + AArch64.v2, + AArch64.v3, + AArch64.v4, + AArch64.v5, + AArch64.v6, + AArch64.v7 + }; + static final Register[] AMD64_GPREG = { + AMD64.rdi, + AMD64.rsi, + AMD64.rdx, + AMD64.rcx, + AMD64.r8, + AMD64.r9 + }; + static final Register[] AMD64_FREG = { + AMD64.xmm0, + AMD64.xmm1, + AMD64.xmm2, + AMD64.xmm3, + AMD64.xmm4, + AMD64.xmm5, + AMD64.xmm6, + AMD64.xmm7 + }; + + class ParamLocationProducer { + Register[] gpregs; + Register[] fregs; + int nextGPRegIdx; + int nextFPregIdx; + int nextStackIdx; + int stackParamCount; + + ParamLocationProducer(ResolvedJavaMethod method) { + Architecture arch = ConfigurationValues.getTarget().arch; + if (arch instanceof AArch64) { + gpregs = AARCH64_GPREG; + fregs = AARCH64_FREG; + } else { + assert arch instanceof AMD64 : "must be"; + gpregs = AMD64_GPREG; + fregs = AMD64_FREG; + } + nextGPRegIdx = 0; + nextFPregIdx = 0; + nextStackIdx = 0; + stackParamCount = computeStackCount(method); + } + + public NativeImageDebugLocalValue nextLocation(JavaKind kind) { + switch (kind) { + case Float: + case Double: + return nextFloatingLocation(); + case Void: + case Illegal: + assert false : "unexpected parameter kind in next location request"; + return null; + default: + return nextIntegerLocation(); + } + } + + public NativeImageDebugLocalValue nextFloatingLocation() { + if (nextFPregIdx < fregs.length) { + return new NativeImageDebugRegisterValue(fregs[nextFPregIdx++].number); + } else { + return nextStackLocation(); + } + } + + public NativeImageDebugLocalValue nextIntegerLocation() { + if (nextGPRegIdx < gpregs.length) { + return new NativeImageDebugRegisterValue(gpregs[nextGPRegIdx++].number); + } else { + return nextStackLocation(); + } + } + + public NativeImageDebugLocalValue nextStackLocation() { + // offset is computed relative to the undecremented stack pointer and includes + // an extra 16 bytes to allow for the stacked return address and alignment + assert nextStackIdx < stackParamCount : "encountered too many stack params"; + int stackIdx = nextStackIdx++; + return new NativeImageDebugStackValue((2 + stackIdx) * -8); + } + + public boolean usesStack() { + return stackParamCount > 0; + } + + private int computeStackCount(ResolvedJavaMethod method) { + int numIntegerParams = 0; + int numFloatingParams = 0; + if (!method.isStatic()) { + numIntegerParams++; + } + Signature signature = method.getSignature(); + int parameterCount = signature.getParameterCount(false); + if (!method.isStatic()) { + numIntegerParams++; + } + for (int i = 0; i < parameterCount; i++) { + switch (signature.getParameterKind(i)) { + case Float: + case Double: + numFloatingParams++; + break; + case Void: + case Illegal: + assert false : "unexpected parameter kind in method sig"; + break; + default: + numIntegerParams++; + break; + } + } + int excessParams = 0; + if (numIntegerParams > gpregs.length) { + excessParams += (numIntegerParams - gpregs.length); + } + if (numFloatingParams > fregs.length) { + excessParams += (numFloatingParams - fregs.length); + } + return excessParams; + } } public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { private final String name; private ResolvedJavaType type; - private final JavaValue value; + private final NativeImageDebugLocalValue value; private final JavaKind kind; private int slot; private int line; @@ -1656,20 +1917,41 @@ public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { NativeImageDebugLocalValueInfo(String name, JavaValue value, JavaKind kind, ResolvedJavaType type, int slot, int line) { this.name = name; - this.value = value; this.kind = kind; this.type = type; this.slot = slot; this.line = line; if (value instanceof RegisterValue) { this.localKind = LocalKind.REGISTER; + this.value = new NativeImageDebugRegisterValue((RegisterValue) value); } else if (value instanceof StackSlot) { this.localKind = LocalKind.STACKSLOT; + this.value = new NativeImageDebugStackValue((StackSlot) value); } else if (value instanceof Constant) { this.localKind = LocalKind.CONSTANT; + this.value = new NativeImageDebugConstantValue((Constant) value); } else { this.localKind = LocalKind.UNDEFINED; + this.value = null; + } + } + + NativeImageDebugLocalValueInfo(String name, NativeImageDebugLocalValue value, JavaKind kind, ResolvedJavaType type, int slot, int line) { + this.name = name; + this.kind = kind; + this.type = type; + this.slot = slot; + this.line = line; + if (value == null) { + this.localKind = LocalKind.UNDEFINED; + } else if (value instanceof NativeImageDebugRegisterValue) { + this.localKind = LocalKind.REGISTER; + } else if (value instanceof NativeImageDebugStackValue) { + this.localKind = LocalKind.STACKSLOT; + } else if (value instanceof NativeImageDebugConstantValue) { + this.localKind = LocalKind.CONSTANT; } + this.value = value; } @Override @@ -1730,17 +2012,17 @@ public LocalKind localKind() { @Override public int regIndex() { - return ((RegisterValue) value).getRegister().number; + return ((NativeImageDebugRegisterValue) value).getNumber(); } @Override public int stackSlot() { - return ((StackSlot) value).getRawOffset(); + return ((NativeImageDebugStackValue) value).getOffset(); } @Override public Constant constantValue() { - return null; + return ((NativeImageDebugConstantValue) value).getConstant(); } public boolean sameAs(NativeImageDebugLocalValueInfo that) { @@ -1751,9 +2033,9 @@ public boolean sameAs(NativeImageDebugLocalValueInfo that) { case STACKSLOT: return stackSlot() != that.stackSlot(); case CONSTANT: { - Constant thisValue = (Constant) value; - Constant thatValue = (Constant) that.value; - return thisValue.equals(thatValue); + Constant thisValue = constantValue(); + Constant thatValue = that.constantValue(); + return (thisValue == thatValue || (thisValue != null && thisValue.equals(thatValue))); } case UNDEFINED: return true; @@ -1763,6 +2045,53 @@ public boolean sameAs(NativeImageDebugLocalValueInfo that) { } } + public class NativeImageDebugLocalValue { + } + + public class NativeImageDebugRegisterValue extends NativeImageDebugLocalValue { + private int number; + + NativeImageDebugRegisterValue(RegisterValue value) { + number = value.getRegister().number; + } + + NativeImageDebugRegisterValue(int number) { + this.number = number; + } + + public int getNumber() { + return number; + } + } + + public class NativeImageDebugStackValue extends NativeImageDebugLocalValue { + private int offset; + + NativeImageDebugStackValue(StackSlot value) { + offset = value.getRawOffset(); + } + + NativeImageDebugStackValue(int offset) { + this.offset = offset; + } + + public int getOffset() { + return offset; + } + } + + public class NativeImageDebugConstantValue extends NativeImageDebugLocalValue { + private Constant value; + + NativeImageDebugConstantValue(Constant value) { + this.value = value; + } + + public Constant getConstant() { + return value; + } + } + /** * Implementation of the DebugFrameSizeChange API interface that allows stack frame size change * info to be passed to an ObjectFile when generation of debug info is enabled. diff --git a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java index 1c711047aecc..ab49f6fce3e2 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -92,6 +92,8 @@ public static void main(String[] args) { inlineCallChain(); noInlineThis(); inlineFrom(); + noInlineManyArgs(0, 1, 2, 3, 4, 5, 6, 7, 8, + 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F); System.exit(0); } @@ -178,4 +180,28 @@ private static void inlineTailRecursion(int n) { } inlineTailRecursion(n - 1); } + + @NeverInline("For testing purposes") + private static void noInlineManyArgs(int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, + float f0, float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8) { + System.out.println("i0 = " + i0); + System.out.println("i1 = " + i1); + System.out.println("i2 = " + i2); + System.out.println("i3 = " + i3); + System.out.println("i4 = " + i4); + System.out.println("i5 = " + i5); + System.out.println("i6 = " + i6); + System.out.println("i7 = " + i7); + System.out.println("i8 = " + i8); + System.out.println("f0 = " + f0); + System.out.println("f1 = " + f1); + System.out.println("f2 = " + f2); + System.out.println("f3 = " + f3); + System.out.println("f4 = " + f4); + System.out.println("f5 = " + f5); + System.out.println("f6 = " + f6); + System.out.println("f7 = " + f7); + System.out.println("f8 = " + f8); + } + } From 0448a5d2f542be3196d442effc046c4a64e7b554 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 22 Apr 2022 16:18:54 +0100 Subject: [PATCH 08/22] Fix problem when AnalysisMethod instances are found in frames. --- .../debuginfo/DebugInfoProvider.java | 3 +- .../image/NativeImageDebugInfoProvider.java | 30 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index b8dcf34cf98b..be82e975ee18 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -245,8 +245,7 @@ interface DebugMethodInfo extends DebugMemberInfo { boolean isOverride(); /* - * Return the unique type that owns this method. - *

    + * Return the unique type that owns this method.

    * * @return the unique type that owns this method */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index b4699622b730..e8e2ecae8d81 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -28,6 +28,7 @@ import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod; import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; @@ -817,9 +818,16 @@ protected abstract class NativeImageDebugBaseMethodInfo extends NativeImageDebug protected final List paramInfo; protected final DebugLocalInfo thisParamInfo; - NativeImageDebugBaseMethodInfo(ResolvedJavaMethod method) { - super(method); - this.method = method; + NativeImageDebugBaseMethodInfo(ResolvedJavaMethod m) { + super(m); + // We occasionally see an AnalysisMethod as input to this constructor. + // That can happen if the points to analysis builds one into a node + // source position when building the initial graph. The global + // replacement that is supposed to ensure the compiler sees HostedXXX + // types rather than AnalysisXXX types appears to skip translating + // method references in node source positions. So, we do the translation + // here just to make sure we use a HostedMethod wherever possible. + method = promoteAnalysisToHosted(m); this.paramInfo = createParamInfo(method); // We use the target modifiers to decide where to install any first param // even though we may have added it according to whether method is static. @@ -835,6 +843,17 @@ protected abstract class NativeImageDebugBaseMethodInfo extends NativeImageDebug } } + private ResolvedJavaMethod promoteAnalysisToHosted(ResolvedJavaMethod m) { + if (m instanceof AnalysisMethod) { + return heap.getUniverse().lookup(m); + } + if (!(m instanceof HostedMethod)) { + debugContext.log(DebugContext.DETAILED_LEVEL, "Method is neither Hosted nor Analysis : %s.%s%s", m.getDeclaringClass().getName(), m.getName(), + m.getSignature().toMethodDescriptor()); + } + return m; + } + /** * Return the unique type that owns this method. *

    @@ -951,6 +970,7 @@ public int modifiers() { } return method.getModifiers(); } + @Override public boolean isConstructor() { return method.isConstructor(); @@ -1035,8 +1055,8 @@ public boolean isVirtual() { @Override public int vtableOffset() { /* - * TODO - provide correct offset, not index. In Graal, the vtable is appended after - * the dynamicHub object, so can't just multiply by sizeof(pointer). + * TODO - provide correct offset, not index. In Graal, the vtable is appended after the + * dynamicHub object, so can't just multiply by sizeof(pointer). */ return hostedMethod.hasVTableIndex() ? hostedMethod.getVTableIndex() : -1; } From 197b2ea69ed58828a080509c22473ef41cd62a31 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 22 Apr 2022 17:34:10 +0100 Subject: [PATCH 09/22] Allow for args being optimized out at start of inlined method. --- substratevm/mx.substratevm/testhello.py | 43 ++++--------------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index d7b8e2fe9eec..48da99a359f5 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -465,51 +465,20 @@ def test(): execute("continue") exec_string = execute("info args") - rexp = [r"this = %s"%(address_pattern), - r"%s = %s"%(varname_pattern, address_pattern)] + # we canto be sure whether "this" or the argument available + # the call to println may get inlined in which case there is no + # guarantee that the args won't be optimized away + rexp = [r"this = %s"%(wildcard_pattern), + r"%s = %s"%(varname_pattern, wildcard_pattern)] checker = Checker("info args println", rexp) checker.check(exec_string) exec_string = execute("ptype this"); + # the debugger shoudl still know the type of "this" rexp = [r"type = class java\.io\.PrintStream : public java\.io\.FilterOutputStream {"] checker = Checker("ptype this", rexp) checker.check(exec_string); - if can_print_data: - # print the java.io.PrintStream instance and check its type - exec_string = execute("print /x *this") - rexp = [r"%s = {"%(wildcard_pattern), - r"%s = {"%(spaces_pattern), - r"%s = {"%(spaces_pattern), - r"%s = {"%(spaces_pattern), - r"%s<_objhdr> = {"%(spaces_pattern), - r"%shub = %s,"%(spaces_pattern, address_pattern), - r"%sidHash = %s"%(spaces_pattern, address_pattern), - r"%s}, }, },"%(spaces_pattern), - r"%smembers of java.io.FilterOutputStream:"%(spaces_pattern), - r"%sclosed = 0x0"%(spaces_pattern), - r"%sout = %s,"%(spaces_pattern, address_pattern), - r"%scloseLock = %s"%(spaces_pattern, address_pattern), - r"%s},"%(spaces_pattern), - r"%smembers of java.io.PrintStream:"%(spaces_pattern), - r"%stextOut = %s,"%(spaces_pattern, address_pattern), - r"%scharOut = %s,"%(spaces_pattern, address_pattern), - r"%sautoFlush = 0x1,"%(spaces_pattern), - r"%sclosing = 0x0"%(spaces_pattern), - r"}"] - - checker = Checker("print DefaultGreeterSystem.out", rexp) - - checker.check(exec_string, skip_fails=True) - - # print the hub name field and check it is java.io.PrintStream - # n.b. the expected String text is not necessarily null terminated - # so we need a wild card before the final quote - exec_string = execute("x/s this->hub->name->value->data") - checker = Checker("print PrintStream hub name", - r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) - checker.check(exec_string, skip_fails=False) - ### # Tests for inlined methods ### From fb5d88b8eb825f9c0fa59dbebacf362db94e2931 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 26 Apr 2022 15:21:49 +0100 Subject: [PATCH 10/22] Support primitive and null constant locals/params and correct stack location offsets. --- .../oracle/objectfile/debugentry/Range.java | 3 +- .../debuginfo/DebugInfoProvider.java | 4 +- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 1 + .../elf/dwarf/DwarfInfoSectionImpl.java | 11 ++- .../elf/dwarf/DwarfLocSectionImpl.java | 74 ++++++++++++++++- .../image/NativeImageDebugInfoProvider.java | 83 +++++++++++-------- 6 files changed, 131 insertions(+), 45 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 6ba4e9aeacc0..b8c154345c72 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -381,12 +381,11 @@ public void addVarRanges(Range subRange, HashMap> va switch (localValueInfo.localKind()) { case REGISTER: case STACKSLOT: + case CONSTANT: List varRanges = varRangeMap.get(local); assert varRanges != null : "local not present in var to ranges map!"; varRanges.add(subRange); break; - case CONSTANT: - // drop through cannot handle constants for now case UNDEFINED: break; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index be82e975ee18..2c8188e89e72 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -31,7 +31,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; @@ -376,7 +376,7 @@ enum LocalKind { int stackSlot(); - Constant constantValue(); + JavaConstant constantValue(); } interface DebugFrameSizeChange { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index 5df996e44547..d9d438cd891a 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -284,6 +284,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte DW_OP_regx = (byte) 0x90; public static final byte DW_OP_bregx = (byte) 0x92; public static final byte DW_OP_push_object_address = (byte) 0x97; + public static final byte DW_OP_implicit_value = (byte) 0x9e; /* Register constants for AArch64. */ public static final byte rheapbase_aarch64 = (byte) 27; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 706e3c8c9901..daf415cfb65e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -32,6 +32,9 @@ import java.util.Iterator; import java.util.List; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.PrimitiveConstant; + import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.LayoutDecision; @@ -1418,8 +1421,14 @@ private int writeMethodLocalLocation(DebugContext context, Range range, DebugLoc localValues.add(value); break; case CONSTANT: - // cannot handle constants just yet + JavaConstant constant = value.constantValue(); + // can only handle primitive or null constants just now + if (constant instanceof PrimitiveConstant || constant.isNull()) { + localValues.add(value); + } + break; default: + break; } } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index e4cd5ae80fa1..01a15add8b58 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -47,6 +47,12 @@ import java.util.Map; import java.util.Set; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.PrimitiveConstant; + +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_implicit_value; + /** * Section generator for debug_loc section. */ @@ -235,6 +241,8 @@ private int writeVarLocations(DebugContext context, DebugLocalInfo local, long b pos = writeStackLocation(context, value.stackSlot(), buffer, pos); break; case CONSTANT: + pos = writeConstantLocation(context, value.constantValue(), buffer, pos); + break; default: assert false : "Should not reach here!"; break; @@ -303,7 +311,7 @@ private int writeStackLocation(DebugContext context, int offset, byte[] buffer, // need to pass base reg index as a ULEB operand pos += putULEB(sp, scratch, 0); } - pos += putSLEB(0L - offset, scratch, 0); + pos += putSLEB(offset, scratch, 0); } else { int patchPos = pos; pos = putShort(byteCount, buffer, pos); @@ -313,7 +321,7 @@ private int writeStackLocation(DebugContext context, int offset, byte[] buffer, // need to pass base reg index as a ULEB operand pos = putULEB(sp, buffer, pos); } - pos = putSLEB(0L - offset, buffer, pos); + pos = putSLEB(offset, buffer, pos); // now backpatch the byte count byteCount = (byte) (pos - zeroPos); putShort(byteCount, buffer, patchPos); @@ -326,6 +334,57 @@ private int writeStackLocation(DebugContext context, int offset, byte[] buffer, return pos; } + private int writeConstantLocation(DebugContext context, JavaConstant constant, byte[] buffer, int p) { + int pos = p; + byte op = DW_OP_implicit_value; + JavaKind kind = constant.getJavaKind(); + int byteCount = (kind == JavaKind.Object ? 8 : kind.getByteCount()); + assert (kind != JavaKind.Object || constant.isNull()) : "only expecting null object constant!"; + if (buffer == null) { + pos += putByte(op, scratch, 0); + pos += putULEB(byteCount, scratch, 0); + if (constant.isNull()) { + pos += writeAttrData8(0, scratch, 0); + } else if (byteCount == 1) { + if (kind == JavaKind.Int) { + pos += putByte((byte) constant.asInt(), scratch, 0); + } else { + pos += putByte((byte) (constant.asBoolean() ? 1 : 0), scratch, 0); + } + } else if (byteCount == 2) { + pos += putShort((short) constant.asInt(), scratch, 0); + } else if (byteCount == 4) { + int i = (kind == JavaKind.Int ? constant.asInt() : Float.floatToRawIntBits(constant.asFloat())); + pos += putInt(i, scratch, 0); + } else { + long l = (kind == JavaKind.Long ? constant.asLong() : Double.doubleToRawLongBits(constant.asDouble())); + pos += putLong(l, scratch, 0); + } + } else { + pos = putByte(op, buffer, pos); + pos = putULEB(byteCount, buffer, pos); + if (constant.isNull()) { + pos = writeAttrData8(0, buffer, pos); + } else if (byteCount == 1) { + if (kind == JavaKind.Int) { + pos = putByte((byte) constant.asInt(), buffer, pos); + } else { + pos = putByte((byte) (constant.asBoolean() ? 1 : 0), buffer, pos); + } + } else if (byteCount == 2) { + pos = putShort((short) constant.asInt(), buffer, pos); + } else if (byteCount == 4) { + int i = (kind == JavaKind.Int ? constant.asInt() : Float.floatToRawIntBits(constant.asFloat())); + pos = putInt(i, buffer, pos); + } else { + long l = (kind == JavaKind.Long ? constant.asLong() : Double.doubleToRawLongBits(constant.asDouble())); + pos = putLong(l, buffer, pos); + } + verboseLog(context, " [0x%08x] CONSTANT %s", pos, constant.toValueString()); + } + return pos; + } + // auxiliary class used to collect per-range locations for a given local // merging adjacent ranges with the same location static class LocalValueExtent { @@ -365,7 +424,14 @@ boolean shouldMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { return false; } break; - case CONSTANT: // should not get here + case CONSTANT: { + JavaConstant constant = value.constantValue(); + JavaConstant otherConstant = otherValue.constantValue(); + // we can only handle primitive or null constants for now + assert constant instanceof PrimitiveConstant || constant.isNull(); + assert otherConstant instanceof PrimitiveConstant || otherConstant.isNull(); + return constant.equals(otherConstant); + } case UNDEFINED: assert false : "unexpected local value type"; break; @@ -373,7 +439,7 @@ boolean shouldMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { return true; } - LocalValueExtent maybeMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { + private LocalValueExtent maybeMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { if (shouldMerge(otherLo, otherHi, otherValue)) { // We can extend the current extent to cover the next one. this.hi = otherHi; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index e8e2ecae8d81..3d6510010078 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -68,12 +68,14 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.LineNumberTable; import jdk.vm.ci.meta.Local; import jdk.vm.ci.meta.LocalVariableTable; +import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -1125,7 +1127,8 @@ public Stream locationInfoProvider() { } final List locationInfos = new ArrayList<>(); final boolean omitInline = SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue(); - final Visitor visitor = (omitInline ? new TopLevelVisitor(locationInfos) : new MultiLevelVisitor(locationInfos)); + int frameSize = getFrameSize(); + final Visitor visitor = (omitInline ? new TopLevelVisitor(locationInfos, frameSize) : new MultiLevelVisitor(locationInfos, frameSize)); // arguments passed by visitor to apply are // NativeImageDebugLocationInfo caller location info // CallNode nodeToEmbed parent call node to convert to entry code leaf @@ -1210,9 +1213,11 @@ private void updateInitialLocation(List locationInfos) { private abstract class SingleLevelVisitor implements Visitor { protected final List locationInfos; + protected final int frameSize; - SingleLevelVisitor(List locationInfos) { + SingleLevelVisitor(List locationInfos, int frameSize) { this.locationInfos = locationInfos; + this.frameSize = frameSize; } public NativeImageDebugLocationInfo process(FrameNode node, NativeImageDebugLocationInfo callerInfo) { @@ -1220,18 +1225,18 @@ public NativeImageDebugLocationInfo process(FrameNode node, NativeImageDebugLoca if (node instanceof CallNode) { // this node represents an inline call range so // add a locationinfo to cover the range of the call - locationInfo = createCallLocationInfo((CallNode) node, callerInfo); + locationInfo = createCallLocationInfo((CallNode) node, callerInfo, frameSize); } else { // this is leaf method code so add details of its range - locationInfo = createLeafLocationInfo(node, callerInfo); + locationInfo = createLeafLocationInfo(node, callerInfo, frameSize); } return locationInfo; } } private class TopLevelVisitor extends SingleLevelVisitor { - TopLevelVisitor(List locationInfos) { - super(locationInfos); + TopLevelVisitor(List locationInfos, int frameSize) { + super(locationInfos, frameSize); } @Override @@ -1256,8 +1261,8 @@ public void apply(FrameNode node, Object... args) { } public class MultiLevelVisitor extends SingleLevelVisitor { - MultiLevelVisitor(List locationInfos) { - super(locationInfos); + MultiLevelVisitor(List locationInfos, int frameSize) { + super(locationInfos, frameSize); } @Override @@ -1274,7 +1279,7 @@ public void apply(FrameNode node, Object... args) { // parent CallNode // its end range is determined by the start of the first node at this // level - NativeImageDebugLocationInfo embeddedLocationInfo = createEmbeddedParentLocationInfo(nodeToEmbed, node, callerInfo); + NativeImageDebugLocationInfo embeddedLocationInfo = createEmbeddedParentLocationInfo(nodeToEmbed, node, callerInfo, frameSize); locationInfos.add(embeddedLocationInfo); // since this is a leaf node we can merge later leafs into it initMerge(embeddedLocationInfo, args); @@ -1297,7 +1302,7 @@ public void apply(FrameNode node, Object... args) { callNode.visitChildren(this, locationInfo, callNode, (Object) null); } else { // we need to embed a leaf node for the whole call range - locationInfo = createEmbeddedParentLocationInfo(callNode, null, locationInfo); + locationInfo = createEmbeddedParentLocationInfo(callNode, null, locationInfo, frameSize); locationInfos.add(locationInfo); } } else { @@ -1333,9 +1338,9 @@ public void apply(FrameNode node, Object... args) { * @param node is a simple FrameNode * @return the newly created location info record */ - private NativeImageDebugLocationInfo createLeafLocationInfo(FrameNode node, NativeImageDebugLocationInfo callerInfo) { + private NativeImageDebugLocationInfo createLeafLocationInfo(FrameNode node, NativeImageDebugLocationInfo callerInfo, int framesize) { assert !(node instanceof CallNode); - NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(node, callerInfo); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(node, callerInfo, framesize); debugContext.log(DebugContext.DETAILED_LEVEL, "Create leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), locationInfo.addressHi() - 1); return locationInfo; @@ -1347,9 +1352,9 @@ private NativeImageDebugLocationInfo createLeafLocationInfo(FrameNode node, Nati * @param callNode is the top level inlined call frame * @return the newly created location info record */ - private NativeImageDebugLocationInfo createCallLocationInfo(CallNode callNode, NativeImageDebugLocationInfo callerInfo) { + private NativeImageDebugLocationInfo createCallLocationInfo(CallNode callNode, NativeImageDebugLocationInfo callerInfo, int framesize) { BytecodePosition callerPos = realCaller(callNode); - NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(callerPos, callNode.getStartPos(), callNode.getEndPos() + 1, callerInfo); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(callerPos, callNode.getStartPos(), callNode.getEndPos() + 1, callerInfo, framesize); debugContext.log(DebugContext.DETAILED_LEVEL, "Create call Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), locationInfo.addressHi() - 1); return locationInfo; @@ -1366,11 +1371,11 @@ private NativeImageDebugLocationInfo createCallLocationInfo(CallNode callNode, N * @param callerLocation the location info created to represent the range for the call * @return a location info to be embedded as the first child range of the caller location. */ - private NativeImageDebugLocationInfo createEmbeddedParentLocationInfo(CallNode parentToEmbed, FrameNode firstChild, NativeImageDebugLocationInfo callerLocation) { + private NativeImageDebugLocationInfo createEmbeddedParentLocationInfo(CallNode parentToEmbed, FrameNode firstChild, NativeImageDebugLocationInfo callerLocation, int framesize) { BytecodePosition pos = parentToEmbed.frame; int startPos = parentToEmbed.getStartPos(); int endPos = (firstChild != null ? firstChild.getStartPos() : parentToEmbed.getEndPos() + 1); - NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(pos, startPos, endPos, callerLocation); + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(pos, startPos, endPos, callerLocation, framesize); debugContext.log(DebugContext.DETAILED_LEVEL, "Embed leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), locationInfo.addressHi() - 1); return locationInfo; @@ -1533,17 +1538,17 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf private DebugLocationInfo callersLocationInfo; private List localInfoList; - NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo) { - this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo); + NativeImageDebugLocationInfo(FrameNode frameNode, NativeImageDebugLocationInfo callersLocationInfo, int framesize) { + this(frameNode.frame, frameNode.getStartPos(), frameNode.getEndPos() + 1, callersLocationInfo, framesize); } - NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo) { + NativeImageDebugLocationInfo(BytecodePosition bcpos, int lo, int hi, NativeImageDebugLocationInfo callersLocationInfo, int framesize) { super(bcpos.getMethod()); this.bci = bcpos.getBCI(); this.lo = lo; this.hi = hi; this.callersLocationInfo = callersLocationInfo; - this.localInfoList = initLocalInfoList(bcpos); + this.localInfoList = initLocalInfoList(bcpos, framesize); } // special constructor for synthetic lcoation info added at start of method @@ -1569,7 +1574,7 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf this.localInfoList = new ArrayList<>(toSplit.localInfoList.size()); for (DebugLocalValueInfo localInfo : toSplit.localInfoList) { if (localInfo.localKind() == DebugLocalValueInfo.LocalKind.STACKSLOT) { - NativeImageDebugLocalValue value = new NativeImageDebugStackValue(localInfo.stackSlot() - (frameSize - 8)); + NativeImageDebugLocalValue value = new NativeImageDebugStackValue(localInfo.stackSlot() + (frameSize - 8)); NativeImageDebugLocalValueInfo nativeLocalInfo = (NativeImageDebugLocalValueInfo) localInfo; NativeImageDebugLocalValueInfo newLocalinfo = new NativeImageDebugLocalValueInfo(nativeLocalInfo.name, value, @@ -1584,7 +1589,7 @@ private class NativeImageDebugLocationInfo extends NativeImageDebugBaseMethodInf } } - private List initLocalInfoList(BytecodePosition bcpos) { + private List initLocalInfoList(BytecodePosition bcpos, int framesize) { if (!(bcpos instanceof BytecodeFrame)) { return null; } @@ -1622,7 +1627,7 @@ private List initLocalInfoList(BytecodePosition bcpos) { if ((storageKind == kind) || isIntegralKindPromotion(storageKind, kind) || (isPseudoObjectType(type, ownerType) && kind == JavaKind.Object && storageKind == JavaKind.Long)) { - localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, type, slot, line)); + localInfos.add(new NativeImageDebugLocalValueInfo(name, value, framesize, storageKind, type, slot, line)); } else if (storageKind != JavaKind.Illegal) { debugContext.log(DebugContext.DETAILED_LEVEL, " value kind incompatible with var kind %s!", type.getJavaKind().toString()); } @@ -1878,7 +1883,7 @@ public NativeImageDebugLocalValue nextStackLocation() { // an extra 16 bytes to allow for the stacked return address and alignment assert nextStackIdx < stackParamCount : "encountered too many stack params"; int stackIdx = nextStackIdx++; - return new NativeImageDebugStackValue((2 + stackIdx) * -8); + return new NativeImageDebugStackValue((2 + stackIdx) * 8); } public boolean usesStack() { @@ -1932,10 +1937,10 @@ public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { private LocalKind localKind; NativeImageDebugLocalValueInfo(String name, JavaKind kind, ResolvedJavaType type, int slot, int line) { - this(name, Value.ILLEGAL, kind, type, slot, line); + this(name, Value.ILLEGAL, 0, kind, type, slot, line); } - NativeImageDebugLocalValueInfo(String name, JavaValue value, JavaKind kind, ResolvedJavaType type, int slot, int line) { + NativeImageDebugLocalValueInfo(String name, JavaValue value, int framesize, JavaKind kind, ResolvedJavaType type, int slot, int line) { this.name = name; this.kind = kind; this.type = type; @@ -1946,10 +1951,16 @@ public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { this.value = new NativeImageDebugRegisterValue((RegisterValue) value); } else if (value instanceof StackSlot) { this.localKind = LocalKind.STACKSLOT; - this.value = new NativeImageDebugStackValue((StackSlot) value); - } else if (value instanceof Constant) { - this.localKind = LocalKind.CONSTANT; - this.value = new NativeImageDebugConstantValue((Constant) value); + this.value = new NativeImageDebugStackValue((StackSlot) value, framesize); + } else if (value instanceof JavaConstant) { + // for now we can only handle primiitve constants + if (value instanceof PrimitiveConstant || ((JavaConstant) value).isNull()) { + this.localKind = LocalKind.CONSTANT; + this.value = new NativeImageDebugConstantValue((JavaConstant) value); + } else { + this.localKind = LocalKind.UNDEFINED; + this.value = null; + } } else { this.localKind = LocalKind.UNDEFINED; this.value = null; @@ -2041,7 +2052,7 @@ public int stackSlot() { } @Override - public Constant constantValue() { + public JavaConstant constantValue() { return ((NativeImageDebugConstantValue) value).getConstant(); } @@ -2087,8 +2098,8 @@ public int getNumber() { public class NativeImageDebugStackValue extends NativeImageDebugLocalValue { private int offset; - NativeImageDebugStackValue(StackSlot value) { - offset = value.getRawOffset(); + NativeImageDebugStackValue(StackSlot value, int framesize) { + offset = value.getOffset(framesize); } NativeImageDebugStackValue(int offset) { @@ -2101,13 +2112,13 @@ public int getOffset() { } public class NativeImageDebugConstantValue extends NativeImageDebugLocalValue { - private Constant value; + private JavaConstant value; - NativeImageDebugConstantValue(Constant value) { + NativeImageDebugConstantValue(JavaConstant value) { this.value = value; } - public Constant getConstant() { + public JavaConstant getConstant() { return value; } } From 83631af27364abe986d4f7792eb7ac895a054e3f Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 3 May 2022 12:03:18 +0100 Subject: [PATCH 11/22] Review rework update test of argument passing. --- substratevm/mx.substratevm/testhello.py | 30 +- .../objectfile/debugentry/DebugInfoBase.java | 2 +- .../oracle/objectfile/debugentry/Range.java | 2 +- .../debuginfo/DebugInfoProvider.java | 2 - .../elf/dwarf/DwarfLocSectionImpl.java | 512 +++++++++--------- .../image/NativeImageDebugInfoProvider.java | 102 +++- .../com.oracle.svm.test/src/hello/Hello.java | 18 +- 7 files changed, 383 insertions(+), 285 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 48da99a359f5..5604ff6dd2bf 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -465,7 +465,7 @@ def test(): execute("continue") exec_string = execute("info args") - # we canto be sure whether "this" or the argument available + # we cannot be sure whether "this" or argument "x" are available # the call to println may get inlined in which case there is no # guarantee that the args won't be optimized away rexp = [r"this = %s"%(wildcard_pattern), @@ -503,7 +503,7 @@ def test(): r"%sprivate:"%spaces_pattern, r"%sstatic void noInlineFoo\(void\);"%spaces_pattern, r"%sstatic void noInlineHere\(int\);"%spaces_pattern, - r"%sstatic void noInlineManyArgs\(int, int, int, int, int, int, int, int, int, float, float, float, float, float, float, float, float, float\);"%spaces_pattern, + r"%sstatic void noInlineManyArgs\(int, int, int, int, boolean, int, int, long, int, long, float, float, float, float, double, float, float, float, float, double, boolean, float\);"%spaces_pattern, r"%sstatic void noInlineTest\(void\);"%spaces_pattern, r"%sstatic void noInlineThis\(void\);"%spaces_pattern, r"}"] @@ -671,25 +671,29 @@ def test(): r"i1 = 1", r"i2 = 2", r"i3 = 3", - r"i4 = 4", + r"b4 = true", r"i5 = 5", r"i6 = 6", - r"i7 = 7", + r"l7 = 7", r"i8 = 8", + r"l9 = 9", r"f0 = 0", r"f1 = 1.125", r"f2 = 2.25", r"f3 = 3.375", - r"f4 = 4.5", + r"d4 = 4.5", r"f5 = 5.625", r"f6 = 6.75", r"f7 = 7.875", - r"f8 = 9"] + r"f8 = 9", + r"d9 = 10.125", + r"b10 = false", + r"f11 = 12.375"] checker = Checker('info args', rexp) checker.check(exec_string) exec_string = execute("x/i $pc") - rexp = r"=> 0x542100 : sub $0x68,%rsp" + rexp = r"=> 0x542100 : sub $0x68,%rsp" rexp = r".*sub %s\$0x%s,%%rsp"%(spaces_pattern, hex_digits_pattern) checker = Checker('x/i $pc', rexp) checker.check(exec_string) @@ -700,20 +704,24 @@ def test(): r"i1 = 1", r"i2 = 2", r"i3 = 3", - r"i4 = 4", + r"b4 = true", r"i5 = 5", r"i6 = 6", - r"i7 = 7", + r"l7 = 7", r"i8 = 8", + r"l9 = 9", r"f0 = 0", r"f1 = 1.125", r"f2 = 2.25", r"f3 = 3.375", - r"f4 = 4.5", + r"d4 = 4.5", r"f5 = 5.625", r"f6 = 6.75", r"f7 = 7.875", - r"f8 = 9"] + r"f8 = 9", + r"d9 = 10.125", + r"b10 = false", + r"f11 = 12.375"] checker = Checker('info args 2', rexp) checker.check(exec_string) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 5df444cdd363..39f12125b386 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -386,7 +386,7 @@ private Range addSubrange(DebugLocationInfo locationInfo, Range primaryRange, Cl DebugLocalValueInfo[] localValueInfos = locationInfo.getLocalValueInfo(); for (int i = 0; i < localValueInfos.length; i++) { DebugLocalValueInfo localValueInfo = localValueInfos[i]; - debugContext.log(DebugContext.DETAILED_LEVEL, " locals[%d] %s:%s = %s", localValueInfo.slot(), localValueInfo.name(), localValueInfo.typeName(), localValueInfo.valueString()); + debugContext.log(DebugContext.DETAILED_LEVEL, " locals[%d] %s:%s = %s", localValueInfo.slot(), localValueInfo.name(), localValueInfo.typeName(), localValueInfo); } subRange.setLocalValueInfo(localValueInfos); return subRange; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index b8c154345c72..c860f6f08f26 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -377,7 +377,7 @@ public void addVarRanges(Range subRange, HashMap> va for (int i = 0; i < localValueCount; i++) { DebugLocalValueInfo localValueInfo = subRange.getLocalValue(i); DebugLocalInfo local = subRange.getLocal(i); - if (local != null && localValueInfo.localKind() != DebugLocalValueInfo.LocalKind.UNDEFINED) { + if (local != null) { switch (localValueInfo.localKind()) { case REGISTER: case STACKSLOT: diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 2c8188e89e72..6aee548f17ef 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -368,8 +368,6 @@ enum LocalKind { CONSTANT } - String valueString(); - LocalKind localKind(); int regIndex(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index 01a15add8b58..3d5de94b8acf 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -241,7 +241,14 @@ private int writeVarLocations(DebugContext context, DebugLocalInfo local, long b pos = writeStackLocation(context, value.stackSlot(), buffer, pos); break; case CONSTANT: - pos = writeConstantLocation(context, value.constantValue(), buffer, pos); + JavaConstant constant = value.constantValue(); + if (constant instanceof PrimitiveConstant) { + pos = writePrimitiveConstantLocation(context, value.constantValue(), buffer, pos); + } else if (constant.isNull()) { + pos = writeNullConstantLocation(context, value.constantValue(), buffer, pos); + } else { + assert false : "Should not reach here!"; + } break; default: assert false : "Should not reach here!"; @@ -334,26 +341,28 @@ private int writeStackLocation(DebugContext context, int offset, byte[] buffer, return pos; } - private int writeConstantLocation(DebugContext context, JavaConstant constant, byte[] buffer, int p) { + private int writePrimitiveConstantLocation(DebugContext context, JavaConstant constant, byte[] buffer, int p) { + assert constant instanceof PrimitiveConstant; int pos = p; byte op = DW_OP_implicit_value; JavaKind kind = constant.getJavaKind(); - int byteCount = (kind == JavaKind.Object ? 8 : kind.getByteCount()); - assert (kind != JavaKind.Object || constant.isNull()) : "only expecting null object constant!"; + int dataByteCount = kind.getByteCount(); + // total bytes is op + uleb + dataByteCount + int byteCount = 1 + 1 + dataByteCount; if (buffer == null) { + pos += putShort((short) byteCount, scratch, 0); pos += putByte(op, scratch, 0); - pos += putULEB(byteCount, scratch, 0); - if (constant.isNull()) { - pos += writeAttrData8(0, scratch, 0); - } else if (byteCount == 1) { - if (kind == JavaKind.Int) { - pos += putByte((byte) constant.asInt(), scratch, 0); - } else { + pos += putULEB(dataByteCount, scratch, 0); + + if (dataByteCount == 1) { + if (kind == JavaKind.Boolean) { pos += putByte((byte) (constant.asBoolean() ? 1 : 0), scratch, 0); + } else { + pos += putByte((byte) constant.asInt(), scratch, 0); } - } else if (byteCount == 2) { + } else if (dataByteCount == 2) { pos += putShort((short) constant.asInt(), scratch, 0); - } else if (byteCount == 4) { + } else if (dataByteCount == 4) { int i = (kind == JavaKind.Int ? constant.asInt() : Float.floatToRawIntBits(constant.asFloat())); pos += putInt(i, scratch, 0); } else { @@ -361,19 +370,18 @@ private int writeConstantLocation(DebugContext context, JavaConstant constant, b pos += putLong(l, scratch, 0); } } else { + pos = putShort((short) byteCount, buffer, pos); pos = putByte(op, buffer, pos); - pos = putULEB(byteCount, buffer, pos); - if (constant.isNull()) { - pos = writeAttrData8(0, buffer, pos); - } else if (byteCount == 1) { - if (kind == JavaKind.Int) { - pos = putByte((byte) constant.asInt(), buffer, pos); - } else { + pos = putULEB(dataByteCount, buffer, pos); + if (dataByteCount == 1) { + if (kind == JavaKind.Boolean) { pos = putByte((byte) (constant.asBoolean() ? 1 : 0), buffer, pos); + } else { + pos = putByte((byte) constant.asInt(), buffer, pos); } - } else if (byteCount == 2) { + } else if (dataByteCount == 2) { pos = putShort((short) constant.asInt(), buffer, pos); - } else if (byteCount == 4) { + } else if (dataByteCount == 4) { int i = (kind == JavaKind.Int ? constant.asInt() : Float.floatToRawIntBits(constant.asFloat())); pos = putInt(i, buffer, pos); } else { @@ -385,6 +393,28 @@ private int writeConstantLocation(DebugContext context, JavaConstant constant, b return pos; } + private int writeNullConstantLocation(DebugContext context, JavaConstant constant, byte[] buffer, int p) { + assert constant.isNull(); + int pos = p; + byte op = DW_OP_implicit_value; + int dataByteCount = 8; + // total bytes is op + uleb + dataByteCount + int byteCount = 1 + 1 + dataByteCount; + if (buffer == null) { + pos += putShort((short) byteCount, scratch, 0); + pos += putByte(op, scratch, 0); + pos += putULEB(dataByteCount, scratch, 0); + pos = writeAttrData8(0, buffer, pos); + } else { + pos = putShort((short) byteCount, buffer, pos); + pos = putByte(op, buffer, pos); + pos = putULEB(dataByteCount, buffer, pos); + pos = writeAttrData8(0, buffer, pos); + verboseLog(context, " [0x%08x] CONSTANT %s", pos, constant.toValueString()); + } + return pos; + } + // auxiliary class used to collect per-range locations for a given local // merging adjacent ranges with the same location static class LocalValueExtent { @@ -404,39 +434,7 @@ boolean shouldMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { if (hi != otherLo) { return false; } - // values need to be for the same line - if (value.line() != otherValue.line()) { - return false; - } - // location kinds must match - if (value.localKind() != otherValue.localKind()) { - return false; - } - // locations must match - switch (value.localKind()) { - case REGISTER: - if (value.regIndex() != otherValue.regIndex()) { - return false; - } - break; - case STACKSLOT: - if (value.stackSlot() != otherValue.stackSlot()) { - return false; - } - break; - case CONSTANT: { - JavaConstant constant = value.constantValue(); - JavaConstant otherConstant = otherValue.constantValue(); - // we can only handle primitive or null constants for now - assert constant instanceof PrimitiveConstant || constant.isNull(); - assert otherConstant instanceof PrimitiveConstant || otherConstant.isNull(); - return constant.equals(otherConstant); - } - case UNDEFINED: - assert false : "unexpected local value type"; - break; - } - return true; + return value.equals(otherValue); } private LocalValueExtent maybeMerge(int otherLo, int otherHi, DebugLocalValueInfo otherValue) { @@ -516,203 +514,235 @@ private int mapToDwarfReg(int regIdx) { private void initDwarfRegMap() { if (dwarfSections.elfMachine == ELFMachine.AArch64) { dwarfRegMap = GRAAL_AARCH64_TO_DWARF_REG_MAP; - dwarfStackRegister = DWARF_REG_AARCH64_SP; + dwarfStackRegister = DwarfRegEncodingAArch64.SP.encoding; } else { assert dwarfSections.elfMachine == ELFMachine.X86_64 : "must be"; dwarfRegMap = GRAAL_X86_64_TO_DWARF_REG_MAP; - dwarfStackRegister = DWARF_REG_X86_64_RSP; + dwarfStackRegister = DwarfRegEncodingAMD64.RSP.encoding; } } // register numbers used by DWARF for AArch64 registers - private static final int DWARF_REG_AARCH64_R0 = 0; - private static final int DWARF_REG_AARCH64_R1 = 1; - private static final int DWARF_REG_AARCH64_R2 = 2; - private static final int DWARF_REG_AARCH64_R3 = 3; - private static final int DWARF_REG_AARCH64_R4 = 4; - private static final int DWARF_REG_AARCH64_R5 = 5; - private static final int DWARF_REG_AARCH64_R6 = 6; - private static final int DWARF_REG_AARCH64_R7 = 7; - private static final int DWARF_REG_AARCH64_R8 = 8; - private static final int DWARF_REG_AARCH64_R9 = 9; - private static final int DWARF_REG_AARCH64_R10 = 10; - private static final int DWARF_REG_AARCH64_R11 = 11; - private static final int DWARF_REG_AARCH64_R12 = 12; - private static final int DWARF_REG_AARCH64_R13 = 13; - private static final int DWARF_REG_AARCH64_R14 = 14; - private static final int DWARF_REG_AARCH64_R15 = 15; - private static final int DWARF_REG_AARCH64_R16 = 16; - private static final int DWARF_REG_AARCH64_R17 = 17; - private static final int DWARF_REG_AARCH64_R18 = 18; - private static final int DWARF_REG_AARCH64_R19 = 19; - private static final int DWARF_REG_AARCH64_R20 = 20; - private static final int DWARF_REG_AARCH64_R21 = 21; - private static final int DWARF_REG_AARCH64_R22 = 22; - private static final int DWARF_REG_AARCH64_R23 = 23; - private static final int DWARF_REG_AARCH64_R24 = 24; - private static final int DWARF_REG_AARCH64_R25 = 25; - private static final int DWARF_REG_AARCH64_R26 = 26; - private static final int DWARF_REG_AARCH64_R27 = 27; - private static final int DWARF_REG_AARCH64_R28 = 28; - private static final int DWARF_REG_AARCH64_R29 = 29; - private static final int DWARF_REG_AARCH64_R30 = 30; - private static final int DWARF_REG_AARCH64_R31 = 31; - private static final int DWARF_REG_AARCH64_ZR = 96; - private static final int DWARF_REG_AARCH64_SP = 31; - private static final int DWARF_REG_AARCH64_V0 = 64; - private static final int DWARF_REG_AARCH64_V1 = 65; - private static final int DWARF_REG_AARCH64_V2 = 66; - private static final int DWARF_REG_AARCH64_V3 = 67; - private static final int DWARF_REG_AARCH64_V4 = 68; - private static final int DWARF_REG_AARCH64_V5 = 69; - private static final int DWARF_REG_AARCH64_V6 = 70; - private static final int DWARF_REG_AARCH64_V7 = 71; - private static final int DWARF_REG_AARCH64_V8 = 72; - private static final int DWARF_REG_AARCH64_V9 = 73; - private static final int DWARF_REG_AARCH64_V10 = 74; - private static final int DWARF_REG_AARCH64_V11 = 75; - private static final int DWARF_REG_AARCH64_V12 = 76; - private static final int DWARF_REG_AARCH64_V13 = 77; - private static final int DWARF_REG_AARCH64_V14 = 78; - private static final int DWARF_REG_AARCH64_V15 = 79; - private static final int DWARF_REG_AARCH64_V16 = 80; - private static final int DWARF_REG_AARCH64_V17 = 81; - private static final int DWARF_REG_AARCH64_V18 = 82; - private static final int DWARF_REG_AARCH64_V19 = 83; - private static final int DWARF_REG_AARCH64_V20 = 84; - private static final int DWARF_REG_AARCH64_V21 = 85; - private static final int DWARF_REG_AARCH64_V22 = 86; - private static final int DWARF_REG_AARCH64_V23 = 87; - private static final int DWARF_REG_AARCH64_V24 = 88; - private static final int DWARF_REG_AARCH64_V25 = 89; - private static final int DWARF_REG_AARCH64_V26 = 90; - private static final int DWARF_REG_AARCH64_V27 = 91; - private static final int DWARF_REG_AARCH64_V28 = 92; - private static final int DWARF_REG_AARCH64_V29 = 93; - private static final int DWARF_REG_AARCH64_V30 = 94; - private static final int DWARF_REG_AARCH64_V31 = 95; - - // map from compiler register indices to corresponding dwarf register + public enum DwarfRegEncodingAArch64 { + R0(0), + R1(1), + R2(2), + R3(3), + R4(4), + R5(5), + R6(6), + R7(7), + R8(8), + R9(9), + R10(10), + R11(11), + R12(12), + R13(13), + R14(14), + R15(15), + R16(16), + R17(17), + R18(18), + R19(19), + R20(20), + R21(21), + R22(22), + R23(23), + R24(24), + R25(25), + R26(26), + R27(27), + R28(28), + R29(29), + R30(30), + R31(31), + ZR(96), + SP(31), + V0(64), + V1(65), + V2(66), + V3(67), + V4(68), + V5(69), + V6(70), + V7(71), + V8(72), + V9(73), + V10(74), + V11(75), + V12(76), + V13(77), + V14(78), + V15(79), + V16(80), + V17(81), + V18(82), + V19(83), + V20(84), + V21(85), + V22(86), + V23(87), + V24(88), + V25(89), + V26(90), + V27(91), + V28(92), + V29(93), + V30(94), + V31(95); + + public final int encoding; + + DwarfRegEncodingAArch64(int encoding) { + this.encoding = encoding; + } + } + + // map from compiler AArch64 register indices to corresponding dwarf AArch64 register index private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = { - DWARF_REG_AARCH64_R0, // 0 - DWARF_REG_AARCH64_R1, // 1 - DWARF_REG_AARCH64_R2, // ... - DWARF_REG_AARCH64_R3, - DWARF_REG_AARCH64_R4, - DWARF_REG_AARCH64_R5, - DWARF_REG_AARCH64_R6, - DWARF_REG_AARCH64_R7, - DWARF_REG_AARCH64_R8, - DWARF_REG_AARCH64_R9, - DWARF_REG_AARCH64_R10, - DWARF_REG_AARCH64_R11, - DWARF_REG_AARCH64_R12, - DWARF_REG_AARCH64_R13, - DWARF_REG_AARCH64_R14, - DWARF_REG_AARCH64_R15, - DWARF_REG_AARCH64_R16, - DWARF_REG_AARCH64_R17, - DWARF_REG_AARCH64_R18, - DWARF_REG_AARCH64_R19, - DWARF_REG_AARCH64_R20, - DWARF_REG_AARCH64_R21, - DWARF_REG_AARCH64_R22, - DWARF_REG_AARCH64_R23, - DWARF_REG_AARCH64_R24, - DWARF_REG_AARCH64_R25, - DWARF_REG_AARCH64_R26, - DWARF_REG_AARCH64_R27, - DWARF_REG_AARCH64_R28, - DWARF_REG_AARCH64_R29, - DWARF_REG_AARCH64_R30, - DWARF_REG_AARCH64_R31, - DWARF_REG_AARCH64_ZR, // 32 - DWARF_REG_AARCH64_SP, // 33 - DWARF_REG_AARCH64_V0, // 34 - DWARF_REG_AARCH64_V1, // ... - DWARF_REG_AARCH64_V2, - DWARF_REG_AARCH64_V3, - DWARF_REG_AARCH64_V4, - DWARF_REG_AARCH64_V5, - DWARF_REG_AARCH64_V6, - DWARF_REG_AARCH64_V7, - DWARF_REG_AARCH64_V8, - DWARF_REG_AARCH64_V9, - DWARF_REG_AARCH64_V10, - DWARF_REG_AARCH64_V11, - DWARF_REG_AARCH64_V12, - DWARF_REG_AARCH64_V13, - DWARF_REG_AARCH64_V14, - DWARF_REG_AARCH64_V15, - DWARF_REG_AARCH64_V16, - DWARF_REG_AARCH64_V17, - DWARF_REG_AARCH64_V18, - DWARF_REG_AARCH64_V19, - DWARF_REG_AARCH64_V20, - DWARF_REG_AARCH64_V21, - DWARF_REG_AARCH64_V22, - DWARF_REG_AARCH64_V23, - DWARF_REG_AARCH64_V24, - DWARF_REG_AARCH64_V25, - DWARF_REG_AARCH64_V26, - DWARF_REG_AARCH64_V27, - DWARF_REG_AARCH64_V28, - DWARF_REG_AARCH64_V29, - DWARF_REG_AARCH64_V30, - DWARF_REG_AARCH64_V31 // 65 + DwarfRegEncodingAArch64.R0.encoding, + DwarfRegEncodingAArch64.R1.encoding, + DwarfRegEncodingAArch64.R2.encoding, + DwarfRegEncodingAArch64.R3.encoding, + DwarfRegEncodingAArch64.R4.encoding, + DwarfRegEncodingAArch64.R5.encoding, + DwarfRegEncodingAArch64.R6.encoding, + DwarfRegEncodingAArch64.R7.encoding, + DwarfRegEncodingAArch64.R8.encoding, + DwarfRegEncodingAArch64.R9.encoding, + DwarfRegEncodingAArch64.R10.encoding, + DwarfRegEncodingAArch64.R11.encoding, + DwarfRegEncodingAArch64.R12.encoding, + DwarfRegEncodingAArch64.R13.encoding, + DwarfRegEncodingAArch64.R14.encoding, + DwarfRegEncodingAArch64.R15.encoding, + DwarfRegEncodingAArch64.R16.encoding, + DwarfRegEncodingAArch64.R17.encoding, + DwarfRegEncodingAArch64.R18.encoding, + DwarfRegEncodingAArch64.R19.encoding, + DwarfRegEncodingAArch64.R20.encoding, + DwarfRegEncodingAArch64.R21.encoding, + DwarfRegEncodingAArch64.R22.encoding, + DwarfRegEncodingAArch64.R23.encoding, + DwarfRegEncodingAArch64.R24.encoding, + DwarfRegEncodingAArch64.R25.encoding, + DwarfRegEncodingAArch64.R26.encoding, + DwarfRegEncodingAArch64.R27.encoding, + DwarfRegEncodingAArch64.R28.encoding, + DwarfRegEncodingAArch64.R29.encoding, + DwarfRegEncodingAArch64.R30.encoding, + DwarfRegEncodingAArch64.R31.encoding, + DwarfRegEncodingAArch64.ZR.encoding, + DwarfRegEncodingAArch64.SP.encoding, + DwarfRegEncodingAArch64.V0.encoding, + DwarfRegEncodingAArch64.V1.encoding, + DwarfRegEncodingAArch64.V2.encoding, + DwarfRegEncodingAArch64.V3.encoding, + DwarfRegEncodingAArch64.V4.encoding, + DwarfRegEncodingAArch64.V5.encoding, + DwarfRegEncodingAArch64.V6.encoding, + DwarfRegEncodingAArch64.V7.encoding, + DwarfRegEncodingAArch64.V8.encoding, + DwarfRegEncodingAArch64.V9.encoding, + DwarfRegEncodingAArch64.V10.encoding, + DwarfRegEncodingAArch64.V11.encoding, + DwarfRegEncodingAArch64.V12.encoding, + DwarfRegEncodingAArch64.V13.encoding, + DwarfRegEncodingAArch64.V14.encoding, + DwarfRegEncodingAArch64.V15.encoding, + DwarfRegEncodingAArch64.V16.encoding, + DwarfRegEncodingAArch64.V17.encoding, + DwarfRegEncodingAArch64.V18.encoding, + DwarfRegEncodingAArch64.V19.encoding, + DwarfRegEncodingAArch64.V20.encoding, + DwarfRegEncodingAArch64.V21.encoding, + DwarfRegEncodingAArch64.V22.encoding, + DwarfRegEncodingAArch64.V23.encoding, + DwarfRegEncodingAArch64.V24.encoding, + DwarfRegEncodingAArch64.V25.encoding, + DwarfRegEncodingAArch64.V26.encoding, + DwarfRegEncodingAArch64.V27.encoding, + DwarfRegEncodingAArch64.V28.encoding, + DwarfRegEncodingAArch64.V29.encoding, + DwarfRegEncodingAArch64.V30.encoding, + DwarfRegEncodingAArch64.V31.encoding, }; - // register numbers used by DWARF for X86_64 registers - private static final int DWARF_REG_X86_64_RAX = 0; - private static final int DWARF_REG_X86_64_RDX = 1; - private static final int DWARF_REG_X86_64_RCX = 2; - private static final int DWARF_REG_X86_64_RBX = 3; - private static final int DWARF_REG_X86_64_RSI = 4; - private static final int DWARF_REG_X86_64_RDI = 5; - private static final int DWARF_REG_X86_64_RBP = 6; - private static final int DWARF_REG_X86_64_RSP = 7; - private static final int DWARF_REG_X86_64_R8 = 8; - private static final int DWARF_REG_X86_64_R9 = 9; - private static final int DWARF_REG_X86_64_R10 = 10; - private static final int DWARF_REG_X86_64_R11 = 11; - private static final int DWARF_REG_X86_64_R12 = 12; - private static final int DWARF_REG_X86_64_R13 = 13; - private static final int DWARF_REG_X86_64_R14 = 14; - private static final int DWARF_REG_X86_64_R15 = 15; - private static final int DWARF_REG_X86_64_XMM0 = 17; + // register numbers used by DWARF for AMD64 registers + public enum DwarfRegEncodingAMD64 { + RAX(0), + RDX(1), + RCX(2), + RBX(3), + RSI(4), + RDI(5), + RBP(6), + RSP(7), + R8(8), + R9(9), + R10(10), + R11(11), + R12(12), + R13(13), + R14(14), + R15(15), + XMM0(17), + XMM1(18), + XMM2(19), + XMM3(20), + XMM4(21), + XMM5(22), + XMM6(23), + XMM7(24), + XMM8(25), + XMM9(26), + XMM10(27), + XMM11(28), + XMM12(29), + XMM13(30), + XMM14(31), + XMM15(32); + + public final int encoding; + + DwarfRegEncodingAMD64(int encoding) { + this.encoding = encoding; + } + } + // map from compiler X86_64 register indices to corresponding dwarf AMD64 register index private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = { - DWARF_REG_X86_64_RAX, // 0 - DWARF_REG_X86_64_RCX, // 1 - DWARF_REG_X86_64_RDX, - DWARF_REG_X86_64_RBX, - DWARF_REG_X86_64_RSP, - DWARF_REG_X86_64_RBP, - DWARF_REG_X86_64_RSI, - DWARF_REG_X86_64_RDI, - DWARF_REG_X86_64_R8, - DWARF_REG_X86_64_R9, - DWARF_REG_X86_64_R10, - DWARF_REG_X86_64_R11, - DWARF_REG_X86_64_R12, - DWARF_REG_X86_64_R13, - DWARF_REG_X86_64_R14, - DWARF_REG_X86_64_R15, // 15 - DWARF_REG_X86_64_XMM0, // 16 - DWARF_REG_X86_64_XMM0 + 1, - DWARF_REG_X86_64_XMM0 + 2, - DWARF_REG_X86_64_XMM0 + 3, - DWARF_REG_X86_64_XMM0 + 4, - DWARF_REG_X86_64_XMM0 + 5, - DWARF_REG_X86_64_XMM0 + 6, - DWARF_REG_X86_64_XMM0 + 7, - DWARF_REG_X86_64_XMM0 + 8, - DWARF_REG_X86_64_XMM0 + 9, - DWARF_REG_X86_64_XMM0 + 10, - DWARF_REG_X86_64_XMM0 + 11, - DWARF_REG_X86_64_XMM0 + 12, - DWARF_REG_X86_64_XMM0 + 13, - DWARF_REG_X86_64_XMM0 + 14, - DWARF_REG_X86_64_XMM0 + 15 + DwarfRegEncodingAMD64.RAX.encoding, + DwarfRegEncodingAMD64.RCX.encoding, + DwarfRegEncodingAMD64.RDX.encoding, + DwarfRegEncodingAMD64.RBX.encoding, + DwarfRegEncodingAMD64.RSP.encoding, + DwarfRegEncodingAMD64.RBP.encoding, + DwarfRegEncodingAMD64.RSI.encoding, + DwarfRegEncodingAMD64.RDI.encoding, + DwarfRegEncodingAMD64.R8.encoding, + DwarfRegEncodingAMD64.R9.encoding, + DwarfRegEncodingAMD64.R10.encoding, + DwarfRegEncodingAMD64.R11.encoding, + DwarfRegEncodingAMD64.R12.encoding, + DwarfRegEncodingAMD64.R13.encoding, + DwarfRegEncodingAMD64.R14.encoding, + DwarfRegEncodingAMD64.R15.encoding, + DwarfRegEncodingAMD64.XMM0.encoding, + DwarfRegEncodingAMD64.XMM1.encoding, + DwarfRegEncodingAMD64.XMM2.encoding, + DwarfRegEncodingAMD64.XMM3.encoding, + DwarfRegEncodingAMD64.XMM4.encoding, + DwarfRegEncodingAMD64.XMM5.encoding, + DwarfRegEncodingAMD64.XMM6.encoding, + DwarfRegEncodingAMD64.XMM7.encoding, + DwarfRegEncodingAMD64.XMM8.encoding, + DwarfRegEncodingAMD64.XMM9.encoding, + DwarfRegEncodingAMD64.XMM10.encoding, + DwarfRegEncodingAMD64.XMM11.encoding, + DwarfRegEncodingAMD64.XMM12.encoding, + DwarfRegEncodingAMD64.XMM13.encoding, + DwarfRegEncodingAMD64.XMM14.encoding, + DwarfRegEncodingAMD64.XMM15.encoding, }; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 3d6510010078..979a83f34941 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -94,6 +94,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; @@ -1766,7 +1767,7 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { for (int i = 0; i < size; i++) { NativeImageDebugLocalValueInfo thisLocal = (NativeImageDebugLocalValueInfo) localInfoList.get(i); NativeImageDebugLocalValueInfo thatLocal = (NativeImageDebugLocalValueInfo) that.localInfoList.get(i); - if (!thisLocal.sameAs(thatLocal)) { + if (!thisLocal.equals(thatLocal)) { return null; } } @@ -1985,6 +1986,40 @@ public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { this.value = value; } + @Override + public boolean equals(Object o) { + if (!(o instanceof NativeImageDebugLocalValueInfo)) { + return false; + } + NativeImageDebugLocalValueInfo that = (NativeImageDebugLocalValueInfo) o; + // values need to have the same name + if (!name.equals(that.name)) { + return false; + } + // values need to be for the same line + if (line != that.line) { + return false; + } + // location kinds must match + if (localKind != that.localKind) { + return false; + } + // locations must match + switch (localKind) { + case REGISTER: + case STACKSLOT: + case CONSTANT: + return value.equals(that.value); + default: + return true; + } + } + + @Override + public int hashCode() { + return Objects.hash(name, value) * 31 + line; + } + @Override public String name() { return name; @@ -2023,14 +2058,14 @@ public int line() { } @Override - public String valueString() { + public String toString() { switch (localKind) { case REGISTER: return "reg[" + regIndex() + "]"; case STACKSLOT: return "stack[" + stackSlot() + "]"; case CONSTANT: - return (constantValue() != null ? constantValue().toValueString() : "null"); + return "constant[" + (constantValue() != null ? constantValue().toValueString() : "null") + "]"; default: return "-"; } @@ -2055,25 +2090,6 @@ public int stackSlot() { public JavaConstant constantValue() { return ((NativeImageDebugConstantValue) value).getConstant(); } - - public boolean sameAs(NativeImageDebugLocalValueInfo that) { - if (localKind == that.localKind) { - switch (localKind) { - case REGISTER: - return regIndex() != that.regIndex(); - case STACKSLOT: - return stackSlot() != that.stackSlot(); - case CONSTANT: { - Constant thisValue = constantValue(); - Constant thatValue = that.constantValue(); - return (thisValue == thatValue || (thisValue != null && thisValue.equals(thatValue))); - } - case UNDEFINED: - return true; - } - } - return false; - } } public class NativeImageDebugLocalValue { @@ -2093,6 +2109,20 @@ public class NativeImageDebugRegisterValue extends NativeImageDebugLocalValue { public int getNumber() { return number; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof NativeImageDebugRegisterValue)) { + return false; + } + NativeImageDebugRegisterValue that = (NativeImageDebugRegisterValue) o; + return number == that.number; + } + + @Override + public int hashCode() { + return number * 31; + } } public class NativeImageDebugStackValue extends NativeImageDebugLocalValue { @@ -2109,6 +2139,20 @@ public class NativeImageDebugStackValue extends NativeImageDebugLocalValue { public int getOffset() { return offset; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof NativeImageDebugStackValue)) { + return false; + } + NativeImageDebugStackValue that = (NativeImageDebugStackValue) o; + return offset == that.offset; + } + + @Override + public int hashCode() { + return offset * 31; + } } public class NativeImageDebugConstantValue extends NativeImageDebugLocalValue { @@ -2121,6 +2165,20 @@ public class NativeImageDebugConstantValue extends NativeImageDebugLocalValue { public JavaConstant getConstant() { return value; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof NativeImageDebugConstantValue)) { + return false; + } + NativeImageDebugConstantValue that = (NativeImageDebugConstantValue) o; + return value .equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } } /** diff --git a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java index ab49f6fce3e2..6be5749ef573 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -92,8 +92,8 @@ public static void main(String[] args) { inlineCallChain(); noInlineThis(); inlineFrom(); - noInlineManyArgs(0, 1, 2, 3, 4, 5, 6, 7, 8, - 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F); + noInlineManyArgs(0, 1, 2, 3, true, 5, 6, 7, 8, 9, + 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F,10.125D, false, 12.375F); System.exit(0); } @@ -182,26 +182,30 @@ private static void inlineTailRecursion(int n) { } @NeverInline("For testing purposes") - private static void noInlineManyArgs(int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, - float f0, float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8) { + private static void noInlineManyArgs(int i0, int i1, int i2, int i3, boolean b4, int i5, int i6, long l7, int i8, long l9, + float f0, float f1, float f2, float f3, double d4, float f5, float f6, float f7, float f8, double d9, boolean b10, float f11) { System.out.println("i0 = " + i0); System.out.println("i1 = " + i1); System.out.println("i2 = " + i2); System.out.println("i3 = " + i3); - System.out.println("i4 = " + i4); + System.out.println("b4 = " + b4); System.out.println("i5 = " + i5); System.out.println("i6 = " + i6); - System.out.println("i7 = " + i7); + System.out.println("i7 = " + l7); System.out.println("i8 = " + i8); + System.out.println("l9 = " + l9); System.out.println("f0 = " + f0); System.out.println("f1 = " + f1); System.out.println("f2 = " + f2); System.out.println("f3 = " + f3); - System.out.println("f4 = " + f4); + System.out.println("d4 = " + d4); System.out.println("f5 = " + f5); System.out.println("f6 = " + f6); System.out.println("f7 = " + f7); System.out.println("f8 = " + f8); + System.out.println("d9 = " + d9); + System.out.println("b10 = " + b10); + System.out.println("f11 = " + f11); } } From 4b28054a9fdb82d965e594154dcbf05e64963178 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 3 May 2022 16:25:53 +0100 Subject: [PATCH 12/22] Review rework complete writing of locations to include object constants. --- .../debuginfo/DebugInfoProvider.java | 2 + .../objectfile/elf/dwarf/DwarfDebugInfo.java | 1 + .../elf/dwarf/DwarfInfoSectionImpl.java | 39 +--- .../elf/dwarf/DwarfLocSectionImpl.java | 214 +++++++++--------- .../elf/dwarf/DwarfSectionImpl.java | 88 ++++++- .../oracle/svm/hosted/image/NativeImage.java | 2 +- .../image/NativeImageDebugInfoProvider.java | 74 ++++-- .../com.oracle.svm.test/src/hello/Hello.java | 2 +- 8 files changed, 264 insertions(+), 158 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 6aee548f17ef..7619c6140560 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -374,6 +374,8 @@ enum LocalKind { int stackSlot(); + long heapOffset(); + JavaConstant constantValue(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index d9d438cd891a..500e1c4172ab 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -285,6 +285,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte DW_OP_bregx = (byte) 0x92; public static final byte DW_OP_push_object_address = (byte) 0x97; public static final byte DW_OP_implicit_value = (byte) 0x9e; + public static final byte DW_OP_stack_value = (byte) 0x9f; /* Register constants for AArch64. */ public static final byte rheapbase_aarch64 = (byte) 27; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index daf415cfb65e..51b6770e54d9 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -33,6 +33,7 @@ import java.util.List; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.PrimitiveConstant; import org.graalvm.compiler.debug.DebugContext; @@ -1063,44 +1064,10 @@ private int writeStaticFieldLocation(DebugContext context, ClassEntry classEntry /* Field offset needs to be relocated relative to static primitive or static object base. */ int offset = fieldEntry.getOffset(); log(context, " [0x%08x] location heapbase + 0x%x (%s)", pos, offset, (fieldEntry.getValueType().isPrimitive() ? "primitive" : "object")); - pos = writeHeapLocation(offset, buffer, pos); + pos = writeHeapLocationExprLoc(offset, buffer, pos); return pos; } - private int writeHeapLocation(int offset, byte[] buffer, int p) { - int pos = p; - if (dwarfSections.useHeapBase()) { - /* Write a location rebasing the offset relative to the heapbase register. */ - byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); - /* - * We have to size the DWARF expression by writing it to the scratch buffer so we can - * write its size as a ULEB before the expression itself. - */ - int size = putByte(regOp, scratch, 0) + putSLEB(offset, scratch, 0); - if (buffer == null) { - /* Add ULEB size to the expression size. */ - return pos + putULEB(size, scratch, 0) + size; - } else { - /* Write the size and expression into the output buffer. */ - pos = putULEB(size, buffer, pos); - pos = putByte(regOp, buffer, pos); - return putSLEB(offset, buffer, pos); - } - } else { - /* Write a relocatable address relative to the heap section start. */ - byte regOp = DwarfDebugInfo.DW_OP_addr; - int size = 9; - /* Write the size and expression into the output buffer. */ - if (buffer == null) { - return pos + putULEB(size, scratch, 0) + size; - } else { - pos = putULEB(size, buffer, pos); - pos = putByte(regOp, buffer, pos); - return putRelocatableHeapOffset(offset, buffer, pos); - } - } - } - private int writeArrayTypes(DebugContext context, byte[] buffer, int pos) { log(context, " [0x%08x] array classes", pos); return getTypes().filter(TypeEntry::isArray).reduce(pos, @@ -1423,7 +1390,7 @@ private int writeMethodLocalLocation(DebugContext context, Range range, DebugLoc case CONSTANT: JavaConstant constant = value.constantValue(); // can only handle primitive or null constants just now - if (constant instanceof PrimitiveConstant || constant.isNull()) { + if (constant instanceof PrimitiveConstant || constant.getJavaKind() == JavaKind.Object) { localValues.add(value); } break; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index 3d5de94b8acf..3d9546e4202d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -246,8 +246,8 @@ private int writeVarLocations(DebugContext context, DebugLocalInfo local, long b pos = writePrimitiveConstantLocation(context, value.constantValue(), buffer, pos); } else if (constant.isNull()) { pos = writeNullConstantLocation(context, value.constantValue(), buffer, pos); - } else { - assert false : "Should not reach here!"; + } else { + pos = writeObjectConstantLocation(context, value.constantValue(), value.heapOffset(), buffer, pos); } break; default: @@ -353,7 +353,7 @@ private int writePrimitiveConstantLocation(DebugContext context, JavaConstant co pos += putShort((short) byteCount, scratch, 0); pos += putByte(op, scratch, 0); pos += putULEB(dataByteCount, scratch, 0); - + if (dataByteCount == 1) { if (kind == JavaKind.Boolean) { pos += putByte((byte) (constant.asBoolean() ? 1 : 0), scratch, 0); @@ -388,7 +388,7 @@ private int writePrimitiveConstantLocation(DebugContext context, JavaConstant co long l = (kind == JavaKind.Long ? constant.asLong() : Double.doubleToRawLongBits(constant.asDouble())); pos = putLong(l, buffer, pos); } - verboseLog(context, " [0x%08x] CONSTANT %s", pos, constant.toValueString()); + verboseLog(context, " [0x%08x] CONSTANT (primitive) %s", pos, constant.toValueString()); } return pos; } @@ -410,11 +410,19 @@ private int writeNullConstantLocation(DebugContext context, JavaConstant constan pos = putByte(op, buffer, pos); pos = putULEB(dataByteCount, buffer, pos); pos = writeAttrData8(0, buffer, pos); - verboseLog(context, " [0x%08x] CONSTANT %s", pos, constant.toValueString()); + verboseLog(context, " [0x%08x] CONSTANT (null) %s", pos, constant.toValueString()); } return pos; } + private int writeObjectConstantLocation(DebugContext context, JavaConstant constant, long heapOffset, byte[] buffer, int p) { + assert constant.getJavaKind() == JavaKind.Object && !constant.isNull(); + int pos = p; + pos = writeHeapLocationLocList(heapOffset, buffer, pos); + verboseLog(context, " [0x%08x] CONSTANT (object) %s", pos, constant.toValueString()); + return pos; + } + // auxiliary class used to collect per-range locations for a given local // merging adjacent ranges with the same location static class LocalValueExtent { @@ -600,72 +608,72 @@ public enum DwarfRegEncodingAArch64 { // map from compiler AArch64 register indices to corresponding dwarf AArch64 register index private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = { - DwarfRegEncodingAArch64.R0.encoding, - DwarfRegEncodingAArch64.R1.encoding, - DwarfRegEncodingAArch64.R2.encoding, - DwarfRegEncodingAArch64.R3.encoding, - DwarfRegEncodingAArch64.R4.encoding, - DwarfRegEncodingAArch64.R5.encoding, - DwarfRegEncodingAArch64.R6.encoding, - DwarfRegEncodingAArch64.R7.encoding, - DwarfRegEncodingAArch64.R8.encoding, - DwarfRegEncodingAArch64.R9.encoding, - DwarfRegEncodingAArch64.R10.encoding, - DwarfRegEncodingAArch64.R11.encoding, - DwarfRegEncodingAArch64.R12.encoding, - DwarfRegEncodingAArch64.R13.encoding, - DwarfRegEncodingAArch64.R14.encoding, - DwarfRegEncodingAArch64.R15.encoding, - DwarfRegEncodingAArch64.R16.encoding, - DwarfRegEncodingAArch64.R17.encoding, - DwarfRegEncodingAArch64.R18.encoding, - DwarfRegEncodingAArch64.R19.encoding, - DwarfRegEncodingAArch64.R20.encoding, - DwarfRegEncodingAArch64.R21.encoding, - DwarfRegEncodingAArch64.R22.encoding, - DwarfRegEncodingAArch64.R23.encoding, - DwarfRegEncodingAArch64.R24.encoding, - DwarfRegEncodingAArch64.R25.encoding, - DwarfRegEncodingAArch64.R26.encoding, - DwarfRegEncodingAArch64.R27.encoding, - DwarfRegEncodingAArch64.R28.encoding, - DwarfRegEncodingAArch64.R29.encoding, - DwarfRegEncodingAArch64.R30.encoding, - DwarfRegEncodingAArch64.R31.encoding, - DwarfRegEncodingAArch64.ZR.encoding, - DwarfRegEncodingAArch64.SP.encoding, - DwarfRegEncodingAArch64.V0.encoding, - DwarfRegEncodingAArch64.V1.encoding, - DwarfRegEncodingAArch64.V2.encoding, - DwarfRegEncodingAArch64.V3.encoding, - DwarfRegEncodingAArch64.V4.encoding, - DwarfRegEncodingAArch64.V5.encoding, - DwarfRegEncodingAArch64.V6.encoding, - DwarfRegEncodingAArch64.V7.encoding, - DwarfRegEncodingAArch64.V8.encoding, - DwarfRegEncodingAArch64.V9.encoding, - DwarfRegEncodingAArch64.V10.encoding, - DwarfRegEncodingAArch64.V11.encoding, - DwarfRegEncodingAArch64.V12.encoding, - DwarfRegEncodingAArch64.V13.encoding, - DwarfRegEncodingAArch64.V14.encoding, - DwarfRegEncodingAArch64.V15.encoding, - DwarfRegEncodingAArch64.V16.encoding, - DwarfRegEncodingAArch64.V17.encoding, - DwarfRegEncodingAArch64.V18.encoding, - DwarfRegEncodingAArch64.V19.encoding, - DwarfRegEncodingAArch64.V20.encoding, - DwarfRegEncodingAArch64.V21.encoding, - DwarfRegEncodingAArch64.V22.encoding, - DwarfRegEncodingAArch64.V23.encoding, - DwarfRegEncodingAArch64.V24.encoding, - DwarfRegEncodingAArch64.V25.encoding, - DwarfRegEncodingAArch64.V26.encoding, - DwarfRegEncodingAArch64.V27.encoding, - DwarfRegEncodingAArch64.V28.encoding, - DwarfRegEncodingAArch64.V29.encoding, - DwarfRegEncodingAArch64.V30.encoding, - DwarfRegEncodingAArch64.V31.encoding, + DwarfRegEncodingAArch64.R0.encoding, + DwarfRegEncodingAArch64.R1.encoding, + DwarfRegEncodingAArch64.R2.encoding, + DwarfRegEncodingAArch64.R3.encoding, + DwarfRegEncodingAArch64.R4.encoding, + DwarfRegEncodingAArch64.R5.encoding, + DwarfRegEncodingAArch64.R6.encoding, + DwarfRegEncodingAArch64.R7.encoding, + DwarfRegEncodingAArch64.R8.encoding, + DwarfRegEncodingAArch64.R9.encoding, + DwarfRegEncodingAArch64.R10.encoding, + DwarfRegEncodingAArch64.R11.encoding, + DwarfRegEncodingAArch64.R12.encoding, + DwarfRegEncodingAArch64.R13.encoding, + DwarfRegEncodingAArch64.R14.encoding, + DwarfRegEncodingAArch64.R15.encoding, + DwarfRegEncodingAArch64.R16.encoding, + DwarfRegEncodingAArch64.R17.encoding, + DwarfRegEncodingAArch64.R18.encoding, + DwarfRegEncodingAArch64.R19.encoding, + DwarfRegEncodingAArch64.R20.encoding, + DwarfRegEncodingAArch64.R21.encoding, + DwarfRegEncodingAArch64.R22.encoding, + DwarfRegEncodingAArch64.R23.encoding, + DwarfRegEncodingAArch64.R24.encoding, + DwarfRegEncodingAArch64.R25.encoding, + DwarfRegEncodingAArch64.R26.encoding, + DwarfRegEncodingAArch64.R27.encoding, + DwarfRegEncodingAArch64.R28.encoding, + DwarfRegEncodingAArch64.R29.encoding, + DwarfRegEncodingAArch64.R30.encoding, + DwarfRegEncodingAArch64.R31.encoding, + DwarfRegEncodingAArch64.ZR.encoding, + DwarfRegEncodingAArch64.SP.encoding, + DwarfRegEncodingAArch64.V0.encoding, + DwarfRegEncodingAArch64.V1.encoding, + DwarfRegEncodingAArch64.V2.encoding, + DwarfRegEncodingAArch64.V3.encoding, + DwarfRegEncodingAArch64.V4.encoding, + DwarfRegEncodingAArch64.V5.encoding, + DwarfRegEncodingAArch64.V6.encoding, + DwarfRegEncodingAArch64.V7.encoding, + DwarfRegEncodingAArch64.V8.encoding, + DwarfRegEncodingAArch64.V9.encoding, + DwarfRegEncodingAArch64.V10.encoding, + DwarfRegEncodingAArch64.V11.encoding, + DwarfRegEncodingAArch64.V12.encoding, + DwarfRegEncodingAArch64.V13.encoding, + DwarfRegEncodingAArch64.V14.encoding, + DwarfRegEncodingAArch64.V15.encoding, + DwarfRegEncodingAArch64.V16.encoding, + DwarfRegEncodingAArch64.V17.encoding, + DwarfRegEncodingAArch64.V18.encoding, + DwarfRegEncodingAArch64.V19.encoding, + DwarfRegEncodingAArch64.V20.encoding, + DwarfRegEncodingAArch64.V21.encoding, + DwarfRegEncodingAArch64.V22.encoding, + DwarfRegEncodingAArch64.V23.encoding, + DwarfRegEncodingAArch64.V24.encoding, + DwarfRegEncodingAArch64.V25.encoding, + DwarfRegEncodingAArch64.V26.encoding, + DwarfRegEncodingAArch64.V27.encoding, + DwarfRegEncodingAArch64.V28.encoding, + DwarfRegEncodingAArch64.V29.encoding, + DwarfRegEncodingAArch64.V30.encoding, + DwarfRegEncodingAArch64.V31.encoding, }; // register numbers used by DWARF for AMD64 registers @@ -712,37 +720,37 @@ public enum DwarfRegEncodingAMD64 { // map from compiler X86_64 register indices to corresponding dwarf AMD64 register index private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = { - DwarfRegEncodingAMD64.RAX.encoding, - DwarfRegEncodingAMD64.RCX.encoding, - DwarfRegEncodingAMD64.RDX.encoding, - DwarfRegEncodingAMD64.RBX.encoding, - DwarfRegEncodingAMD64.RSP.encoding, - DwarfRegEncodingAMD64.RBP.encoding, - DwarfRegEncodingAMD64.RSI.encoding, - DwarfRegEncodingAMD64.RDI.encoding, - DwarfRegEncodingAMD64.R8.encoding, - DwarfRegEncodingAMD64.R9.encoding, - DwarfRegEncodingAMD64.R10.encoding, - DwarfRegEncodingAMD64.R11.encoding, - DwarfRegEncodingAMD64.R12.encoding, - DwarfRegEncodingAMD64.R13.encoding, - DwarfRegEncodingAMD64.R14.encoding, - DwarfRegEncodingAMD64.R15.encoding, - DwarfRegEncodingAMD64.XMM0.encoding, - DwarfRegEncodingAMD64.XMM1.encoding, - DwarfRegEncodingAMD64.XMM2.encoding, - DwarfRegEncodingAMD64.XMM3.encoding, - DwarfRegEncodingAMD64.XMM4.encoding, - DwarfRegEncodingAMD64.XMM5.encoding, - DwarfRegEncodingAMD64.XMM6.encoding, - DwarfRegEncodingAMD64.XMM7.encoding, - DwarfRegEncodingAMD64.XMM8.encoding, - DwarfRegEncodingAMD64.XMM9.encoding, - DwarfRegEncodingAMD64.XMM10.encoding, - DwarfRegEncodingAMD64.XMM11.encoding, - DwarfRegEncodingAMD64.XMM12.encoding, - DwarfRegEncodingAMD64.XMM13.encoding, - DwarfRegEncodingAMD64.XMM14.encoding, - DwarfRegEncodingAMD64.XMM15.encoding, + DwarfRegEncodingAMD64.RAX.encoding, + DwarfRegEncodingAMD64.RCX.encoding, + DwarfRegEncodingAMD64.RDX.encoding, + DwarfRegEncodingAMD64.RBX.encoding, + DwarfRegEncodingAMD64.RSP.encoding, + DwarfRegEncodingAMD64.RBP.encoding, + DwarfRegEncodingAMD64.RSI.encoding, + DwarfRegEncodingAMD64.RDI.encoding, + DwarfRegEncodingAMD64.R8.encoding, + DwarfRegEncodingAMD64.R9.encoding, + DwarfRegEncodingAMD64.R10.encoding, + DwarfRegEncodingAMD64.R11.encoding, + DwarfRegEncodingAMD64.R12.encoding, + DwarfRegEncodingAMD64.R13.encoding, + DwarfRegEncodingAMD64.R14.encoding, + DwarfRegEncodingAMD64.R15.encoding, + DwarfRegEncodingAMD64.XMM0.encoding, + DwarfRegEncodingAMD64.XMM1.encoding, + DwarfRegEncodingAMD64.XMM2.encoding, + DwarfRegEncodingAMD64.XMM3.encoding, + DwarfRegEncodingAMD64.XMM4.encoding, + DwarfRegEncodingAMD64.XMM5.encoding, + DwarfRegEncodingAMD64.XMM6.encoding, + DwarfRegEncodingAMD64.XMM7.encoding, + DwarfRegEncodingAMD64.XMM8.encoding, + DwarfRegEncodingAMD64.XMM9.encoding, + DwarfRegEncodingAMD64.XMM10.encoding, + DwarfRegEncodingAMD64.XMM11.encoding, + DwarfRegEncodingAMD64.XMM12.encoding, + DwarfRegEncodingAMD64.XMM13.encoding, + DwarfRegEncodingAMD64.XMM14.encoding, + DwarfRegEncodingAMD64.XMM15.encoding, }; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 504a19ad6edf..ebb7287b2a31 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -48,6 +48,8 @@ import java.util.Set; import java.util.stream.Stream; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_stack_value; + /** * A class from which all DWARF debug sections inherit providing common behaviours. */ @@ -399,6 +401,90 @@ protected int writeAttrNull(byte[] buffer, int pos) { } } + /* + * Write a heap location expression preceded by a ULEB block size count as appropriate for an + * attribute with FORM exprloc. + */ + protected int writeHeapLocationExprLoc(long offset, byte[] buffer, int p) { + int pos = p; + /* + * We have to size the DWARF location expression by writing it to the scratch buffer so we + * can write its size as a ULEB before the expression itself. + */ + int size = writeHeapLocation(offset, null, 0); + + if (buffer == null) { + /* Add ULEB size to the expression size. */ + return pos + putULEB(size, scratch, 0) + size; + } else { + /* Write the size and expression into the output buffer. */ + pos = putULEB(size, buffer, pos); + return writeHeapLocation(offset, buffer, pos); + } + } + + /* + * Write a heap location expression preceded by a ULEB block size count as appropriate for + * location list in the debug_loc section. + */ + protected int writeHeapLocationLocList(long offset, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putShort((short) 0, scratch, 0); + pos += writeHeapLocation(offset, buffer, 0); + return pos + putByte(DW_OP_stack_value, scratch, 0); + } else { + short len = 0; + int lenPos = pos; + // write dummy length + pos = putShort(len, buffer, pos); + pos = writeHeapLocation(offset, buffer, pos); + pos = putByte(DW_OP_stack_value, buffer, pos); + // backpatch length + len = (short) (pos - (lenPos + 2)); + putShort(len, buffer, lenPos); + return pos; + } + } + + /* + * Write a bare heap location expression as appropriate for a single location. + */ + protected int writeHeapLocation(long offset, byte[] buffer, int p) { + if (dwarfSections.useHeapBase()) { + return writeHeapLocationBaseRelative(offset, buffer, p); + } else { + return writeHeapLocationRelocatable(offset, buffer, p); + } + } + + private int writeHeapLocationBaseRelative(long offset, byte[] buffer, int p) { + int pos = p; + /* Write a location rebasing the offset relative to the heapbase register. */ + byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); + if (buffer == null) { + /* Add ULEB size to the expression size. */ + pos += putByte(regOp, scratch, 0); + return pos + putSLEB(offset, scratch, 0); + } else { + /* Write the size and expression into the output buffer. */ + pos = putByte(regOp, buffer, pos); + return putSLEB(offset, buffer, pos); + } + } + + private int writeHeapLocationRelocatable(long offset, byte[] buffer, int p) { + int pos = p; + /* Write a relocatable address relative to the heap section start. */ + byte regOp = DwarfDebugInfo.DW_OP_addr; + if (buffer == null) { + return pos + 9; + } else { + pos = putByte(regOp, buffer, pos); + return putRelocatableHeapOffset(offset, buffer, pos); + } + } + protected static String formatValue(DebugInfoProvider.DebugLocalValueInfo value) { switch (value.localKind()) { case REGISTER: @@ -406,7 +492,7 @@ protected static String formatValue(DebugInfoProvider.DebugLocalValueInfo value) case STACKSLOT: return "STACK:" + value.stackSlot(); case CONSTANT: - return "CONST:" + value.constantValue(); + return "CONST:" + value.constantValue() + "[" + Long.toHexString(value.heapOffset()) + "]"; case UNDEFINED: default: return "-"; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 401b75145327..5d654c86431d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -471,7 +471,7 @@ public void build(String imageName, DebugContext debug) { Timer timer = TimerCollection.singleton().get(TimerCollection.Registry.DEBUG_INFO); try (Timer.StopTimer t = timer.start()) { ImageSingletons.add(SourceManager.class, new SourceManager()); - DebugInfoProvider provider = new NativeImageDebugInfoProvider(debug, codeCache, heap); + DebugInfoProvider provider = new NativeImageDebugInfoProvider(debug, codeCache, heap, metaAccess); objectFile.installDebugInfo(provider); } ProgressReporter.singleton().setDebugInfoTimer(timer); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 979a83f34941..c8fbf0062227 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -42,6 +42,7 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.image.ImageHeapPartition; +import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.annotation.CustomSubstitutionType; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; @@ -52,6 +53,7 @@ import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedInstanceClass; import com.oracle.svm.hosted.meta.HostedInterface; +import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedPrimitiveType; import com.oracle.svm.hosted.meta.HostedType; @@ -67,7 +69,6 @@ import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; @@ -86,6 +87,7 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.word.WordBase; import java.lang.reflect.Modifier; import java.nio.file.Path; @@ -111,7 +113,7 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { private final DebugContext debugContext; private final NativeImageCodeCache codeCache; - @SuppressWarnings("unused") private final NativeImageHeap heap; + private final NativeImageHeap heap; boolean useHeapBase; int compressShift; int tagsMask; @@ -123,9 +125,7 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { private final Set allOverrides; HostedType wordBaseType; - private static final String GRAAL_WORDBASE_TYPE_NAME = "Lorg/graalvm/word/WordBase;"; - - NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap) { + NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap, HostedMetaAccess metaAccess) { super(); this.debugContext = debugContext; this.codeCache = codeCache; @@ -154,7 +154,7 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { .flatMap(m -> Arrays.stream(m.getImplementations()) .filter(Predicate.not(m::equals))) .collect(Collectors.toSet()); - wordBaseType = heap.getUniverse().getTypes().stream().filter(type -> type.getName().equals(GRAAL_WORDBASE_TYPE_NAME)).findFirst().get(); + wordBaseType = metaAccess.lookupJavaType(WordBase.class); } @Override @@ -208,6 +208,27 @@ static ObjectLayout getObjectLayout() { return ConfigurationValues.getObjectLayout(); } + /** + * Return the offset into the initial heap at which the object identified by constant is located + * or -1 if the object is not present in the initial heap. + * + * @param constant must have JavaKind Object and must be non-null. + * + * @return the offset into the initial heap at which the object identified by constant is + * located or -1 if the object is not present in the initial heap. + */ + public long objectOffset(JavaConstant constant) { + assert constant.getJavaKind() == JavaKind.Object && !constant.isNull() : "invalid constant for object offset lookup"; + if (constant instanceof SubstrateObjectConstant) { + Object object = SubstrateObjectConstant.asObject(constant); + ObjectInfo objectInfo = heap.getObjectInfo(object); + if (objectInfo != null) { + return objectInfo.getAddress(); + } + } + return -1; + } + /* * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to * getSourceFilename to the wrapped class so for consistency we need to do type names and path @@ -1954,13 +1975,19 @@ public class NativeImageDebugLocalValueInfo implements DebugLocalValueInfo { this.localKind = LocalKind.STACKSLOT; this.value = new NativeImageDebugStackValue((StackSlot) value, framesize); } else if (value instanceof JavaConstant) { - // for now we can only handle primiitve constants - if (value instanceof PrimitiveConstant || ((JavaConstant) value).isNull()) { + JavaConstant constant = (JavaConstant) value; + if (constant instanceof PrimitiveConstant || constant.isNull()) { this.localKind = LocalKind.CONSTANT; - this.value = new NativeImageDebugConstantValue((JavaConstant) value); + this.value = new NativeImageDebugConstantValue(constant); } else { - this.localKind = LocalKind.UNDEFINED; - this.value = null; + long heapOffset = objectOffset(constant); + if (heapOffset >= 0) { + this.localKind = LocalKind.CONSTANT; + this.value = new NativeImageDebugConstantValue(constant, heapOffset); + } else { + this.localKind = LocalKind.UNDEFINED; + this.value = null; + } } } else { this.localKind = LocalKind.UNDEFINED; @@ -2086,6 +2113,11 @@ public int stackSlot() { return ((NativeImageDebugStackValue) value).getOffset(); } + @Override + public long heapOffset() { + return ((NativeImageDebugConstantValue) value).getHeapOffset(); + } + @Override public JavaConstant constantValue() { return ((NativeImageDebugConstantValue) value).getConstant(); @@ -2112,7 +2144,7 @@ public int getNumber() { @Override public boolean equals(Object o) { - if (!(o instanceof NativeImageDebugRegisterValue)) { + if (!(o instanceof NativeImageDebugRegisterValue)) { return false; } NativeImageDebugRegisterValue that = (NativeImageDebugRegisterValue) o; @@ -2142,7 +2174,7 @@ public int getOffset() { @Override public boolean equals(Object o) { - if (!(o instanceof NativeImageDebugStackValue)) { + if (!(o instanceof NativeImageDebugStackValue)) { return false; } NativeImageDebugStackValue that = (NativeImageDebugStackValue) o; @@ -2157,27 +2189,37 @@ public int hashCode() { public class NativeImageDebugConstantValue extends NativeImageDebugLocalValue { private JavaConstant value; + private long heapoffset; NativeImageDebugConstantValue(JavaConstant value) { + this(value, -1); + } + + NativeImageDebugConstantValue(JavaConstant value, long heapoffset) { this.value = value; + this.heapoffset = heapoffset; } public JavaConstant getConstant() { return value; } + public long getHeapOffset() { + return heapoffset; + } + @Override public boolean equals(Object o) { - if (!(o instanceof NativeImageDebugConstantValue)) { + if (!(o instanceof NativeImageDebugConstantValue)) { return false; } NativeImageDebugConstantValue that = (NativeImageDebugConstantValue) o; - return value .equals(that.value); + return heapoffset == that.heapoffset && value.equals(that.value); } @Override public int hashCode() { - return Objects.hash(value); + return Objects.hash(value) * 31 + (int) heapoffset; } } diff --git a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java index 6be5749ef573..2ea2a1baff7f 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -93,7 +93,7 @@ public static void main(String[] args) { noInlineThis(); inlineFrom(); noInlineManyArgs(0, 1, 2, 3, true, 5, 6, 7, 8, 9, - 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F,10.125D, false, 12.375F); + 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F, 10.125D, false, 12.375F); System.exit(0); } From 8d75553928fa1251ca739802607eb1efa25f6a2c Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Wed, 4 May 2022 15:16:19 +0100 Subject: [PATCH 13/22] Extend debuginfo test to check inline constants --- substratevm/mx.substratevm/mx_substratevm.py | 4 +- substratevm/mx.substratevm/testhello.py | 163 +++++++++++------- .../com.oracle.svm.test/src/hello/Hello.java | 21 +++ 3 files changed, 126 insertions(+), 62 deletions(-) diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 8116420a019f..09301a51f58d 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -838,12 +838,12 @@ def build_debug_test(extra_args): build_debug_test(['-H:+SpawnIsolates']) if mx.get_os() == 'linux' and not build_only: - os.environ.update({'debuginfotest.isolates' : 'yes'}) + os.environ.update({'debuginfotest_isolates' : 'yes'}) mx.run([os.environ.get('GDB_BIN', 'gdb'), '-ex', 'python "ISOLATES=True"', '-x', join(parent, 'mx.substratevm/testhello.py'), join(path, 'hello.hello')]) build_debug_test(['-H:-SpawnIsolates']) if mx.get_os() == 'linux' and not build_only: - os.environ.update({'debuginfotest.isolates' : 'no'}) + os.environ.update({'debuginfotest_isolates' : 'no'}) mx.run([os.environ.get('GDB_BIN', 'gdb'), '-ex', 'python "ISOLATES=False"', '-x', join(parent, 'mx.substratevm/testhello.py'), join(path, 'hello.hello')]) def _javac_image(native_image, path, args=None): diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 5604ff6dd2bf..4a9b4c4ae50d 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -156,7 +156,7 @@ def test(): can_print_data = True isolates = False - if os.environ.get('debuginfotest.isolates', 'no') == 'yes': + if os.environ.get('debuginfotest_isolates', 'no') == 'yes': isolates = True if isolates: @@ -496,6 +496,7 @@ def test(): r"%sstatic void inlineMee\(void\);"%spaces_pattern, r"%sstatic void inlineMixTo\(int\);"%spaces_pattern, r"%sstatic void inlineMoo\(void\);"%spaces_pattern, + r"%sstatic void inlineReceiveConstants\(byte, int, long, java\.lang\.String \*, float, double\);"%spaces_pattern, r"%sstatic void inlineTailRecursion\(int\);"%spaces_pattern, r"%sstatic void inlineTo\(int\);"%spaces_pattern, r"%spublic:"%spaces_pattern, @@ -504,6 +505,7 @@ def test(): r"%sstatic void noInlineFoo\(void\);"%spaces_pattern, r"%sstatic void noInlineHere\(int\);"%spaces_pattern, r"%sstatic void noInlineManyArgs\(int, int, int, int, boolean, int, int, long, int, long, float, float, float, float, double, float, float, float, float, double, boolean, float\);"%spaces_pattern, + r"%sstatic void noInlinePassConstants\(void\);"%spaces_pattern, r"%sstatic void noInlineTest\(void\);"%spaces_pattern, r"%sstatic void noInlineThis\(void\);"%spaces_pattern, r"}"] @@ -524,13 +526,13 @@ def test(): # list inlineIs and inlineA and check that the listing maps to the inlined code instead of the actual code, # although not ideal this is how GDB treats inlined code in C/C++ as well - rexp = [r"127%sinlineA\(\);"%spaces_pattern] + rexp = [r"128%sinlineA\(\);"%spaces_pattern] checker = Checker('list inlineIs', rexp) checker.check(execute("list inlineIs")) - rexp = [r'file: "hello/Hello\.java", line number: 132, symbol: "hello\.Hello::inlineA"', - r"132%snoInlineTest\(\);"%spaces_pattern, - r'file: "hello/Hello\.java", line number: 133, symbol: "hello\.Hello::inlineA"', - r"133%s}"%spaces_pattern] + rexp = [r'file: "hello/Hello\.java", line number: 133, symbol: "hello\.Hello::inlineA"', + r"133%snoInlineTest\(\);"%spaces_pattern, + r'file: "hello/Hello\.java", line number: 134, symbol: "hello\.Hello::inlineA"', + r"134%s}"%spaces_pattern] checker = Checker('list inlineA', rexp) checker.check(execute("list inlineA")) @@ -543,38 +545,38 @@ def test(): execute("continue") exec_string = execute("list") - rexp = [r"127%sinlineA\(\);"%spaces_pattern] + rexp = [r"128%sinlineA\(\);"%spaces_pattern] checker = Checker('hit break at inlineIs', rexp) checker.check(exec_string, skip_fails=False) execute("step") exec_string = execute("list") - rexp = [r"132%snoInlineTest\(\);"%spaces_pattern] + rexp = [r"133%snoInlineTest\(\);"%spaces_pattern] checker = Checker('step in inlineA', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 4") - rexp = [r"#0%shello\.Hello::inlineA \(\) at hello/Hello\.java:132"%spaces_pattern, - r"#1%shello\.Hello::inlineIs \(\) at hello/Hello\.java:127"%spaces_pattern, - r"#2%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:122"%spaces_pattern, + rexp = [r"#0%shello\.Hello::inlineA \(\) at hello/Hello\.java:133"%spaces_pattern, + r"#1%shello\.Hello::inlineIs \(\) at hello/Hello\.java:128"%spaces_pattern, + r"#2%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:123"%spaces_pattern, r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace inlineMee', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") exec_string = execute("break hello.Hello::noInlineTest") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 137\."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 138\."%(digits_pattern, address_pattern) checker = Checker('break noInlineTest', rexp) checker.check(exec_string, skip_fails=False) execute("continue") exec_string = execute("list") - rexp = r"137%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern + rexp = r"138%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern checker = Checker('hit breakpoint in noInlineTest', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 5") - rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:137"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:132"%(spaces_pattern, address_pattern), - r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:127"%(spaces_pattern), - r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:122"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:138"%(spaces_pattern), + r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:133"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:128"%(spaces_pattern), + r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:123"%(spaces_pattern), r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:93"%(spaces_pattern, address_pattern, arg_values_pattern)] checker = Checker('backtrace in inlineMethod', rexp) checker.check(exec_string, skip_fails=False) @@ -582,86 +584,86 @@ def test(): execute("delete breakpoints") # Set breakpoint at method with inline and not-inlined invocation in same line exec_string = execute("break hello.Hello::inlineFrom") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 143."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 144."%(digits_pattern, address_pattern) checker = Checker('break inlineFrom', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("info break 6") - rexp = [r"6%sbreakpoint%skeep%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:143"%(spaces_pattern, spaces_pattern, spaces_pattern, spaces_pattern, address_pattern)] + rexp = [r"6%sbreakpoint%skeep%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:144"%(spaces_pattern, spaces_pattern, spaces_pattern, spaces_pattern, address_pattern)] checker = Checker('info break inlineFrom', rexp) checker.check(exec_string) execute("delete breakpoints") exec_string = execute("break Hello.java:159") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 159\."%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:157', rexp) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 160\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:158', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineMixTo %s at hello/Hello\.java:159"%(spaces_pattern, arg_values_pattern), - r"#1%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#2%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#3%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#4%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#5%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#6%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#7%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#8%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#9%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#10%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:157"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#11%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:151"%(spaces_pattern, arg_values_pattern), - r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:143"%(spaces_pattern, address_pattern), + rexp = [r"#0%shello\.Hello::inlineMixTo %s at hello/Hello\.java:160"%(spaces_pattern, arg_values_pattern), + r"#1%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:158"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:158"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#6%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:158"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#7%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#8%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:158"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#9%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#10%s%s in hello\.Hello::inlineMixTo %s at hello/Hello\.java:158"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#11%shello\.Hello::noInlineHere\(int\) %s at hello/Hello\.java:152"%(spaces_pattern, arg_values_pattern), + r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:144"%(spaces_pattern, address_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:172") - rexp = r"Breakpoint %s at %s: Hello\.java:172\. \(2 locations\)"%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:172', rexp) + exec_string = execute("break Hello.java:173") + rexp = r"Breakpoint %s at %s: Hello\.java:173\. \(2 locations\)"%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:173', rexp) checker.check(exec_string) execute("continue") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:172"%(spaces_pattern, arg_values_pattern), - r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:170"%(spaces_pattern, arg_values_pattern), - r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:164"%(spaces_pattern, arg_values_pattern), - r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:145"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:173"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), + r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), + r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), + r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, arg_values_pattern), + r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:146"%(spaces_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:178") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 178\."%(digits_pattern, address_pattern) + exec_string = execute("break Hello.java:179") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 179\."%(digits_pattern, address_pattern) checker = Checker('break Hello.java:178', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 8") - rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:178"%(spaces_pattern, arg_values_pattern), - r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:181"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:146"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, arg_values_pattern), + r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:147"%(spaces_pattern), r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("break hello.Hello::noInlineManyArgs") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 187\."%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 188\."%(digits_pattern, address_pattern) checker = Checker('break hello.Hello::noInlineManyArgs', rexp) checker.check(exec_string) @@ -725,6 +727,47 @@ def test(): checker = Checker('info args 2', rexp) checker.check(exec_string) + execute("delete breakpoints"); + + exec_string = execute("break hello.Hello::inlineReceiveConstants") + rexp = r"Breakpoint %s at %s: hello\.Hello::inlineReceiveConstants\. \(%s locations\)"%(digits_pattern, address_pattern, digits_pattern) + checker = Checker('break hello.Hello::inlineReceiveConstants', rexp) + checker.check(exec_string) + + execute("continue") + + exec_string = execute("info args") + rexp =[r"b = 1 '\\001'", + r"i = 2", + r"l = 3", + r"s = %s"%(address_pattern), + r"f = 4", + r"d = 5"] + checker = Checker('info args 3', rexp) + checker.check(exec_string) + + execute("set print elements 10") + exec_string = execute("x/s s->value->data"); + execute("set print elements unlimited") + rexp=[r'%s:%s"stringtext"'%(address_pattern, spaces_pattern)]; + checker = Checker('x/s s->value->data', rexp) + checker.check(exec_string, skip_fails=True) + + exec_string = execute("next 3") + exec_string = execute("info locals") + rexp =[r"n = 6", + r"q = 20", + r"t = %s"%(address_pattern)] + checker = Checker('info locals 3', rexp) + checker.check(exec_string) + + execute("set print elements 11") + exec_string = execute("x/s t->value->data"); + execute("set print elements unlimited") + rexp=[r'%s:%s"stringtext!"'%(address_pattern, spaces_pattern)]; + checker = Checker('x/s t->value->data', rexp) + checker.check(exec_string, skip_fails=True) + print(execute("quit 0")) test() diff --git a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java index 2ea2a1baff7f..4ea5c8366924 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -94,6 +94,7 @@ public static void main(String[] args) { inlineFrom(); noInlineManyArgs(0, 1, 2, 3, true, 5, 6, 7, 8, 9, 0.0F, 1.125F, 2.25F, 3.375F, 4.5F, 5.625F, 6.75F, 7.875F, 9.0F, 10.125D, false, 12.375F); + noInlinePassConstants(); System.exit(0); } @@ -208,4 +209,24 @@ private static void noInlineManyArgs(int i0, int i1, int i2, int i3, boolean b4, System.out.println("f11 = " + f11); } + @NeverInline("For testing purposes") + private static void noInlinePassConstants() { + inlineReceiveConstants((byte) 1, 2, 3L, "stringtext", 4.0F, 5.0D); + } + + @AlwaysInline("For testing purposes") + private static void inlineReceiveConstants(byte b, int i, long l, String s, float f, double d) { + long n = i * l; + double q = f * d; + String t = s + "!"; + System.out.println(String.format("b = %d\n", b)); + System.out.println(String.format("i = %d\n", i)); + System.out.println(String.format("l = %d\n", l)); + System.out.println(String.format("s = %s\n", s)); + System.out.println(String.format("f = %g\n", f)); + System.out.println(String.format("d = %g\n", d)); + System.out.println(String.format("n = %d\n", n)); + System.out.println(String.format("q = %g\n", q)); + System.out.println(String.format("t = %s\n", t)); + } } From bb29da1335aa0e7279d288553c449b3b3bc7dda9 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 6 May 2022 15:00:06 +0100 Subject: [PATCH 14/22] Use OS-CPU specific calling convention for initial param reg mapping --- .../image/NativeImageDebugInfoProvider.java | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index c8fbf0062227..3aacdef2add2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -30,6 +30,7 @@ import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.svm.core.OS; import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.code.CompilationResultFrameTree.Builder; @@ -1827,7 +1828,7 @@ public NativeImageDebugLocationInfo split(int stackDecrement, int frameSize) { AArch64.v6, AArch64.v7 }; - static final Register[] AMD64_GPREG = { + static final Register[] AMD64_GPREG_LINUX = { AMD64.rdi, AMD64.rsi, AMD64.rdx, @@ -1835,7 +1836,7 @@ public NativeImageDebugLocationInfo split(int stackDecrement, int frameSize) { AMD64.r8, AMD64.r9 }; - static final Register[] AMD64_FREG = { + static final Register[] AMD64_FREG_LINUX = { AMD64.xmm0, AMD64.xmm1, AMD64.xmm2, @@ -1845,6 +1846,20 @@ public NativeImageDebugLocationInfo split(int stackDecrement, int frameSize) { AMD64.xmm6, AMD64.xmm7 }; + static final Register[] AMD64_GPREG_WINDOWS = { + AMD64.rdx, + AMD64.r8, + AMD64.r9, + AMD64.rdi, + AMD64.rsi, + AMD64.rcx + }; + static final Register[] AMD64_FREG_WINDOWS = { + AMD64.xmm0, + AMD64.xmm1, + AMD64.xmm2, + AMD64.xmm3 + }; class ParamLocationProducer { Register[] gpregs; @@ -1856,13 +1871,21 @@ class ParamLocationProducer { ParamLocationProducer(ResolvedJavaMethod method) { Architecture arch = ConfigurationValues.getTarget().arch; + assert arch instanceof AMD64 || arch instanceof AMD64 : "unexpected architecture"; + OS os = OS.getCurrent(); + assert os == OS.LINUX || os == OS.WINDOWS : "unexpected os"; if (arch instanceof AArch64) { + assert os == OS.LINUX : "unexpected os/architecture"; gpregs = AARCH64_GPREG; fregs = AARCH64_FREG; } else { - assert arch instanceof AMD64 : "must be"; - gpregs = AMD64_GPREG; - fregs = AMD64_FREG; + if (os == OS.LINUX) { + gpregs = AMD64_GPREG_LINUX; + fregs = AMD64_FREG_LINUX; + } else { + gpregs = AMD64_GPREG_WINDOWS; + fregs = AMD64_FREG_WINDOWS; + } } nextGPRegIdx = 0; nextFPregIdx = 0; From bb567ca62e95e386f6ef313ee0f56a9808dcbc61 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 6 May 2022 15:41:13 +0100 Subject: [PATCH 15/22] Document mappings from compiler register numbers to DWARF encodings. --- .../elf/dwarf/DwarfLocSectionImpl.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index 3d9546e4202d..aa65eb20fee9 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -530,7 +530,7 @@ private void initDwarfRegMap() { } } - // register numbers used by DWARF for AArch64 registers + // Register numbers used by DWARF for AArch64 registers. public enum DwarfRegEncodingAArch64 { R0(0), R1(1), @@ -606,7 +606,11 @@ public enum DwarfRegEncodingAArch64 { } } - // map from compiler AArch64 register indices to corresponding dwarf AArch64 register index + // Map from compiler AArch64 register numbers to corresponding DWARF AArch64 register encoding. + // Register numbers for compiler general purpose and float registers occupy index ranges 0-31 + // and 34-65 respectively. Table entries provided the corresponding number used by DWARF to + // identify the same register. Note that the table includes entries for ZR (32) and SP (33) + // even though we should not see those register numbers appearing in location values. private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = { DwarfRegEncodingAArch64.R0.encoding, DwarfRegEncodingAArch64.R1.encoding, @@ -676,7 +680,9 @@ public enum DwarfRegEncodingAArch64 { DwarfRegEncodingAArch64.V31.encoding, }; - // register numbers used by DWARF for AMD64 registers + // Register numbers used by DWARF for AMD64 registers. Note that the first 8 general + // purpose registers appear in a different order to that used by the compiler. For + // example the compiler number for RDX is 3 while the DWARF number for RDX is 1. public enum DwarfRegEncodingAMD64 { RAX(0), RDX(1), @@ -718,7 +724,19 @@ public enum DwarfRegEncodingAMD64 { } } - // map from compiler X86_64 register indices to corresponding dwarf AMD64 register index + // Map from compiler X86_64 register numbers to corresponding DWARF AMD64 register encoding. + // Register numbers for general purpose and float registers occupy index ranges 0-15 and 16-31 + // respectively. Table entries provide the corresponding number used by DWARF to identify the + // same register. Note that the first 8 initialization expressions in the array initializer + // do not appear in ascending order of the enum tag declarations, That is because of the + // disparity + // between the compiler's chosen order for these 8 registers and DWARF's chosen order. The order + // of + // the initialization entries effectively encodes a permutation from the compiler register order + // to + // the DWARF register order. The access to the encoding field then completes the translation + // from + // DWARF register to the corresponding DWARF numbering. private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = { DwarfRegEncodingAMD64.RAX.encoding, DwarfRegEncodingAMD64.RCX.encoding, From 15e2046bea89a7584b5cbcd4e4ade02a3da0fa91 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 9 May 2022 13:48:52 +0100 Subject: [PATCH 16/22] Simplify Graal to DWARF register number mapping. --- substratevm/mx.substratevm/suite.py | 3 + .../elf/dwarf/DwarfLocSectionImpl.java | 369 +++++++----------- 2 files changed, 147 insertions(+), 225 deletions(-) diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 4eebf193143f..d034e5fb406a 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -795,6 +795,9 @@ "jdk.internal.ref", "sun.nio.ch", ], + "jdk.internal.vm.ci" : [ + "jdk.vm.ci.code", + ], }, "checkstyle" : "com.oracle.svm.hosted", "javaCompliance": "11+", diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index aa65eb20fee9..9505d872ebd9 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -41,12 +41,15 @@ import org.graalvm.compiler.debug.DebugContext; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.PrimitiveConstant; @@ -522,87 +525,98 @@ private int mapToDwarfReg(int regIdx) { private void initDwarfRegMap() { if (dwarfSections.elfMachine == ELFMachine.AArch64) { dwarfRegMap = GRAAL_AARCH64_TO_DWARF_REG_MAP; - dwarfStackRegister = DwarfRegEncodingAArch64.SP.encoding; + dwarfStackRegister = DwarfRegEncodingAArch64.SP.getDwarfEncoding(); } else { assert dwarfSections.elfMachine == ELFMachine.X86_64 : "must be"; dwarfRegMap = GRAAL_X86_64_TO_DWARF_REG_MAP; - dwarfStackRegister = DwarfRegEncodingAMD64.RSP.encoding; + dwarfStackRegister = DwarfRegEncodingAMD64.RSP.getDwarfEncoding(); } } - // Register numbers used by DWARF for AArch64 registers. + // Register numbers used by DWARF for AArch64 registers encoded + // along with their respective GraalVM compiler number. public enum DwarfRegEncodingAArch64 { - R0(0), - R1(1), - R2(2), - R3(3), - R4(4), - R5(5), - R6(6), - R7(7), - R8(8), - R9(9), - R10(10), - R11(11), - R12(12), - R13(13), - R14(14), - R15(15), - R16(16), - R17(17), - R18(18), - R19(19), - R20(20), - R21(21), - R22(22), - R23(23), - R24(24), - R25(25), - R26(26), - R27(27), - R28(28), - R29(29), - R30(30), - R31(31), - ZR(96), - SP(31), - V0(64), - V1(65), - V2(66), - V3(67), - V4(68), - V5(69), - V6(70), - V7(71), - V8(72), - V9(73), - V10(74), - V11(75), - V12(76), - V13(77), - V14(78), - V15(79), - V16(80), - V17(81), - V18(82), - V19(83), - V20(84), - V21(85), - V22(86), - V23(87), - V24(88), - V25(89), - V26(90), - V27(91), - V28(92), - V29(93), - V30(94), - V31(95); - - public final int encoding; - - DwarfRegEncodingAArch64(int encoding) { - this.encoding = encoding; + R0(0, AArch64.r0.number), + R1(1, AArch64.r1.number), + R2(2, AArch64.r2.number), + R3(3, AArch64.r3.number), + R4(4, AArch64.r4.number), + R5(5, AArch64.r5.number), + R6(6, AArch64.r6.number), + R7(7, AArch64.r7.number), + R8(8, AArch64.r8.number), + R9(9, AArch64.r9.number), + R10(10, AArch64.r10.number), + R11(11, AArch64.r11.number), + R12(12, AArch64.r12.number), + R13(13, AArch64.r13.number), + R14(14, AArch64.r14.number), + R15(15, AArch64.r15.number), + R16(16, AArch64.r16.number), + R17(17, AArch64.r17.number), + R18(18, AArch64.r18.number), + R19(19, AArch64.r19.number), + R20(20, AArch64.r20.number), + R21(21, AArch64.r21.number), + R22(22, AArch64.r22.number), + R23(23, AArch64.r23.number), + R24(24, AArch64.r24.number), + R25(25, AArch64.r25.number), + R26(26, AArch64.r26.number), + R27(27, AArch64.r27.number), + R28(28, AArch64.r28.number), + R29(29, AArch64.r29.number), + R30(30, AArch64.r30.number), + R31(31, AArch64.r31.number), + ZR(31, AArch64.zr.number), + SP(31, AArch64.sp.number), + V0(64, AArch64.v0.number), + V1(65, AArch64.r1.number), + V2(66, AArch64.v2.number), + V3(67, AArch64.v3.number), + V4(68, AArch64.v4.number), + V5(69, AArch64.v5.number), + V6(70, AArch64.v6.number), + V7(71, AArch64.v7.number), + V8(72, AArch64.v8.number), + V9(73, AArch64.v9.number), + V10(74, AArch64.v10.number), + V11(75, AArch64.v11.number), + V12(76, AArch64.v12.number), + V13(77, AArch64.v13.number), + V14(78, AArch64.v14.number), + V15(79, AArch64.v15.number), + V16(80, AArch64.v16.number), + V17(81, AArch64.v17.number), + V18(82, AArch64.v18.number), + V19(83, AArch64.v19.number), + V20(84, AArch64.v20.number), + V21(85, AArch64.v21.number), + V22(86, AArch64.v22.number), + V23(87, AArch64.v23.number), + V24(88, AArch64.v24.number), + V25(89, AArch64.v25.number), + V26(90, AArch64.v26.number), + V27(91, AArch64.v27.number), + V28(92, AArch64.v28.number), + V29(93, AArch64.v29.number), + V30(94, AArch64.v30.number), + V31(95, AArch64.v31.number); + + private final int dwarfEncoding; + private final int graalEncoding; + + DwarfRegEncodingAArch64(int dwarfEncoding, int graalEncoding) { + this.dwarfEncoding = dwarfEncoding; + this.graalEncoding = graalEncoding; + } + + public static int graalOrder(DwarfRegEncodingAArch64 e1, DwarfRegEncodingAArch64 e2) { + return Integer.compare(e1.graalEncoding, e2.graalEncoding); + } + + public int getDwarfEncoding() { + return dwarfEncoding; } } @@ -611,164 +625,69 @@ public enum DwarfRegEncodingAArch64 { // and 34-65 respectively. Table entries provided the corresponding number used by DWARF to // identify the same register. Note that the table includes entries for ZR (32) and SP (33) // even though we should not see those register numbers appearing in location values. - private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = { - DwarfRegEncodingAArch64.R0.encoding, - DwarfRegEncodingAArch64.R1.encoding, - DwarfRegEncodingAArch64.R2.encoding, - DwarfRegEncodingAArch64.R3.encoding, - DwarfRegEncodingAArch64.R4.encoding, - DwarfRegEncodingAArch64.R5.encoding, - DwarfRegEncodingAArch64.R6.encoding, - DwarfRegEncodingAArch64.R7.encoding, - DwarfRegEncodingAArch64.R8.encoding, - DwarfRegEncodingAArch64.R9.encoding, - DwarfRegEncodingAArch64.R10.encoding, - DwarfRegEncodingAArch64.R11.encoding, - DwarfRegEncodingAArch64.R12.encoding, - DwarfRegEncodingAArch64.R13.encoding, - DwarfRegEncodingAArch64.R14.encoding, - DwarfRegEncodingAArch64.R15.encoding, - DwarfRegEncodingAArch64.R16.encoding, - DwarfRegEncodingAArch64.R17.encoding, - DwarfRegEncodingAArch64.R18.encoding, - DwarfRegEncodingAArch64.R19.encoding, - DwarfRegEncodingAArch64.R20.encoding, - DwarfRegEncodingAArch64.R21.encoding, - DwarfRegEncodingAArch64.R22.encoding, - DwarfRegEncodingAArch64.R23.encoding, - DwarfRegEncodingAArch64.R24.encoding, - DwarfRegEncodingAArch64.R25.encoding, - DwarfRegEncodingAArch64.R26.encoding, - DwarfRegEncodingAArch64.R27.encoding, - DwarfRegEncodingAArch64.R28.encoding, - DwarfRegEncodingAArch64.R29.encoding, - DwarfRegEncodingAArch64.R30.encoding, - DwarfRegEncodingAArch64.R31.encoding, - DwarfRegEncodingAArch64.ZR.encoding, - DwarfRegEncodingAArch64.SP.encoding, - DwarfRegEncodingAArch64.V0.encoding, - DwarfRegEncodingAArch64.V1.encoding, - DwarfRegEncodingAArch64.V2.encoding, - DwarfRegEncodingAArch64.V3.encoding, - DwarfRegEncodingAArch64.V4.encoding, - DwarfRegEncodingAArch64.V5.encoding, - DwarfRegEncodingAArch64.V6.encoding, - DwarfRegEncodingAArch64.V7.encoding, - DwarfRegEncodingAArch64.V8.encoding, - DwarfRegEncodingAArch64.V9.encoding, - DwarfRegEncodingAArch64.V10.encoding, - DwarfRegEncodingAArch64.V11.encoding, - DwarfRegEncodingAArch64.V12.encoding, - DwarfRegEncodingAArch64.V13.encoding, - DwarfRegEncodingAArch64.V14.encoding, - DwarfRegEncodingAArch64.V15.encoding, - DwarfRegEncodingAArch64.V16.encoding, - DwarfRegEncodingAArch64.V17.encoding, - DwarfRegEncodingAArch64.V18.encoding, - DwarfRegEncodingAArch64.V19.encoding, - DwarfRegEncodingAArch64.V20.encoding, - DwarfRegEncodingAArch64.V21.encoding, - DwarfRegEncodingAArch64.V22.encoding, - DwarfRegEncodingAArch64.V23.encoding, - DwarfRegEncodingAArch64.V24.encoding, - DwarfRegEncodingAArch64.V25.encoding, - DwarfRegEncodingAArch64.V26.encoding, - DwarfRegEncodingAArch64.V27.encoding, - DwarfRegEncodingAArch64.V28.encoding, - DwarfRegEncodingAArch64.V29.encoding, - DwarfRegEncodingAArch64.V30.encoding, - DwarfRegEncodingAArch64.V31.encoding, - }; + private static final int[] GRAAL_AARCH64_TO_DWARF_REG_MAP = Arrays.stream(DwarfRegEncodingAArch64.values()).sorted(DwarfRegEncodingAArch64::graalOrder) + .mapToInt(DwarfRegEncodingAArch64::getDwarfEncoding).toArray(); - // Register numbers used by DWARF for AMD64 registers. Note that the first 8 general - // purpose registers appear in a different order to that used by the compiler. For + // Register numbers used by DWARF for AMD64 registers encoded + // along with their respective GraalVM compiler number. n.b. some of the initial + // 8 general purpose registers have different Dwarf and GraalVM encodings. For // example the compiler number for RDX is 3 while the DWARF number for RDX is 1. public enum DwarfRegEncodingAMD64 { - RAX(0), - RDX(1), - RCX(2), - RBX(3), - RSI(4), - RDI(5), - RBP(6), - RSP(7), - R8(8), - R9(9), - R10(10), - R11(11), - R12(12), - R13(13), - R14(14), - R15(15), - XMM0(17), - XMM1(18), - XMM2(19), - XMM3(20), - XMM4(21), - XMM5(22), - XMM6(23), - XMM7(24), - XMM8(25), - XMM9(26), - XMM10(27), - XMM11(28), - XMM12(29), - XMM13(30), - XMM14(31), - XMM15(32); - - public final int encoding; - - DwarfRegEncodingAMD64(int encoding) { - this.encoding = encoding; + RAX(0, AMD64.rax.number), + RDX(1, AMD64.rdx.number), + RCX(2, AMD64.rcx.number), + RBX(3, AMD64.rbx.number), + RSI(4, AMD64.rsi.number), + RDI(5, AMD64.rdi.number), + RBP(6, AMD64.rbp.number), + RSP(7, AMD64.rsp.number), + R8(8, AMD64.r8.number), + R9(9, AMD64.r9.number), + R10(10, AMD64.r10.number), + R11(11, AMD64.r11.number), + R12(12, AMD64.r12.number), + R13(13, AMD64.r13.number), + R14(14, AMD64.r14.number), + R15(15, AMD64.r15.number), + XMM0(17, AMD64.xmm0.number), + XMM1(18, AMD64.xmm1.number), + XMM2(19, AMD64.xmm2.number), + XMM3(20, AMD64.xmm3.number), + XMM4(21, AMD64.xmm4.number), + XMM5(22, AMD64.xmm5.number), + XMM6(23, AMD64.xmm6.number), + XMM7(24, AMD64.xmm7.number), + XMM8(25, AMD64.xmm8.number), + XMM9(26, AMD64.xmm9.number), + XMM10(27, AMD64.xmm10.number), + XMM11(28, AMD64.xmm11.number), + XMM12(29, AMD64.xmm12.number), + XMM13(30, AMD64.xmm13.number), + XMM14(31, AMD64.xmm14.number), + XMM15(32, AMD64.xmm15.number); + + private final int dwarfEncoding; + private final int graalEncoding; + + DwarfRegEncodingAMD64(int dwarfEncoding, int graalEncoding) { + this.dwarfEncoding = dwarfEncoding; + this.graalEncoding = graalEncoding; + } + + public static int graalOrder(DwarfRegEncodingAMD64 e1, DwarfRegEncodingAMD64 e2) { + return Integer.compare(e1.graalEncoding, e2.graalEncoding); + } + + public int getDwarfEncoding() { + return dwarfEncoding; } } // Map from compiler X86_64 register numbers to corresponding DWARF AMD64 register encoding. // Register numbers for general purpose and float registers occupy index ranges 0-15 and 16-31 // respectively. Table entries provide the corresponding number used by DWARF to identify the - // same register. Note that the first 8 initialization expressions in the array initializer - // do not appear in ascending order of the enum tag declarations, That is because of the - // disparity - // between the compiler's chosen order for these 8 registers and DWARF's chosen order. The order - // of - // the initialization entries effectively encodes a permutation from the compiler register order - // to - // the DWARF register order. The access to the encoding field then completes the translation - // from - // DWARF register to the corresponding DWARF numbering. - private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = { - DwarfRegEncodingAMD64.RAX.encoding, - DwarfRegEncodingAMD64.RCX.encoding, - DwarfRegEncodingAMD64.RDX.encoding, - DwarfRegEncodingAMD64.RBX.encoding, - DwarfRegEncodingAMD64.RSP.encoding, - DwarfRegEncodingAMD64.RBP.encoding, - DwarfRegEncodingAMD64.RSI.encoding, - DwarfRegEncodingAMD64.RDI.encoding, - DwarfRegEncodingAMD64.R8.encoding, - DwarfRegEncodingAMD64.R9.encoding, - DwarfRegEncodingAMD64.R10.encoding, - DwarfRegEncodingAMD64.R11.encoding, - DwarfRegEncodingAMD64.R12.encoding, - DwarfRegEncodingAMD64.R13.encoding, - DwarfRegEncodingAMD64.R14.encoding, - DwarfRegEncodingAMD64.R15.encoding, - DwarfRegEncodingAMD64.XMM0.encoding, - DwarfRegEncodingAMD64.XMM1.encoding, - DwarfRegEncodingAMD64.XMM2.encoding, - DwarfRegEncodingAMD64.XMM3.encoding, - DwarfRegEncodingAMD64.XMM4.encoding, - DwarfRegEncodingAMD64.XMM5.encoding, - DwarfRegEncodingAMD64.XMM6.encoding, - DwarfRegEncodingAMD64.XMM7.encoding, - DwarfRegEncodingAMD64.XMM8.encoding, - DwarfRegEncodingAMD64.XMM9.encoding, - DwarfRegEncodingAMD64.XMM10.encoding, - DwarfRegEncodingAMD64.XMM11.encoding, - DwarfRegEncodingAMD64.XMM12.encoding, - DwarfRegEncodingAMD64.XMM13.encoding, - DwarfRegEncodingAMD64.XMM14.encoding, - DwarfRegEncodingAMD64.XMM15.encoding, - }; + // same register. + private static final int[] GRAAL_X86_64_TO_DWARF_REG_MAP = Arrays.stream(DwarfRegEncodingAMD64.values()).sorted(DwarfRegEncodingAMD64::graalOrder).mapToInt(DwarfRegEncodingAMD64::getDwarfEncoding) + .toArray(); + } From d57108d8bd1cbdd3832c74bb0dcc2b7d08ae4cf2 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 9 May 2022 14:20:52 +0100 Subject: [PATCH 17/22] Correct typo in architecture name. --- .../oracle/svm/hosted/image/NativeImageDebugInfoProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 3aacdef2add2..a1c5e0420b87 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1871,7 +1871,7 @@ class ParamLocationProducer { ParamLocationProducer(ResolvedJavaMethod method) { Architecture arch = ConfigurationValues.getTarget().arch; - assert arch instanceof AMD64 || arch instanceof AMD64 : "unexpected architecture"; + assert arch instanceof AMD64 || arch instanceof AArch64 : "unexpected architecture"; OS os = OS.getCurrent(); assert os == OS.LINUX || os == OS.WINDOWS : "unexpected os"; if (arch instanceof AArch64) { From 2132bfff26314d68d4a8ee234e3b3650900974e4 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Thu, 12 May 2022 16:42:31 +0100 Subject: [PATCH 18/22] Handle bad source positions generated occasionally on AArch64. --- .../image/NativeImageDebugInfoProvider.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index a1c5e0420b87..71f1914aaaec 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1249,6 +1249,8 @@ public NativeImageDebugLocationInfo process(FrameNode node, NativeImageDebugLoca // this node represents an inline call range so // add a locationinfo to cover the range of the call locationInfo = createCallLocationInfo((CallNode) node, callerInfo, frameSize); + } else if (isBadLeaf(node, callerInfo)) { + locationInfo = createBadLeafLocationInfo(node, callerInfo, frameSize); } else { // this is leaf method code so add details of its range locationInfo = createLeafLocationInfo(node, callerInfo, frameSize); @@ -1404,6 +1406,38 @@ private NativeImageDebugLocationInfo createEmbeddedParentLocationInfo(CallNode p return locationInfo; } + private NativeImageDebugLocationInfo createBadLeafLocationInfo(FrameNode node, NativeImageDebugLocationInfo callerLocation, int framesize) { + assert !(node instanceof CallNode) : "bad leaf location cannot be a call node!"; + assert callerLocation == null : "should only see bad leaf at top level!"; + BytecodePosition pos = node.frame; + BytecodePosition callerPos = pos.getCaller(); + assert callerPos != null : "bad leaf must have a caller"; + assert callerPos.getCaller() == null : "bad leaf caller must be root method"; + int startPos = node.getStartPos(); + int endPos = node.getEndPos() + 1; + NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(callerPos, startPos, endPos, null, framesize); + debugContext.log(DebugContext.DETAILED_LEVEL, "Embed leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), + locationInfo.addressHi() - 1); + return locationInfo; + } + + private boolean isBadLeaf(FrameNode node, NativeImageDebugLocationInfo callerLocation) { + // Sometimes we see a leaf node marked as belonging to an inlined method + // that sits directly under the root method rather than under a call node. + // It needs replacing with a location info for the root method that covers + // the relevant code range. + if (callerLocation == null) { + BytecodePosition pos = node.frame; + BytecodePosition callerPos = pos.getCaller(); + if (callerPos != null && callerPos.getMethod() != pos.getMethod()) { + if (callerPos.getCaller() == null) { + return true; + } + } + } + return false; + } + /** * Test whether a bytecode position represents a bogus frame added by the compiler when a * substitution or snippet call is injected. From 017a20910c87a91f80b3573ab87b39019f627bc7 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 13 May 2022 14:06:34 +0100 Subject: [PATCH 19/22] Fix problems found by Oracle gate checks. --- .../svm/hosted/image/NativeImageDebugInfoProvider.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 71f1914aaaec..bbe794435346 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1677,7 +1677,7 @@ private List initLocalInfoList(BytecodePosition bcpos, int debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", i, name, type.getName(), slot); JavaValue value = (slot < frame.numLocals ? frame.getLocalValue(slot) : Value.ILLEGAL); JavaKind storageKind = (slot < frame.numLocals ? frame.getLocalValueKind(slot) : JavaKind.Illegal); - debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value, storageKind); int bciStart = l.getStartBCI(); int line = (lineNumberTable != null ? lineNumberTable.getLineNumber(bciStart) : -1); // only add the local if the kinds match @@ -1686,7 +1686,7 @@ private List initLocalInfoList(BytecodePosition bcpos, int (isPseudoObjectType(type, ownerType) && kind == JavaKind.Object && storageKind == JavaKind.Long)) { localInfos.add(new NativeImageDebugLocalValueInfo(name, value, framesize, storageKind, type, slot, line)); } else if (storageKind != JavaKind.Illegal) { - debugContext.log(DebugContext.DETAILED_LEVEL, " value kind incompatible with var kind %s!", type.getJavaKind().toString()); + debugContext.log(DebugContext.DETAILED_LEVEL, " value kind incompatible with var kind %s!", type.getJavaKind()); } } } @@ -1710,7 +1710,7 @@ private List initSyntheticInfoList(ParamLocationProducer lo assert kind == JavaKind.Object : "must be an object"; NativeImageDebugLocalValue value = locProducer.nextLocation(kind); debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", localIdx, name, ownerType.getName(), slot); - debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value, storageKind); localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, ownerType, slot, line)); slot += storageKind.getSlotCount(); localIdx++; @@ -1723,7 +1723,7 @@ private List initSyntheticInfoList(ParamLocationProducer lo JavaKind storageKind = isPseudoObjectType(paramType, ownerType) ? JavaKind.Long : kind; NativeImageDebugLocalValue value = locProducer.nextLocation(kind); debugContext.log(DebugContext.DETAILED_LEVEL, "locals[%d] %s type %s slot %d", localIdx, name, ownerType.getName(), slot); - debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value.toString(), storageKind.toString()); + debugContext.log(DebugContext.DETAILED_LEVEL, " => %s kind %s", value, storageKind); localInfos.add(new NativeImageDebugLocalValueInfo(name, value, storageKind, paramType, slot, line)); slot += storageKind.getSlotCount(); localIdx++; @@ -1810,7 +1810,7 @@ NativeImageDebugLocationInfo merge(NativeImageDebugLocationInfo that) { if (this.hi != that.lo) { return null; } - if (method != that.method) { + if (!method.equals(that.method)) { return null; } if (line() != that.line()) { From 1c651c81aa29844c7cb36e64b4b30c492eb66e47 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 13 May 2022 14:38:49 +0100 Subject: [PATCH 20/22] Fix problems with debug info test script due to variation in inlining. --- substratevm/mx.substratevm/testhello.py | 46 +++++++++++++------ .../image/NativeImageDebugInfoProvider.java | 2 +- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 4a9b4c4ae50d..039033e1d4fe 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -458,6 +458,8 @@ def test(): # As a result the breakpoint will be set at all println overrides. # expect "Breakpoint 1 at 0x[0-9a-f]+: java.io.PrintStream::println. ([0-9]+ locations)"" exec_string = execute("break java.io.PrintStream::println") + # we cannot be sure how much inlining will happen so we + # specify a pattern for the number of locations rexp = r"Breakpoint %s at %s: java\.io\.PrintStream::println\. \(%s locations\)"%(digits_pattern, address_pattern, digits_pattern) checker = Checker('break println', rexp) checker.check(exec_string, skip_fails=False) @@ -620,24 +622,30 @@ def test(): execute("delete breakpoints") exec_string = execute("break Hello.java:173") - rexp = r"Breakpoint %s at %s: Hello\.java:173\. \(2 locations\)"%(digits_pattern, address_pattern) + # we cannot be sure how much inlining will happen so we + # specify a pattern for the number of locations + rexp = r"Breakpoint %s at %s: Hello\.java:173\. \(%s locations\)"%(digits_pattern, address_pattern, digits_pattern) checker = Checker('break Hello.java:173', rexp) checker.check(exec_string) execute("continue") exec_string = execute("backtrace 14") + # we cannot be sure exactly how much inlining happens + # which means the format of the frame display may vary from + # one build to the next. so we use a generic match after the + # first two pairs. rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:173"%(spaces_pattern, arg_values_pattern), r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#6%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), - r"#7%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#8%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), - r"#9%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#10%shello\.Hello::inlineTo %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), - r"#11%shello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, arg_values_pattern), + r"#4%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), + r"#5%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), + r"#6%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), + r"#7%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), + r"#8%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), + r"#9%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), + r"#10%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), + r"#11%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:146"%(spaces_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) @@ -645,18 +653,24 @@ def test(): execute("delete breakpoints") exec_string = execute("break Hello.java:179") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 179\."%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:178', rexp) + # we cannot be sure how much inlining will happen so we + # specify a pattern for the number of locations + rexp = r"Breakpoint %s at %s: Hello\.java:179\. \(%s locations\)"%(digits_pattern, address_pattern, digits_pattern) + checker = Checker('break Hello.java:179', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 8") + # we cannot be sure exactly how much inlining happens + # which means the format of the frame display may vary from + # one build to the next. so we use a generic match after the + # first two. rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, arg_values_pattern), r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#5%s%s in hello\.Hello::inlineTailRecursion %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), + r"#3%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), + r"#4%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), + r"#5%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:147"%(spaces_pattern), r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) %s at hello/Hello\.java:94"%(spaces_pattern, arg_values_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) @@ -730,6 +744,8 @@ def test(): execute("delete breakpoints"); exec_string = execute("break hello.Hello::inlineReceiveConstants") + # we cannot be sure how much inlining will happen so we + # specify a pattern for the number of locations rexp = r"Breakpoint %s at %s: hello\.Hello::inlineReceiveConstants\. \(%s locations\)"%(digits_pattern, address_pattern, digits_pattern) checker = Checker('break hello.Hello::inlineReceiveConstants', rexp) checker.check(exec_string) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index bbe794435346..ada77b945663 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1417,7 +1417,7 @@ private NativeImageDebugLocationInfo createBadLeafLocationInfo(FrameNode node, N int endPos = node.getEndPos() + 1; NativeImageDebugLocationInfo locationInfo = new NativeImageDebugLocationInfo(callerPos, startPos, endPos, null, framesize); debugContext.log(DebugContext.DETAILED_LEVEL, "Embed leaf Location Info : %s depth %d (%d, %d)", locationInfo.name(), locationInfo.depth(), locationInfo.addressLo(), - locationInfo.addressHi() - 1); + locationInfo.addressHi() - 1); return locationInfo; } From 9dba3318059d32257295a50064787c5a6b2f6348 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 13 May 2022 15:45:38 +0100 Subject: [PATCH 21/22] Correct further output format issues with the debuginfotest script --- substratevm/mx.substratevm/testhello.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 039033e1d4fe..3e936d4be61d 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -633,11 +633,11 @@ def test(): # we cannot be sure exactly how much inlining happens # which means the format of the frame display may vary from # one build to the next. so we use a generic match after the - # first two pairs. + # first pair. rexp = [r"#0%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:173"%(spaces_pattern, arg_values_pattern), r"#1%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), - r"#2%shello\.Hello::inlineTo\(int\) %s at hello/Hello\.java:171"%(spaces_pattern, arg_values_pattern), - r"#3%s%s in hello\.Hello::inlineHere %s at hello/Hello\.java:165"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#2%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), + r"#3%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), r"#4%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), r"#5%shello\.Hello::inlineHere%s at hello/Hello\.java:165"%(wildcard_pattern, wildcard_pattern), r"#6%shello\.Hello::inlineTo%s at hello/Hello\.java:171"%(wildcard_pattern, wildcard_pattern), @@ -664,9 +664,9 @@ def test(): # we cannot be sure exactly how much inlining happens # which means the format of the frame display may vary from # one build to the next. so we use a generic match after the - # first two. + # first one. rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:179"%(spaces_pattern, arg_values_pattern), - r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) %s at hello/Hello\.java:182"%(spaces_pattern, address_pattern, arg_values_pattern), + r"#1%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), r"#2%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), r"#3%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), r"#4%shello\.Hello::inlineTailRecursion%s at hello/Hello\.java:182"%(wildcard_pattern, wildcard_pattern), From d47313eb3c090de9bf08b91a827df5888de7d03a Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 13 May 2022 16:59:50 +0100 Subject: [PATCH 22/22] Use equals test for method comparisons. --- .../oracle/svm/hosted/image/NativeImageDebugInfoProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index ada77b945663..b4a2890fd00d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1429,7 +1429,7 @@ private boolean isBadLeaf(FrameNode node, NativeImageDebugLocationInfo callerLoc if (callerLocation == null) { BytecodePosition pos = node.frame; BytecodePosition callerPos = pos.getCaller(); - if (callerPos != null && callerPos.getMethod() != pos.getMethod()) { + if (callerPos != null && !callerPos.getMethod().equals(pos.getMethod())) { if (callerPos.getCaller() == null) { return true; }