From 6fcf98378231f0e754061b683c3dba387ce8b0e9 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 30 Sep 2020 03:00:33 +0300 Subject: [PATCH 01/27] Augment Range with info for nested inlining --- .../objectfile/debugentry/DebugInfoBase.java | 3 ++- .../oracle/objectfile/debugentry/Range.java | 22 +++++++++++++++++-- .../elf/dwarf/DwarfLineSectionImpl.java | 4 ++++ 3 files changed, 26 insertions(+), 3 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 e328446d3b00..dcbb4733ab17 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 @@ -255,6 +255,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { Path filePathAtLine = debugLineInfo.filePath(); String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.ownerType()); String methodNameAtLine = debugLineInfo.name(); + boolean isInlined = false; int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); @@ -264,7 +265,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ ClassEntry subClassEntry = ensureClassEntry(classNameAtLine); MethodEntry subMethodEntry = subClassEntry.ensureMethodEntryForDebugRangeInfo(debugLineInfo, this, debugContext); - Range subRange = new Range(stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange); + Range subRange = new Range(stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, null); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine); 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 5f7f3fe3bdab..cc407a53acdf 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 @@ -34,11 +34,14 @@ public class Range { private static final String CLASS_DELIMITER = "."; + private final Range caller; private final MethodEntry methodEntry; private final String fullMethodNameWithParams; private final int lo; private final int hi; private final int line; + private final boolean isInlined; + private final boolean withChildren; /* * This is null for a primary range. */ @@ -48,13 +51,13 @@ 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); + this(stringTable, methodEntry, lo, hi, line, null, false, false, null); } /* * Create a primary or secondary range. */ - public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary) { + public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isInline, boolean withChildren, Range caller) { assert methodEntry != null; if (methodEntry.fileEntry != null) { stringTable.uniqueDebugString(methodEntry.fileEntry.getFileName()); @@ -65,7 +68,10 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i this.lo = lo; this.hi = hi; this.line = line; + this.isInlined = isInline; this.primary = primary; + this.withChildren = withChildren; + this.caller = caller; } public boolean contains(Range other) { @@ -167,4 +173,16 @@ public String getFileName() { public MethodEntry getMethodEntry() { return methodEntry; } + + public boolean isInlined() { + return isInlined; + } + + public boolean withChildren() { + return withChildren; + } + + public Range getCaller() { + return caller; + } } 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 23e206f91d27..45f493f22ca8 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 @@ -516,6 +516,10 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by * Now write a row for each subrange lo and hi. */ for (Range subrange : primaryEntry.getSubranges()) { + if (subrange.withChildren()) { + /* skip caller subranges */ + continue; + } assert subrange.getLo() >= primaryRange.getLo(); assert subrange.getHi() <= primaryRange.getHi(); FileEntry subFileEntry = subrange.getFileEntry(); From 641c36f6523dc00cb6b90a7725ddabfbbdc447c5 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 30 Sep 2020 02:33:50 +0300 Subject: [PATCH 02/27] Chain debugLineInfo instances to get call-chain for nested inlining --- .../objectfile/debugentry/DebugInfoBase.java | 2 +- .../debuginfo/DebugInfoProvider.java | 5 ++++ .../image/NativeImageDebugInfoProvider.java | 29 +++++++++++++++---- 3 files changed, 30 insertions(+), 6 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 dcbb4733ab17..8f18ef4aae0b 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 @@ -255,7 +255,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { Path filePathAtLine = debugLineInfo.filePath(); String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.ownerType()); String methodNameAtLine = debugLineInfo.name(); - boolean isInlined = false; + boolean isInlined = debugLineInfo.getCaller() != null; int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); 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 00ef51179038..c1641220fcce 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 @@ -315,6 +315,11 @@ interface DebugLineInfo extends DebugRangeInfo { * @return the line number for the outer or inlined segment. */ int line(); + + /** + * @return the {@link DebugLineInfo} of the nested inline caller-line + */ + DebugLineInfo getCaller(); } interface DebugFrameSizeChange { 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 b80b3b507750..95439b6a8ee5 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 @@ -1002,8 +1002,8 @@ private static boolean filterLineInfoSourceMapping(SourceMapping sourceMapping) } /** - * Implementation of the DebugLineInfo API interface that allows line number info to be passed - * to an ObjectFile when generation of debug info is enabled. + * 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. */ private class NativeImageDebugLineInfo implements DebugLineInfo { private final int bci; @@ -1012,15 +1012,29 @@ private class NativeImageDebugLineInfo implements DebugLineInfo { private final int hi; private Path cachePath; private Path fullFilePath; + private DebugLineInfo callersLineInfo; NativeImageDebugLineInfo(SourceMapping sourceMapping) { - NodeSourcePosition position = sourceMapping.getSourcePosition(); + this(sourceMapping.getSourcePosition(), sourceMapping.getStartOffset(), sourceMapping.getEndOffset()); + } + + NativeImageDebugLineInfo(DebugLineInfo lineInfo, NodeSourcePosition position) { + this(position, lineInfo.addressLo(), lineInfo.addressHi()); + } + + NativeImageDebugLineInfo(NodeSourcePosition position, int lo, int hi) { int posbci = position.getBCI(); this.bci = (posbci >= 0 ? posbci : 0); this.method = position.getMethod(); - this.lo = sourceMapping.getStartOffset(); - this.hi = sourceMapping.getEndOffset(); + this.lo = lo; + this.hi = hi; this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); + final NodeSourcePosition callerPosition = position.getCaller(); + if (callerPosition != null) { + callersLineInfo = new NativeImageDebugLineInfo(this, callerPosition); + } else { + callersLineInfo = null; + } computeFullFilePath(); } @@ -1159,6 +1173,11 @@ public int modifiers() { return method.getModifiers(); } + @Override + public DebugLineInfo getCaller() { + return callersLineInfo; + } + @SuppressWarnings("try") private void computeFullFilePath() { ResolvedJavaType declaringClass; From bf15d3118307c058ab211ef3b7f9362901d04782 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 30 Sep 2020 02:12:10 +0300 Subject: [PATCH 03/27] Extend DWARF abbrevs --- .../oracle/objectfile/debugentry/Range.java | 4 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 58 ++++++++++++--- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 26 ++++++- .../elf/dwarf/DwarfInfoSectionImpl.java | 71 ++++++++++++++++--- 4 files changed, 138 insertions(+), 21 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 cc407a53acdf..3fdd573de099 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 @@ -36,6 +36,7 @@ public class Range { private static final String CLASS_DELIMITER = "."; private final Range caller; private final MethodEntry methodEntry; + private final String fullMethodName; private final String fullMethodNameWithParams; private final int lo; private final int hi; @@ -64,6 +65,7 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i stringTable.uniqueDebugString(methodEntry.fileEntry.getPathName()); } this.methodEntry = methodEntry; + this.fullMethodName = isInline ? stringTable.uniqueDebugString(constructClassAndMethodName()) : stringTable.uniqueString(constructClassAndMethodName()); this.fullMethodNameWithParams = stringTable.uniqueString(constructClassAndMethodNameWithParams()); this.lo = lo; this.hi = hi; @@ -111,7 +113,7 @@ public int getLine() { } public String getFullMethodName() { - return constructClassAndMethodName(); + return fullMethodName; } public String getFullMethodNameWithParams() { 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 bf8a1035ff50..68b52d2f4397 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 @@ -710,11 +710,14 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { pos = writeHeaderFieldAbbrev(context, buffer, pos); pos = writeArrayDataTypeAbbrev(context, buffer, pos); - pos = writeMethodLocationAbbrev(context, buffer, pos); + pos = writeMethodLocationAbbrevs(context, buffer, pos); pos = writeStaticFieldLocationAbbrev(context, buffer, pos); pos = writeSuperReferenceAbbrev(context, buffer, pos); pos = writeInterfaceImplementorAbbrev(context, buffer, pos); + pos = writeInlinedSubroutineAbbrevs(buffer, pos, false); + pos = writeInlinedSubroutineAbbrevs(buffer, pos, true); + /* * if we address rebasing is required then then we need to use indirect layout types * supplied with a suitable data_location attribute and indirect pointer types to ensure @@ -936,8 +939,8 @@ private int writeClassReferenceAbbrev(@SuppressWarnings("unused") DebugContext c private int writeMethodDeclarationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1, buffer, pos); - pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration_static, buffer, pos); return pos; } @@ -971,7 +974,7 @@ private int writeMethodDeclarationAbbrev(@SuppressWarnings("unused") DebugContex // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_containing_type, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); - if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1) { + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration) { pos = writeAttrType(DwarfDebugInfo.DW_AT_object_pointer, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); } @@ -1165,15 +1168,29 @@ private int writeArrayDataTypeAbbrev(@SuppressWarnings("unused") DebugContext co return pos; } - private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + private int writeMethodLocationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeMethodLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_location, buffer, pos); + pos = writeMethodLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline, buffer, pos); + + return pos; + } + + private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, long abbrevCode, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_method_location, buffer, pos); + pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_inline, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + } else { + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + } pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); @@ -1326,6 +1343,27 @@ private int writeNullAbbrev(@SuppressWarnings("unused") DebugContext context, by return pos; } + private int writeInlinedSubroutineAbbrevs(byte[] buffer, int p, boolean withChildren) { + int pos = p; + pos = writeAbbrevCode(withChildren ? DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children : DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_inlined_subroutine, buffer, pos); + pos = writeFlag(withChildren ? DwarfDebugInfo.DW_CHILDREN_yes : DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_abstract_origin, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_call_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_call_line, 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; + } + /** * The debug_abbrev section depends on debug_aranges section. */ 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 48c651ba1ee5..27bd7a03af97 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 @@ -78,6 +78,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_ABBREV_CODE_class_layout2 = 9; public static final int DW_ABBREV_CODE_class_pointer = 10; public static final int DW_ABBREV_CODE_method_location = 11; + public static final int DW_ABBREV_CODE_method_location_inline = 32; public static final int DW_ABBREV_CODE_static_field_location = 12; public static final int DW_ABBREV_CODE_array_layout = 13; public static final int DW_ABBREV_CODE_array_pointer = 14; @@ -86,8 +87,8 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_ABBREV_CODE_indirect_layout = 17; public static final int DW_ABBREV_CODE_indirect_pointer = 18; /* Level 2 DIEs. */ - public static final int DW_ABBREV_CODE_method_declaration1 = 19; - public static final int DW_ABBREV_CODE_method_declaration2 = 20; + public static final int DW_ABBREV_CODE_method_declaration = 19; + public static final int DW_ABBREV_CODE_method_declaration_static = 20; public static final int DW_ABBREV_CODE_field_declaration1 = 21; public static final int DW_ABBREV_CODE_field_declaration2 = 22; public static final int DW_ABBREV_CODE_field_declaration3 = 23; @@ -100,6 +101,11 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 29; public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 30; public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 31; + + /* Level X DIEs */ + public static final int DW_ABBREV_CODE_inlined_subroutine = 33; + public static final int DW_ABBREV_CODE_inlined_subroutine_with_children = 34; + /* * Define all the Dwarf tags we need for our DIEs. */ @@ -116,6 +122,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_TAG_subprogram = 0x2e; public static final int DW_TAG_variable = 0x34; public static final int DW_TAG_unspecified_type = 0x3b; + public static final int DW_TAG_inlined_subroutine = 0x1d; /* * Define all the Dwarf attributes we need for our DIEs. @@ -131,6 +138,8 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_AT_language = 0x13; public static final int DW_AT_comp_dir = 0x1b; public static final int DW_AT_containing_type = 0x1d; + public static final int DW_AT_inline = 0x20; + public static final int DW_AT_abstract_origin = 0x31; public static final int DW_AT_accessibility = 0x32; public static final int DW_AT_artificial = 0x34; public static final int DW_AT_data_member_location = 0x38; @@ -146,6 +155,8 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_AT_type = 0x49; public static final int DW_AT_data_location = 0x50; public static final int DW_AT_use_UTF8 = 0x53; + public static final int DW_AT_call_file = 0x58; + public static final int DW_AT_call_line = 0x59; public static final int DW_AT_object_pointer = 0x64; /* @@ -158,6 +169,10 @@ public class DwarfDebugInfo extends DebugInfoBase { @SuppressWarnings("unused") public static final int DW_FORM_data8 = 0x7; @SuppressWarnings("unused") private static final int DW_FORM_string = 0x8; @SuppressWarnings("unused") public static final int DW_FORM_block1 = 0x0a; + @SuppressWarnings("unused") public static final int DW_FORM_ref1 = 0x11; + @SuppressWarnings("unused") public static final int DW_FORM_ref2 = 0x12; + public static final int DW_FORM_ref4 = 0x13; + @SuppressWarnings("unused") public static final int DW_FORM_ref8 = 0x14; public static final int DW_FORM_ref_addr = 0x10; public static final int DW_FORM_data1 = 0x0b; public static final int DW_FORM_flag = 0xc; @@ -181,6 +196,13 @@ public class DwarfDebugInfo extends DebugInfoBase { * Value for DW_AT_language attribute with form DATA1. */ public static final byte DW_LANG_Java = 0xb; + /* + * Values for {@link DW_AT_inline} attribute with form DATA1. + */ + @SuppressWarnings("unused") public static final byte DW_INL_not_inlined = 0; + public static final byte DW_INL_inlined = 1; + @SuppressWarnings("unused") public static final byte DW_INL_declared_not_inlined = 2; + @SuppressWarnings("unused") public static final byte DW_INL_declared_inlined = 3; /* * DW_AT_Accessibility attribute values. 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 51cc50b33fb0..d0fc0c90c735 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 @@ -679,7 +679,7 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, int modifiers = method.getModifiers(); boolean isStatic = Modifier.isStatic(modifiers); log(context, " [0x%08x] method declaration %s", pos, methodKey); - int abbrevCode = (isStatic ? DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2 : DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1); + int abbrevCode = (isStatic ? DwarfDebugInfo.DW_ABBREV_CODE_method_declaration_static : DwarfDebugInfo.DW_ABBREV_CODE_method_declaration); log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] external true", pos); @@ -708,7 +708,7 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, int typeIdx = getLayoutIndex(classEntry); log(context, " [0x%08x] containing_type 0x%x (%s)", pos, typeIdx, classEntry.getTypeName()); pos = writeAttrRefAddr(typeIdx, buffer, pos); - if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1) { + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration) { /* Record the current position so we can back patch the object pointer. */ int objectPointerIndex = pos; /* @@ -1230,13 +1230,23 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] method location", pos); - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; - log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + int abbrevCode; + if (range.isInlined()) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline; + } else { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; + } + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, 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); + if (range.isInlined()) { + verboseLog(context, " [0x%08x] inline 0x%X", pos, DwarfDebugInfo.DW_INL_inlined); + pos = writeAttrData1(DwarfDebugInfo.DW_INL_inlined, buffer, pos); + } else { + 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); + } /* * Should pass true only if method is non-private. */ @@ -1253,6 +1263,51 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Ran return writeAttrNull(buffer, pos); } + private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p, int subprogramOffset, int depth) { + assert range.isInlined(); + int pos = p; + final Range callerSubrange = range.getCaller(); + assert callerSubrange != null; + int callLine = callerSubrange.getLine(); + assert callLine >= -1 : callLine; + if (callLine == -1) { + log(context, " Unable to retrieve call line for inlined method %s", range.getFullMethodName()); + return p; + } + Integer fileIndex; + if (callerSubrange == range) { + fileIndex = 1; + } else { + FileEntry subFileEntry = callerSubrange.getFileEntry(); + assert subFileEntry != null; + fileIndex = classEntry.localFilesIdx(subFileEntry); + assert fileIndex != null; + } + final int code; + if (range.withChildren()) { + code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; + } else { + code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; + } + verboseLog(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 2, code); + pos = writeAbbrevCode(code, buffer, pos); + verboseLog(context, " [0x%08x] abstract_origin 0x%X", pos, subprogramOffset); + pos = writeAttrRef4(subprogramOffset, buffer, pos); + verboseLog(context, " [0x%08x] lo_pc 0x%08x", pos, range.getLo()); + pos = writeAttrAddress(range.getLo(), buffer, pos); + verboseLog(context, " [0x%08x] hi_pc 0x%08x", pos, range.getHi()); + pos = writeAttrAddress(range.getHi(), buffer, pos); + verboseLog(context, " [0x%08x] call_file %d", pos, fileIndex); + pos = writeAttrData4(fileIndex, buffer, pos); + verboseLog(context, " [0x%08x] call_line %d", pos, callLine); + pos = writeAttrData4(callLine, buffer, pos); + return pos; + } + + private int writeAttrRef4(int reference, byte[] buffer, int p) { + return writeAttrData4(reference, buffer, p); + } + private int writeCUHeader(byte[] buffer, int p) { int pos = p; if (buffer == null) { From d36246115cf605ce8cc4a5a571330ff41efbc565 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 30 Sep 2020 03:01:16 +0300 Subject: [PATCH 04/27] Create subranges for nested inlined callers --- .../objectfile/debugentry/DebugInfoBase.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) 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 8f18ef4aae0b..8864e93fd802 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 @@ -259,13 +259,14 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); + Range caller = addCallersSubRanges(debugLineInfo.getCaller(), primaryRange, classEntry, debugContext); /* * 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. */ ClassEntry subClassEntry = ensureClassEntry(classNameAtLine); MethodEntry subMethodEntry = subClassEntry.ensureMethodEntryForDebugRangeInfo(debugLineInfo, this, debugContext); - Range subRange = new Range(stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, null); + Range subRange = new Range(stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, caller); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine); @@ -351,6 +352,42 @@ ClassEntry lookupClassEntry(String typeName) { return (ClassEntry) typeEntry; } + /** + * Recursively creates the inlined caller subranges for nested inline subranges. + * + * @param lineInfo + * @param primaryRange + * @param classEntry + * @param debugContext + * @return the subrange for {@code lineInfo} linked with all its caller subranges up to the + * primaryRange + */ + @SuppressWarnings("try") + private Range addCallersSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Range primaryRange, ClassEntry classEntry, DebugContext debugContext) { + /* Don't process the root method, it is already added as the primary range */ + if (lineInfo == null || lineInfo.getCaller() == null) { + assert lineInfo == null || (lineInfo.name().equals(primaryRange.getMethodName()) && lineInfo.ownerType().equals(primaryRange.getClassName())); + return primaryRange; + } + Range caller = addCallersSubRanges(lineInfo.getCaller(), primaryRange, classEntry, debugContext); + final String fileName = lineInfo.fileName(); + final Path filePath = lineInfo.filePath(); + final String className = 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(); + ClassEntry subClassEntry = ensureClassEntry(className); + MethodEntry subMethodEntry = subClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); + Range subRange = new Range(stringTable, subMethodEntry, lo, hi, line, primaryRange, true, true, caller); + classEntry.indexSubRange(subRange); + try (DebugContext.Scope s = debugContext.scope("Subranges")) { + debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", + className, methodName, filePath, fileName, line, lo, hi); + } + return subRange; + } + private ClassEntry ensureClassEntry(String className) { /* See if we already have an entry. */ ClassEntry classEntry = primaryClassesIndex.get(className); From f0c5eca55d71cd82c08b653eaaa7c7127b2bf91a Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 30 Sep 2020 03:03:35 +0300 Subject: [PATCH 05/27] Add dwarf info for nested inlined subroutines --- .../elf/dwarf/DwarfInfoSectionImpl.java | 70 +++++++++++++++---- 1 file changed, 58 insertions(+), 12 deletions(-) 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 d0fc0c90c735..57a576bec8dd 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 @@ -28,6 +28,7 @@ import java.lang.reflect.Modifier; import java.util.List; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -474,7 +475,7 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b /* Write all method locations. */ - pos = writeMethodLocations(context, classEntry, buffer, pos); + pos = writeMethodLocations(context, classEntry, false, buffer, pos); /* Write all static field definitions. */ @@ -921,16 +922,65 @@ private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfa return pos; } - private int writeMethodLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + private int writeMethodLocations(DebugContext context, ClassEntry classEntry, boolean deoptTargets, byte[] buffer, int p) { int pos = p; List classPrimaryEntries = classEntry.getPrimaryEntries(); + + /* Keep a map of method names to the corresponding position of their subprogram entry. */ + HashMap primaryMap = new HashMap<>(); + /* The primary file entry should always be first in the local files list. */ + assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; + for (PrimaryEntry primaryEntry : classPrimaryEntries) { Range range = primaryEntry.getPrimary(); - if (!range.isDeoptTarget()) { - pos = writeMethodLocation(context, classEntry, range, buffer, pos); + if (range.isDeoptTarget() != deoptTargets) { + continue; } + boolean withInlinedMethods = false; + /* + * Go through the subranges and generate abstract debug entries for inlined methods. + */ + for (Range subrange : primaryEntry.getSubranges()) { + if (!subrange.isInlined()) { + continue; + } + withInlinedMethods = true; + final String symbolName = subrange.getSymbolName(); + if (primaryMap.get(symbolName) == null) { + primaryMap.put(symbolName, pos); + ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); + pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); + pos = writeAttrNull(buffer, pos); + } + } + primaryMap.put(range.getSymbolName(), pos); + pos = writeMethodLocation(context, classEntry, range, buffer, pos); + if (withInlinedMethods) { + int depth = 0; + /* + * Go through the subranges and generate concrete debug entries for inlined methods. + */ + for (Range subrange : primaryEntry.getSubranges()) { + if (!subrange.isInlined()) { + continue; + } + Integer subprogramPos = primaryMap.get(subrange.getSymbolName()); + assert subprogramPos != null; + int previousPos = pos; + pos = writeInlineSubroutine(context, classEntry, subrange, buffer, pos, subprogramPos - getCUIndex(classEntry), depth); + if (!subrange.withChildren()) { + while (depth > 0) { + pos = writeAttrNull(buffer, pos); + depth--; + } + } else if (previousPos != pos) { + depth++; + } + } + assert depth == 0 : depth; + } + pos = writeAttrNull(buffer, pos); } - return pos; } @@ -1192,6 +1242,7 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt int pos = p; assert classEntry.includesDeoptTarget(); List classPrimaryEntries = classEntry.getPrimaryEntries(); + assert !classPrimaryEntries.isEmpty(); String fileName = classEntry.getFileName(); int lineIndex = getLineIndex(classEntry); int abbrevCode = (fileName.length() > 0 ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit2); @@ -1215,12 +1266,7 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt pos = writeAttrData4(lineIndex, buffer, pos); } - for (PrimaryEntry primaryEntry : classPrimaryEntries) { - Range range = primaryEntry.getPrimary(); - if (range.isDeoptTarget()) { - pos = writeMethodLocation(context, classEntry, range, buffer, pos); - } - } + pos = writeMethodLocations(context, classEntry, true, buffer, pos); /* * Write a terminating null attribute. */ @@ -1260,7 +1306,7 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Ran /* * Write a terminating null attribute. */ - return writeAttrNull(buffer, pos); + return pos; } private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p, int subprogramOffset, int depth) { From 46ec35cbee2bac09232e3e04bb7a822306248063 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 7 Oct 2020 03:25:34 +0300 Subject: [PATCH 06/27] DebugInfo fix file association for corner case If line gets successfully retrieved from subrange instead of primaryrange get file index from the subrange since the line might be from a different file for inlined methods. --- .../objectfile/elf/dwarf/DwarfLineSectionImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 45f493f22ca8..847df5184907 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 @@ -455,7 +455,18 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by */ long line = primaryRange.getLine(); if (line < 0 && primaryEntry.getSubranges().size() > 0) { - line = primaryEntry.getSubranges().get(0).getLine(); + final Range subRange = primaryEntry.getSubranges().get(0); + 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); + } + } } if (line < 0) { line = 0; From ddd6d139049cea45a299e702a7d69dca10b749d6 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 16 Mar 2021 19:00:31 +0200 Subject: [PATCH 07/27] Update debuginfotest to work with new debug info --- substratevm/mx.substratevm/testhello.py | 27 ++++++++++--------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 1861b8958520..1c6ca778ac4a 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -124,6 +124,7 @@ def test(): # define some useful patterns address_pattern = '0x[0-9a-f]+' + hash_pattern = '[0-9a-f]+' spaces_pattern = '[ \t]+' maybe_spaces_pattern = '[ \t]*' digits_pattern = '[0-9]+' @@ -189,8 +190,10 @@ def test(): exec_string = execute("backtrace") checker = Checker("backtrace hello.Hello::main", [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, wildcard_pattern), - r"#1%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern) - ]) + r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), + r"#2%s com\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), + r"#3%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, hash_pattern, wildcard_pattern, package_pattern) + ]) checker.check(exec_string, skip_fails=False) if can_print_data: @@ -293,16 +296,9 @@ def test(): # expect "All functions matching regular expression "java.io.PrintStream.println":" # expect "" # expect "File java.base/java/io/PrintStream.java:" - # expect " void java.io.PrintStream::println(java.lang.Object);" - # expect " void java.io.PrintStream::println(java.lang.String);" + # expect " void java.io.PrintStream::println(java.lang.Object *);" + # expect " void java.io.PrintStream::println(java.lang.String *);" exec_string = execute("info func java.io.PrintStream::println") - # checker = Checker("info func java.io.PrintStream::println", - # ["All functions matching regular expression \"java\\.io\\.PrintStream::println\":", - # "", - # "File .*java/io/PrintStream.java:", - # "[ \t]*void java.io.PrintStream::println\\(java\\.lang\\.Object \\*\\);", - # "[ \t]*void java.io.PrintStream::println\\(java\\.lang\\.String \\*\\);", - # ]) rexp = r"%svoid java.io.PrintStream::println\(java\.lang\.String \*\)"%maybe_spaces_pattern checker = Checker("info func java.io.PrintStream::println", rexp) checker.check(exec_string) @@ -399,15 +395,14 @@ def test(): checker.check(exec_string, skip_fails=True) # run a backtrace - # expect "#0 _hello.Hello$Greeter::greeter(java.lang.String[]).* at hello.Hello.java:34" - # expect "#1 0x[0-9a-f]+ in _hello.Hello::main(java.lang.String[]).* at hello.Hello.java:67" - # expect "#2 0x[0-9a-f]+ in _com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_.* at [a-z/]+/JavaMainWrapper.java:[0-9]+" exec_string = execute("backtrace") checker = Checker("backtrace hello.Hello.Greeter::greeter", [r"#0%shello\.Hello\$Greeter::greeter\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:34"%(spaces_pattern, wildcard_pattern), r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, address_pattern, wildcard_pattern), - r"#2%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s at [a-z/]+/JavaMainWrapper\.java:%s"%(spaces_pattern, address_pattern, wildcard_pattern, digits_pattern) - ]) + r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), + r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), + r"#4%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, hash_pattern, wildcard_pattern, package_pattern) + ]) checker.check(exec_string, skip_fails=False) # now step into inlined code From abe98eaaa6dd772b7419b819fa342e810a0939e0 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 29 Jun 2021 13:38:49 +0300 Subject: [PATCH 08/27] Optimize debug info generation with -H:+OmitInlinedMethodDebugLineInfo When using -H:+OmitInlinedMethodDebugLineInfo we know that ranges are not expected to have inlined subranges so we can avoid looping over them to see if we need to generate inline debug info. --- .../objectfile/debugentry/DebugInfoBase.java | 1 + .../oracle/objectfile/debugentry/Range.java | 10 ++++++ .../elf/dwarf/DwarfInfoSectionImpl.java | 32 +++++++++---------- 3 files changed, 27 insertions(+), 16 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 8864e93fd802..60ade4873b75 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 @@ -370,6 +370,7 @@ private Range addCallersSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Rang return primaryRange; } Range caller = addCallersSubRanges(lineInfo.getCaller(), primaryRange, classEntry, debugContext); + caller.setWithInlinedChildren(true); final String fileName = lineInfo.fileName(); final Path filePath = lineInfo.filePath(); final String className = lineInfo.ownerType(); 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 3fdd573de099..037024f17551 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 @@ -43,6 +43,7 @@ public class Range { private final int line; private final boolean isInlined; private final boolean withChildren; + private boolean withInlinedChildren; /* * This is null for a primary range. */ @@ -73,6 +74,7 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i this.isInlined = isInline; this.primary = primary; this.withChildren = withChildren; + this.withInlinedChildren = false; this.caller = caller; } @@ -184,6 +186,14 @@ public boolean withChildren() { return withChildren; } + public boolean withInlinedChildren() { + return withInlinedChildren; + } + + public boolean setWithInlinedChildren(boolean withInlinedChildren) { + return this.withInlinedChildren = withInlinedChildren; + } + public Range getCaller() { return caller; } 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 57a576bec8dd..f46dad5b5108 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 @@ -936,26 +936,26 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo if (range.isDeoptTarget() != deoptTargets) { continue; } - boolean withInlinedMethods = false; - /* - * Go through the subranges and generate abstract debug entries for inlined methods. - */ - for (Range subrange : primaryEntry.getSubranges()) { - if (!subrange.isInlined()) { - continue; - } - withInlinedMethods = true; - final String symbolName = subrange.getSymbolName(); - if (primaryMap.get(symbolName) == null) { - primaryMap.put(symbolName, pos); - ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); - pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); - pos = writeAttrNull(buffer, pos); + if (range.withInlinedChildren()) { + /* + * Go through the subranges and generate abstract debug entries for inlined methods. + */ + for (Range subrange : primaryEntry.getSubranges()) { + if (!subrange.isInlined()) { + continue; + } + final String symbolName = subrange.getSymbolName(); + if (primaryMap.get(symbolName) == null) { + primaryMap.put(symbolName, pos); + ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); + pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); + pos = writeAttrNull(buffer, pos); + } } } primaryMap.put(range.getSymbolName(), pos); pos = writeMethodLocation(context, classEntry, range, buffer, pos); - if (withInlinedMethods) { + if (range.withInlinedChildren()) { int depth = 0; /* * Go through the subranges and generate concrete debug entries for inlined methods. From cdeb9010c02bbdb7527796f482162c4538162338 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 29 Jun 2021 13:48:51 +0300 Subject: [PATCH 09/27] Refactor: Improve readability of DwarfInfoSectionImpl#writeMethodLocations --- .../elf/dwarf/DwarfInfoSectionImpl.java | 97 +++++++++++-------- 1 file changed, 57 insertions(+), 40 deletions(-) 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 f46dad5b5108..bfeebf90821e 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 @@ -936,50 +936,67 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo if (range.isDeoptTarget() != deoptTargets) { continue; } - if (range.withInlinedChildren()) { - /* - * Go through the subranges and generate abstract debug entries for inlined methods. - */ - for (Range subrange : primaryEntry.getSubranges()) { - if (!subrange.isInlined()) { - continue; - } - final String symbolName = subrange.getSymbolName(); - if (primaryMap.get(symbolName) == null) { - primaryMap.put(symbolName, pos); - ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); - pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); - pos = writeAttrNull(buffer, pos); - } - } - } + pos = maybeGenerateAbstractDebugEntriesForInlinedMethods(context, primaryMap, primaryEntry, buffer, pos); primaryMap.put(range.getSymbolName(), pos); pos = writeMethodLocation(context, classEntry, range, buffer, pos); - if (range.withInlinedChildren()) { - int depth = 0; - /* - * Go through the subranges and generate concrete debug entries for inlined methods. - */ - for (Range subrange : primaryEntry.getSubranges()) { - if (!subrange.isInlined()) { - continue; - } - Integer subprogramPos = primaryMap.get(subrange.getSymbolName()); - assert subprogramPos != null; - int previousPos = pos; - pos = writeInlineSubroutine(context, classEntry, subrange, buffer, pos, subprogramPos - getCUIndex(classEntry), depth); - if (!subrange.withChildren()) { - while (depth > 0) { - pos = writeAttrNull(buffer, pos); - depth--; - } - } else if (previousPos != pos) { - depth++; - } + pos = maybeGenerateConcreteDebugEntriesForInlinedMethods(context, classEntry, primaryMap, primaryEntry, buffer, pos); + pos = writeAttrNull(buffer, pos); + } + return pos; + } + + /** + * Go through the subranges and generate concrete debug entries for inlined methods. + */ + private int maybeGenerateConcreteDebugEntriesForInlinedMethods(DebugContext context, ClassEntry classEntry, + HashMap primaryMap, PrimaryEntry primaryEntry, byte[] buffer, int p) { + if (!primaryEntry.getPrimary().withInlinedChildren()) { + return p; + } + int pos = p; + int depth = 0; + for (Range subrange : primaryEntry.getSubranges()) { + if (!subrange.isInlined()) { + continue; + } + Integer subprogramPos = primaryMap.get(subrange.getSymbolName()); + assert subprogramPos != null; + int previousPos = pos; + int subprogramOffset = subprogramPos - getCUIndex(classEntry); + pos = writeInlineSubroutine(context, classEntry, subrange, buffer, pos, subprogramOffset, depth); + if (!subrange.withChildren()) { + while (depth > 0) { + pos = writeAttrNull(buffer, pos); + depth--; } - assert depth == 0 : depth; + } else if (previousPos != pos) { + depth++; + } + } + assert depth == 0 : depth; + return pos; + } + + /** + * Go through the subranges and generate abstract debug entries for inlined methods. + */ + private int maybeGenerateAbstractDebugEntriesForInlinedMethods(DebugContext context, + HashMap primaryMap, PrimaryEntry primaryEntry, byte[] buffer, int p) { + if (!primaryEntry.getPrimary().withInlinedChildren()) { + return p; + } + int pos = p; + for (Range subrange : primaryEntry.getSubranges()) { + if (!subrange.isInlined()) { + continue; + } + final String symbolName = subrange.getSymbolName(); + if (primaryMap.get(symbolName) == null) { + primaryMap.put(symbolName, pos); + ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); + pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); + pos = writeAttrNull(buffer, pos); } - pos = writeAttrNull(buffer, pos); } return pos; } From 219a2e3cf13fd7b5101af6e4ec8ffc384724fd9a Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 20 Jul 2021 15:03:15 +0300 Subject: [PATCH 10/27] Refactor: Use types to decide if a method is inlined, used, etc. --- .../objectfile/debugentry/ClassEntry.java | 20 +++-- .../objectfile/debugentry/MethodEntry.java | 80 ++++++++++++++----- .../oracle/objectfile/debugentry/Range.java | 2 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 6 +- 4 files changed, 80 insertions(+), 28 deletions(-) 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 2057fde4a297..b44819ad3c1e 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 @@ -38,6 +38,8 @@ 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.DebugCodeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugRangeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; @@ -137,7 +139,7 @@ public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInf /* Add details of fields and field types */ debugInstanceTypeInfo.fieldInfoProvider().forEach(debugFieldInfo -> this.processField(debugFieldInfo, debugInfoBase, debugContext)); /* Add details of methods and method types */ - debugInstanceTypeInfo.methodInfoProvider().forEach(methodFieldInfo -> this.methods.add(this.processMethod(methodFieldInfo, debugInfoBase, debugContext, false))); + debugInstanceTypeInfo.methodInfoProvider().forEach(debugMethodInfo -> this.methods.add(this.processMethod(debugMethodInfo, debugInfoBase, debugContext))); /* Sort methods to improve lookup speed */ this.methods.sort(MethodEntry::compareTo); } @@ -273,7 +275,15 @@ private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, interfaceClassEntry.addImplementor(this, debugContext); } - protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext, boolean fromRangeInfo) { + protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + boolean fromRange = debugMethodInfo instanceof DebugCodeInfo; + boolean fromInlineRange = false; + if (debugMethodInfo instanceof DebugLineInfo) { + DebugLineInfo lineInfo = (DebugLineInfo) debugMethodInfo; + if (lineInfo.getCaller() != null) { + fromInlineRange = true; + } + } String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); int modifiers = debugMethodInfo.modifiers(); @@ -298,7 +308,7 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(debugMethodInfo); return new MethodEntry(methodFileEntry, debugMethodInfo.symbolNameForMethod(), methodName, this, resultType, - paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget(), fromRangeInfo); + paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget(), fromRange, fromInlineRange); } @Override @@ -349,7 +359,7 @@ public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeI MethodEntry methodEntry = methodIterator.next(); int comparisonResult = methodEntry.compareTo(methodName, paramSignature, returnTypeName); if (comparisonResult == 0) { - methodEntry.setInRangeAndUpdateFileEntry(debugInfoBase, debugRangeInfo); + methodEntry.updateRangeInfo(debugInfoBase, debugRangeInfo); if (methodEntry.fileEntry != null) { /* Ensure that the methodEntry's fileEntry is present in the localsFileIndex */ indexLocalFileEntry(methodEntry.fileEntry); @@ -360,7 +370,7 @@ public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeI break; } } - MethodEntry newMethodEntry = processMethod(debugRangeInfo, debugInfoBase, debugContext, true); + MethodEntry newMethodEntry = processMethod(debugRangeInfo, debugInfoBase, debugContext); methodIterator.add(newMethodEntry); return newMethodEntry; } 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 ab8471a6a348..ee9844a34137 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,6 +26,8 @@ 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.DebugRangeInfo; import java.util.Arrays; @@ -34,23 +36,32 @@ public class MethodEntry extends MemberEntry implements Comparable { final TypeEntry[] paramTypes; final String[] paramNames; - public final boolean isDeoptTarget; - boolean isInRange; - + static final int DEOPT = 1 << 0; + static final int IN_RANGE = 1 << 1; + static final int INLINED = 1 << 2; + int flags; final String symbolName; private String signature; public MethodEntry(FileEntry fileEntry, String symbolName, String methodName, ClassEntry ownerType, TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers, - boolean isDeoptTarget, boolean isInRange) { + boolean isDeoptTarget, boolean fromRange, boolean fromInlineRange) { super(fileEntry, methodName, ownerType, valueType, modifiers); assert ((paramTypes == null && paramNames == null) || (paramTypes != null && paramNames != null && paramTypes.length == paramNames.length)); this.paramTypes = paramTypes; this.paramNames = paramNames; - this.isDeoptTarget = isDeoptTarget; - this.isInRange = isInRange; this.symbolName = symbolName; + this.flags = 0; + if (isDeoptTarget) { + setIsDeopt(); + } + if (fromRange) { + setIsInRange(); + } + if (fromInlineRange) { + setIsInlined(); + } } public String methodName() { @@ -91,8 +102,28 @@ public String getParamName(int idx) { return paramNames[idx]; } + private void setIsDeopt() { + flags |= DEOPT; + } + + public boolean isDeopt() { + return (flags & DEOPT) != 0; + } + + private void setIsInRange() { + flags |= IN_RANGE; + } + public boolean isInRange() { - return isInRange; + return (flags & IN_RANGE) != 0; + } + + private void setIsInlined() { + flags |= INLINED; + } + + public boolean isInlined() { + return (flags & INLINED) != 0; } /** @@ -105,19 +136,30 @@ public boolean isInRange() { * @param debugInfoBase * @param debugRangeInfo */ - public void setInRangeAndUpdateFileEntry(DebugInfoBase debugInfoBase, DebugRangeInfo debugRangeInfo) { - if (isInRange) { - assert fileEntry == debugInfoBase.ensureFileEntry(debugRangeInfo); - return; + public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugRangeInfo debugRangeInfo) { + if (debugRangeInfo instanceof DebugLineInfo) { + DebugLineInfo lineInfo = (DebugLineInfo) debugRangeInfo; + if (lineInfo.getCaller() != null) { + /* this is a real inlined method not just a top level primary range */ + setIsInlined(); + } + } else if (debugRangeInfo instanceof DebugCodeInfo) { + /* this method has been seen in a primary range */ + if (isInRange()) { + /* it has already been seen -- just check for consistency */ + assert fileEntry == debugInfoBase.ensureFileEntry(debugRangeInfo); + } else { + /* + * If the MethodEntry was added by traversing the DeclaredMethods of a Class its + * fileEntry may point to the original source file, which will be wrong for + * substituted methods. As a result when setting a MethodEntry as isInRange we also + * make sure that its fileEntry reflects the file info associated with the + * corresponding Range. + */ + setIsInRange(); + fileEntry = debugInfoBase.ensureFileEntry(debugRangeInfo); + } } - isInRange = true; - /* - * If the MethodEntry was added by traversing the DeclaredMethods of a Class its fileEntry - * will point to the original source file, thus it will be wrong for substituted methods. As - * a result when setting a MethodEntry as isInRange we also make sure that its fileEntry - * reflects the file info associated with the corresponding Range. - */ - fileEntry = debugInfoBase.ensureFileEntry(debugRangeInfo); } public String getSymbolName() { 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 037024f17551..0ed8775cff60 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 @@ -123,7 +123,7 @@ public String getFullMethodNameWithParams() { } public boolean isDeoptTarget() { - return methodEntry.isDeoptTarget; + return methodEntry.isDeopt(); } private String getExtendedMethodName(boolean includeClass, boolean includeParams, boolean includeReturnType) { 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 bfeebf90821e..687da9aec1f5 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 @@ -661,7 +661,7 @@ private int writeField(DebugContext context, StructureTypeEntry entry, FieldEntr private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; for (MethodEntry method : classEntry.getMethods()) { - if (method.isInRange()) { + if (method.isInRange() || method.isInlined()) { /* * Declare all methods including deopt targets even though they are written in * separate CUs. @@ -700,8 +700,8 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, int retTypeIdx = getTypeIndex(returnTypeName); log(context, " [0x%08x] type 0x%x (%s)", pos, retTypeIdx, returnTypeName); pos = writeAttrRefAddr(retTypeIdx, buffer, pos); - log(context, " [0x%08x] artificial %s", pos, method.isDeoptTarget ? "true" : "false"); - pos = writeFlag((method.isDeoptTarget ? (byte) 1 : (byte) 0), buffer, pos); + log(context, " [0x%08x] artificial %s", pos, method.isDeopt() ? "true" : "false"); + pos = writeFlag((method.isDeopt() ? (byte) 1 : (byte) 0), buffer, pos); log(context, " [0x%08x] accessibility %s", pos, "public"); pos = writeAttrAccessibility(modifiers, buffer, pos); log(context, " [0x%08x] declaration true", pos); From 1334190298f7050df0c3727463f1cbf4691b5737 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 20 Jul 2021 14:40:47 +0300 Subject: [PATCH 11/27] Generate abstract inline DIEs in classes' CUs This way we save space and time by not replicating abstract inline DIEs in the caller's CU. --- substratevm/mx.substratevm/testhello.py | 4 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 2 +- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 32 ++++- .../elf/dwarf/DwarfInfoSectionImpl.java | 126 ++++++++++-------- .../elf/dwarf/DwarfSectionImpl.java | 11 ++ 5 files changed, 114 insertions(+), 61 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 1c6ca778ac4a..c20fa718519d 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -192,7 +192,7 @@ def test(): [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, wildcard_pattern), r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#2%s com\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#3%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, hash_pattern, wildcard_pattern, package_pattern) + r"#3%smain \(\) at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, package_pattern) ]) checker.check(exec_string, skip_fails=False) @@ -401,7 +401,7 @@ def test(): r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, address_pattern, wildcard_pattern), r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#4%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, hash_pattern, wildcard_pattern, package_pattern) + r"#4%smain \(\) at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, package_pattern) ]) checker.check(exec_string, skip_fails=False) 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 68b52d2f4397..f741a8fe26dc 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 @@ -1349,7 +1349,7 @@ private int writeInlinedSubroutineAbbrevs(byte[] buffer, int p, boolean withChil pos = writeTag(DwarfDebugInfo.DW_TAG_inlined_subroutine, buffer, pos); pos = writeFlag(withChildren ? DwarfDebugInfo.DW_CHILDREN_yes : DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_abstract_origin, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); 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 27bd7a03af97..d700522f916c 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 @@ -171,7 +171,7 @@ public class DwarfDebugInfo extends DebugInfoBase { @SuppressWarnings("unused") public static final int DW_FORM_block1 = 0x0a; @SuppressWarnings("unused") public static final int DW_FORM_ref1 = 0x11; @SuppressWarnings("unused") public static final int DW_FORM_ref2 = 0x12; - public static final int DW_FORM_ref4 = 0x13; + @SuppressWarnings("unused") public static final int DW_FORM_ref4 = 0x13; @SuppressWarnings("unused") public static final int DW_FORM_ref8 = 0x14; public static final int DW_FORM_ref_addr = 0x10; public static final int DW_FORM_data1 = 0x0b; @@ -458,6 +458,10 @@ static class DwarfClassProperties extends DwarfTypeProperties { * Map from method names to info section index for the field declaration. */ private HashMap methodDeclarationIndex; + /** + * Map from method names to info section index for the field declaration. + */ + private HashMap abstractInlineMethodIndex; DwarfClassProperties(StructureTypeEntry entry) { super(entry); @@ -470,6 +474,7 @@ static class DwarfClassProperties extends DwarfTypeProperties { this.lineSectionSize = -1; fieldDeclarationIndex = null; methodDeclarationIndex = null; + abstractInlineMethodIndex = null; } } @@ -722,4 +727,29 @@ public int getMethodDeclarationIndex(ClassEntry classEntry, String methodName) { assert methodDeclarationIndex.get(methodName) != null; return methodDeclarationIndex.get(methodName); } + + public void setAbstractInlineMethodIndex(ClassEntry classEntry, String methodName, int pos) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap abstractInlineMethodIndex = classProperties.abstractInlineMethodIndex; + if (abstractInlineMethodIndex == null) { + classProperties.abstractInlineMethodIndex = abstractInlineMethodIndex = new HashMap<>(); + } + if (abstractInlineMethodIndex.get(methodName) != null) { + assert abstractInlineMethodIndex.get(methodName) == pos : classEntry.getTypeName() + methodName; + } else { + abstractInlineMethodIndex.put(methodName, pos); + } + } + + public int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodName) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap abstractInlineMethodIndex = classProperties.abstractInlineMethodIndex; + assert abstractInlineMethodIndex != null : classEntry.getTypeName() + methodName; + assert abstractInlineMethodIndex.get(methodName) != null : classEntry.getTypeName() + methodName; + return abstractInlineMethodIndex.get(methodName); + } } 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 687da9aec1f5..d885e5113be1 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 @@ -28,7 +28,6 @@ import java.lang.reflect.Modifier; import java.util.List; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -398,6 +397,10 @@ private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry /* Note, for a non-primary there are no method definitions to write. */ + /* Write abstract inline methods. */ + + pos = writeAbstractInlineMethods(context, classEntry, buffer, pos); + /* Write all static field definitions. */ pos = writeStaticFieldLocations(context, classEntry, buffer, pos); @@ -477,6 +480,10 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b pos = writeMethodLocations(context, classEntry, false, buffer, pos); + /* Write abstract inline method locations. */ + + pos = writeAbstractInlineMethods(context, classEntry, buffer, pos); + /* Write all static field definitions. */ pos = writeStaticFieldLocations(context, classEntry, buffer, pos); @@ -926,8 +933,6 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo int pos = p; List classPrimaryEntries = classEntry.getPrimaryEntries(); - /* Keep a map of method names to the corresponding position of their subprogram entry. */ - HashMap primaryMap = new HashMap<>(); /* The primary file entry should always be first in the local files list. */ assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; @@ -936,11 +941,25 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo if (range.isDeoptTarget() != deoptTargets) { continue; } - pos = maybeGenerateAbstractDebugEntriesForInlinedMethods(context, primaryMap, primaryEntry, buffer, pos); - primaryMap.put(range.getSymbolName(), pos); pos = writeMethodLocation(context, classEntry, range, buffer, pos); - pos = maybeGenerateConcreteDebugEntriesForInlinedMethods(context, classEntry, primaryMap, primaryEntry, buffer, pos); - pos = writeAttrNull(buffer, pos); + if (primaryEntry.getPrimary().withInlinedChildren()) { + /* + * the method has inlined ranges so write concrete inlined method entries + */ + pos = generateConcreteInlinedMethods(context, classEntry, primaryEntry, buffer, pos); + } + } + return pos; + } + + private int writeAbstractInlineMethods(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + for (MethodEntry method : classEntry.getMethods()) { + if (method.isInlined()) { + String methodKey = method.getSymbolName(); + setAbstractInlineMethodIndex(classEntry, methodKey, pos); + pos = writeAbstractInlineMethod(context, classEntry, method, buffer, pos); + } } return pos; } @@ -948,56 +967,34 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo /** * Go through the subranges and generate concrete debug entries for inlined methods. */ - private int maybeGenerateConcreteDebugEntriesForInlinedMethods(DebugContext context, ClassEntry classEntry, - HashMap primaryMap, PrimaryEntry primaryEntry, byte[] buffer, int p) { - if (!primaryEntry.getPrimary().withInlinedChildren()) { - return p; - } + private int generateConcreteInlinedMethods(DebugContext context, ClassEntry classEntry, + PrimaryEntry primaryEntry, byte[] buffer, int p) { + Range primary = primaryEntry.getPrimary(); + assert primary.withInlinedChildren(); int pos = p; + log(context, " [0x%08x] concrete entries [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodName()); int depth = 0; for (Range subrange : primaryEntry.getSubranges()) { if (!subrange.isInlined()) { continue; } - Integer subprogramPos = primaryMap.get(subrange.getSymbolName()); - assert subprogramPos != null; + MethodEntry method = subrange.getMethodEntry(); + ClassEntry methodClassEntry = method.ownerType(); + String methodKey = method.getSymbolName(); int previousPos = pos; - int subprogramOffset = subprogramPos - getCUIndex(classEntry); - pos = writeInlineSubroutine(context, classEntry, subrange, buffer, pos, subprogramOffset, depth); + /* 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.withChildren()) { while (depth > 0) { pos = writeAttrNull(buffer, pos); depth--; } } else if (previousPos != pos) { + // increment depth while write the children depth++; } } - assert depth == 0 : depth; - return pos; - } - - /** - * Go through the subranges and generate abstract debug entries for inlined methods. - */ - private int maybeGenerateAbstractDebugEntriesForInlinedMethods(DebugContext context, - HashMap primaryMap, PrimaryEntry primaryEntry, byte[] buffer, int p) { - if (!primaryEntry.getPrimary().withInlinedChildren()) { - return p; - } - int pos = p; - for (Range subrange : primaryEntry.getSubranges()) { - if (!subrange.isInlined()) { - continue; - } - final String symbolName = subrange.getSymbolName(); - if (primaryMap.get(symbolName) == null) { - primaryMap.put(symbolName, pos); - ClassEntry inlinedClassEntry = (ClassEntry) lookupType(subrange.getClassName()); - pos = writeMethodLocation(context, inlinedClassEntry, subrange, buffer, pos); - pos = writeAttrNull(buffer, pos); - } - } return pos; } @@ -1294,22 +1291,13 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Ran int pos = p; log(context, " [0x%08x] method location", pos); int abbrevCode; - if (range.isInlined()) { - abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline; - } else { - abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; - } + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - if (range.isInlined()) { - verboseLog(context, " [0x%08x] inline 0x%X", pos, DwarfDebugInfo.DW_INL_inlined); - pos = writeAttrData1(DwarfDebugInfo.DW_INL_inlined, buffer, pos); - } else { - 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] 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); /* * Should pass true only if method is non-private. */ @@ -1323,10 +1311,34 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Ran /* * Write a terminating null attribute. */ - return pos; + return writeAttrNull(buffer, pos); + } + + private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) { + int pos = p; + String methodKey = method.getSymbolName(); + log(context, " [0x%08x] abstract inline method %s", pos, method.getSymbolName()); + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] inline 0x%x", pos, DwarfDebugInfo.DW_INL_inlined); + pos = writeAttrData1(DwarfDebugInfo.DW_INL_inlined, buffer, pos); + /* + * Should pass true only if method is non-private. + */ + log(context, " [0x%08x] external true", pos); + pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); + 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, method, false, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); } - private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p, int subprogramOffset, int depth) { + private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, int subprogramOffset, int depth, byte[] buffer, int p) { assert range.isInlined(); int pos = p; final Range callerSubrange = range.getCaller(); 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 fdaf82261afe..ce45876549e2 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 @@ -598,4 +598,15 @@ protected int getMethodDeclarationIndex(ClassEntry classEntry, String methodName } return dwarfSections.getMethodDeclarationIndex(classEntry, methodName); } + + protected void setAbstractInlineMethodIndex(ClassEntry classEntry, String methodName, int pos) { + dwarfSections.setAbstractInlineMethodIndex(classEntry, methodName, pos); + } + + protected int getAbstractInlineMethodIndex(ClassEntry classEntry, String methodName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getAbstractInlineMethodIndex(classEntry, methodName); + } } From 99d3c5c80fec4cadaa26b239a16739e0d701f98a Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 15:43:47 +0300 Subject: [PATCH 12/27] Filter out potentially invalid line and caller records * remove line records with zero extent * remove caller marking substitution entry (bci = -1) --- .../image/NativeImageDebugInfoProvider.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 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 95439b6a8ee5..5e6ed24b63ad 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 @@ -995,10 +995,16 @@ public int modifiers() { } private static boolean filterLineInfoSourceMapping(SourceMapping sourceMapping) { - if (!SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue()) { - return true; + NodeSourcePosition sourcePosition = sourceMapping.getSourcePosition(); + /* Don't report line info for zero length ranges. */ + if (sourceMapping.getStartOffset() == sourceMapping.getEndOffset()) { + return false; } - return sourceMapping.getSourcePosition().getCaller() == null; + /* Don't report inline line info unless the user has configured it. */ + if (SubstrateOptions.OmitInlinedMethodDebugLineInfo.getValue() && sourcePosition.getCaller() != null) { + return false; + } + return true; } /** @@ -1023,13 +1029,16 @@ private class NativeImageDebugLineInfo implements DebugLineInfo { } NativeImageDebugLineInfo(NodeSourcePosition position, int lo, int hi) { - int posbci = position.getBCI(); - this.bci = (posbci >= 0 ? posbci : 0); + this.bci = position.getBCI(); this.method = position.getMethod(); this.lo = lo; this.hi = hi; this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); - final NodeSourcePosition callerPosition = position.getCaller(); + NodeSourcePosition callerPosition = position.getCaller(); + /* Skip substitutions with bytecode index -1 */ + while (callerPosition != null && callerPosition.isSubstitution() && callerPosition.getBCI() == -1) { + callerPosition = callerPosition.getCaller(); + } if (callerPosition != null) { callersLineInfo = new NativeImageDebugLineInfo(this, callerPosition); } else { @@ -1138,7 +1147,7 @@ public int addressHi() { @Override public int line() { LineNumberTable lineNumberTable = method.getLineNumberTable(); - if (lineNumberTable != null) { + if (lineNumberTable != null && bci >= 0) { return lineNumberTable.getLineNumber(bci); } return -1; From 8434b31dd88eaadb6800f001b84dbb67ec400947 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 17 Aug 2021 16:42:44 +0300 Subject: [PATCH 13/27] Refactor: Move isInline and isInRange logic in MethodEntry.updateRangeInfo --- .../objectfile/debugentry/ClassEntry.java | 14 +------ .../objectfile/debugentry/MethodEntry.java | 37 ++++++++----------- 2 files changed, 18 insertions(+), 33 deletions(-) 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 b44819ad3c1e..96c3020ba32c 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 @@ -38,8 +38,6 @@ 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.DebugCodeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugRangeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; @@ -276,14 +274,6 @@ private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, } protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { - boolean fromRange = debugMethodInfo instanceof DebugCodeInfo; - boolean fromInlineRange = false; - if (debugMethodInfo instanceof DebugLineInfo) { - DebugLineInfo lineInfo = (DebugLineInfo) debugMethodInfo; - if (lineInfo.getCaller() != null) { - fromInlineRange = true; - } - } String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); int modifiers = debugMethodInfo.modifiers(); @@ -307,8 +297,8 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa * substitution */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(debugMethodInfo); - return new MethodEntry(methodFileEntry, debugMethodInfo.symbolNameForMethod(), methodName, this, resultType, - paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget(), fromRange, fromInlineRange); + return new MethodEntry(debugInfoBase, debugMethodInfo, methodFileEntry, methodName, this, resultType, + paramTypeArray, paramNameArray); } @Override 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 ee9844a34137..326ffdb594b8 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 @@ -28,7 +28,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugRangeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import java.util.Arrays; import java.util.stream.Collectors; @@ -43,25 +43,20 @@ public class MethodEntry extends MemberEntry implements Comparable final String symbolName; private String signature; - public MethodEntry(FileEntry fileEntry, String symbolName, String methodName, ClassEntry ownerType, - TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers, - boolean isDeoptTarget, boolean fromRange, boolean fromInlineRange) { - super(fileEntry, methodName, ownerType, valueType, modifiers); + public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, + FileEntry fileEntry, String methodName, ClassEntry ownerType, + TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames) { + 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.symbolName = symbolName; + this.symbolName = debugMethodInfo.symbolNameForMethod(); this.flags = 0; - if (isDeoptTarget) { + if (debugMethodInfo.isDeoptTarget()) { setIsDeopt(); } - if (fromRange) { - setIsInRange(); - } - if (fromInlineRange) { - setIsInlined(); - } + updateRangeInfo(debugInfoBase, debugMethodInfo); } public String methodName() { @@ -132,22 +127,22 @@ public boolean isInlined() { * to the original source file, thus it will be wrong for substituted methods. As a result when * setting a MethodEntry as isInRange we also make sure that its fileEntry reflects the file * info associated with the corresponding Range. - * + * * @param debugInfoBase - * @param debugRangeInfo + * @param debugMethodInfo */ - public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugRangeInfo debugRangeInfo) { - if (debugRangeInfo instanceof DebugLineInfo) { - DebugLineInfo lineInfo = (DebugLineInfo) debugRangeInfo; + public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo) { + if (debugMethodInfo instanceof DebugLineInfo) { + DebugLineInfo lineInfo = (DebugLineInfo) debugMethodInfo; if (lineInfo.getCaller() != null) { /* this is a real inlined method not just a top level primary range */ setIsInlined(); } - } else if (debugRangeInfo instanceof DebugCodeInfo) { + } else if (debugMethodInfo instanceof DebugCodeInfo) { /* this method has been seen in a primary range */ if (isInRange()) { /* it has already been seen -- just check for consistency */ - assert fileEntry == debugInfoBase.ensureFileEntry(debugRangeInfo); + assert fileEntry == debugInfoBase.ensureFileEntry(debugMethodInfo); } else { /* * If the MethodEntry was added by traversing the DeclaredMethods of a Class its @@ -157,7 +152,7 @@ public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugRangeInfo debugRan * corresponding Range. */ setIsInRange(); - fileEntry = debugInfoBase.ensureFileEntry(debugRangeInfo); + fileEntry = debugInfoBase.ensureFileEntry(debugMethodInfo); } } } From 49b94625da58487a94158504fb67629591aecdda Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 16:45:06 +0300 Subject: [PATCH 14/27] Refactor: Renaming variables --- .../oracle/objectfile/debugentry/DebugInfoBase.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 60ade4873b75..d1b668011dd0 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 @@ -264,9 +264,9 @@ 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. */ - ClassEntry subClassEntry = ensureClassEntry(classNameAtLine); - MethodEntry subMethodEntry = subClassEntry.ensureMethodEntryForDebugRangeInfo(debugLineInfo, this, debugContext); - Range subRange = new Range(stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, caller); + ClassEntry subRangeClassEntry = ensureClassEntry(classNameAtLine); + MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(debugLineInfo, this, debugContext); + Range subRange = new Range(stringTable, subRangeMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, caller); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine); @@ -378,9 +378,9 @@ private Range addCallersSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Rang final int lo = primaryRange.getLo() + lineInfo.addressLo(); final int hi = primaryRange.getLo() + lineInfo.addressHi(); final int line = lineInfo.line(); - ClassEntry subClassEntry = ensureClassEntry(className); - MethodEntry subMethodEntry = subClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); - Range subRange = new Range(stringTable, subMethodEntry, lo, hi, line, primaryRange, true, true, caller); + ClassEntry inlineClassEntry = ensureClassEntry(className); + MethodEntry inlineMethodEntry = inlineClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); + Range subRange = new Range(stringTable, inlineMethodEntry, lo, hi, line, primaryRange, true, true, caller); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", From 57cd7c4cdc9b6efa791b8f897151af2ea6c55089 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 16:54:26 +0300 Subject: [PATCH 15/27] Introduce dwarf abbreviation for non-primary classes + cleanUp + docs --- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 171 ++++++++++++++---- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 78 ++++---- .../elf/dwarf/DwarfInfoSectionImpl.java | 28 ++- 3 files changed, 182 insertions(+), 95 deletions(-) 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 f741a8fe26dc..642b3bbce3d0 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 @@ -98,7 +98,7 @@ public void createContent() { *
  • code = builtin_unit, TAG = compile_unit - Java primitive and header type * compile unit * - *
  • code = class_unit1/2, tag = compile_unit - Java instance type compile + *
  • code = class_unit1/2/3, tag = compile_unit - Java instance type compile * unit * *
  • code = array_unit, tag = compile_unit - Java array type compile unit @@ -124,6 +124,10 @@ public void createContent() { *
  • code = method_location, tag = subprogram , parent = class_unit - Java * method code definition (i.e. location of code) * + *
  • code = abstract_inline_method, tag = subprogram , parent = class_unit - + * Java abstract inline method (i.e. proxy for method definition referenced by concrete + * inline instance) + * *
  • code = static_field_location, tag = variable, parent = class_unit - Java * static field definition (i.e. location of data) * @@ -172,12 +176,17 @@ public void createContent() { *
  • code == interface_implementor, tag == member, parent = interface_layout * - union member typed using class layout of a given implementing class * + *
  • code = inlined_subroutine/inlined_subroutine_with_children, tag = subprogram, + * parent = method_location/inlined_subroutine_with_children - provides range and + * abstract origin for a concrete inline method + * * * *
  • Level 2/3 DIEs * *
  • code == method_parameter_declaration1/2/3, tag == formal_parameter, parent = - * method_declaration1/2, method_location - details of method parameters + * method_declaration1/2, method_location, abstract_inline_method - details of method + * parameters * * Details of each specific DIE contents are as follows: * @@ -245,11 +254,13 @@ public void createContent() { * * * Instance Classes: For each class there is a level 0 DIE defining the class compilation - * unit + * unit. low_pc and hi_pc are only included if the class has compiled methods i.e. for + * variants 1 and 2. stmt_list is is only included if the class has an associated source + * file and may therefore have line info i.e. for variant 1. * *
      * - *
    • abbrev_code == class_unit1/2, tag == DW_TAG_compilation_unit, + *
    • abbrev_code == class_unit1/2/3, tag == DW_TAG_compilation_unit, * has_children * *
    • DW_AT_language : ... DW_FORM_data1 @@ -258,9 +269,11 @@ public void createContent() { * *
    • DW_AT_comp_dir : ... DW_FORM_strp * - *
    • DW_AT_low_pc : ..... DW_FORM_address + *
    • DW_AT_low_pc : ..... DW_FORM_address n.b only for abbrev-code == + * class_unit1/2 * - *
    • DW_AT_hi_pc : ...... DW_FORM_address + *
    • DW_AT_hi_pc : ...... DW_FORM_address n.b only for abbrev-code == + * class_unit1/2 * *
    • DW_AT_use_UTF8 : ... DW_FORM_flag * @@ -482,13 +495,16 @@ public void createContent() { * *
    * - * Method Code Locations: For each method within a class there is a 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. + * Method Code Locations: For each method within a class there will normally be a + * 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. + * + * 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 * *
      * @@ -505,6 +521,73 @@ public void createContent() { * *
    * + * 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 + * 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. + * + * Note that an abstract inline method DIE is generated in the compile unit of the class + * which declares the inlined method whereas a concrete inlined method DIE is generated in + * the compile unit of the class which declares method into which code has been inlined. + * + *
      + * + *
    • abbrev_code == DW_ABBREV_CODE_abstract_inline_method, tag == DW_TAG_subprogram, + * has_children + * + *
    • DW_AT_inline : .......... DW_FORM_data1 + * + *
    • DW_AT_external : ........ DW_FORM_flag + * + *
    • DW_AT_specification : ... DW_FORM_ref_addr + * + *
    + * + * Concrete Inlined Methods: Concrete inlined methods are nested as a tree of children under + * the method_location DIE for the method into which they have been inlined. Each inlined + * method DIE defines an address range that is a subrange of its parent DIE. A + * method_location DIE occurs at depth 1 in a compile unit (class_unit). So, this means that + * for any method which has been inlined into a compiled method at depth K in the inline + * frame stack there will be a corresponding level 2+K DIE that identifies the method that + * was inlined (by referencing the corresponding abstract inline method DIE) and locates the + * call point by citing the file index and line number of its caller. So, if compiled method + * M inlines a call to m1 at source position f0:l0, m1 inlines a call to method m2 at source + * position f1:l1 and m2 inlines a call to m3 at source position f2:l2 then there will be a + * level 2 DIE for the inline code range derived from m1 referencing the abstract entry for + * m1 with f0 and l0 as file and line, a level 3 DIE for the inline code range derived from + * m2 referencing the abstract entry for m2 with f1 and l1 as file and line and a level 3 + * DIE for the inline code range derived from m3 referencing the abstract entry for m3 with + * f2 and l2 as file and line. + * + * Note that a concrete inlined method DIE is generated in the compile unit of the class + * which declares the method into which code has been inlined whereas an abstract inlined + * method DIE is generated in the compile unit of the class which declares of the inlined + * method. + * + *
      + * + *
    • abbrev_code == DW_ABBREV_CODE_inlined_subroutine, tag == DW_TAG_subprogram, + * no_children + * + *
    • abbrev_code == DW_ABBREV_CODE_inlined_subroutine_with_children, tag == + * DW_TAG_subprogram, has_children + * + *
    • DW_AT_abstract_origin : ... DW_FORM_ref_addr + * + *
    • DW_AT_low_pc : ............ DW_FORM_addr + * + *
    • DW_AT_hi_pc : ............. DW_FORM_addr + * + *
    • DW_AT_call_file : ......... DW_FORM_data4 + * + *
    • DW_AT_call_line : ......... DW_FORM_data4 + * + *
    + * * Static Field Locations: For each static field within the class there is a level 1 DIE * providing details of the static field location * @@ -710,13 +793,14 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { pos = writeHeaderFieldAbbrev(context, buffer, pos); pos = writeArrayDataTypeAbbrev(context, buffer, pos); - pos = writeMethodLocationAbbrevs(context, buffer, pos); + pos = writeMethodLocationAbbrev(context, buffer, pos); + pos = writeAbstractInlineMethodAbbrev(context, buffer, pos); pos = writeStaticFieldLocationAbbrev(context, buffer, pos); pos = writeSuperReferenceAbbrev(context, buffer, pos); pos = writeInterfaceImplementorAbbrev(context, buffer, pos); - pos = writeInlinedSubroutineAbbrevs(buffer, pos, false); - pos = writeInlinedSubroutineAbbrevs(buffer, pos, true); + pos = writeInlinedSubroutineAbbrev(buffer, pos, false); + pos = writeInlinedSubroutineAbbrev(buffer, pos, true); /* * if we address rebasing is required then then we need to use indirect layout types @@ -772,10 +856,12 @@ private int writeBuiltInUnitAbbrev(@SuppressWarnings("unused") DebugContext cont private int writeClassUnitAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; - /* class compile unit no line info */ + /* class compile unit with compiled methods and line info */ pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit1, buffer, pos); - /* class compile unit with line info */ + /* class compile unit with line info but without line info */ pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit2, buffer, pos); + /* class compile unit without line info and without line info */ + pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit3, buffer, pos); return pos; } @@ -792,10 +878,12 @@ private int writeClassUnitAbbrev(@SuppressWarnings("unused") DebugContext contex pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_comp_dir, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 || abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit2) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + } if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit1) { pos = writeAttrType(DwarfDebugInfo.DW_AT_stmt_list, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); @@ -1168,29 +1256,34 @@ private int writeArrayDataTypeAbbrev(@SuppressWarnings("unused") DebugContext co return pos; } - private int writeMethodLocationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - - pos = writeMethodLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_location, buffer, pos); - pos = writeMethodLocationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline, buffer, pos); - + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_method_location, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, long abbrevCode, byte[] buffer, int p) { + private int writeAbstractInlineMethodAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_abstract_inline_method, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); - if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline) { - pos = writeAttrType(DwarfDebugInfo.DW_AT_inline, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); - } else { - pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - } + pos = writeAttrType(DwarfDebugInfo.DW_AT_inline, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); @@ -1343,7 +1436,7 @@ private int writeNullAbbrev(@SuppressWarnings("unused") DebugContext context, by return pos; } - private int writeInlinedSubroutineAbbrevs(byte[] buffer, int p, boolean withChildren) { + private int writeInlinedSubroutineAbbrev(byte[] buffer, int p, boolean withChildren) { int pos = p; pos = writeAbbrevCode(withChildren ? DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children : DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_inlined_subroutine, buffer, pos); 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 d700522f916c..8fcad6f32b1d 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 @@ -69,42 +69,42 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_ABBREV_CODE_builtin_unit = 1; public static final int DW_ABBREV_CODE_class_unit1 = 2; public static final int DW_ABBREV_CODE_class_unit2 = 3; - public static final int DW_ABBREV_CODE_array_unit = 4; + public static final int DW_ABBREV_CODE_class_unit3 = 4; + public static final int DW_ABBREV_CODE_array_unit = 5; /* Level 1 DIEs. */ - public static final int DW_ABBREV_CODE_primitive_type = 5; - public static final int DW_ABBREV_CODE_void_type = 6; - public static final int DW_ABBREV_CODE_object_header = 7; - public static final int DW_ABBREV_CODE_class_layout1 = 8; - public static final int DW_ABBREV_CODE_class_layout2 = 9; - public static final int DW_ABBREV_CODE_class_pointer = 10; - public static final int DW_ABBREV_CODE_method_location = 11; - public static final int DW_ABBREV_CODE_method_location_inline = 32; - public static final int DW_ABBREV_CODE_static_field_location = 12; - public static final int DW_ABBREV_CODE_array_layout = 13; - public static final int DW_ABBREV_CODE_array_pointer = 14; - public static final int DW_ABBREV_CODE_interface_layout = 15; - public static final int DW_ABBREV_CODE_interface_pointer = 16; - public static final int DW_ABBREV_CODE_indirect_layout = 17; - public static final int DW_ABBREV_CODE_indirect_pointer = 18; + public static final int DW_ABBREV_CODE_primitive_type = 6; + public static final int DW_ABBREV_CODE_void_type = 7; + public static final int DW_ABBREV_CODE_object_header = 8; + public static final int DW_ABBREV_CODE_class_layout1 = 9; + public static final int DW_ABBREV_CODE_class_layout2 = 10; + public static final int DW_ABBREV_CODE_class_pointer = 11; + public static final int DW_ABBREV_CODE_method_location = 12; + public static final int DW_ABBREV_CODE_abstract_inline_method = 13; + public static final int DW_ABBREV_CODE_static_field_location = 14; + public static final int DW_ABBREV_CODE_array_layout = 15; + public static final int DW_ABBREV_CODE_array_pointer = 16; + public static final int DW_ABBREV_CODE_interface_layout = 17; + public static final int DW_ABBREV_CODE_interface_pointer = 18; + public static final int DW_ABBREV_CODE_indirect_layout = 19; + public static final int DW_ABBREV_CODE_indirect_pointer = 20; /* Level 2 DIEs. */ - public static final int DW_ABBREV_CODE_method_declaration = 19; - public static final int DW_ABBREV_CODE_method_declaration_static = 20; - public static final int DW_ABBREV_CODE_field_declaration1 = 21; - public static final int DW_ABBREV_CODE_field_declaration2 = 22; - public static final int DW_ABBREV_CODE_field_declaration3 = 23; - public static final int DW_ABBREV_CODE_field_declaration4 = 24; - public static final int DW_ABBREV_CODE_header_field = 25; - public static final int DW_ABBREV_CODE_array_data_type = 26; - public static final int DW_ABBREV_CODE_super_reference = 27; - public static final int DW_ABBREV_CODE_interface_implementor = 28; + public static final int DW_ABBREV_CODE_method_declaration = 21; + public static final int DW_ABBREV_CODE_method_declaration_static = 22; + public static final int DW_ABBREV_CODE_field_declaration1 = 23; + public static final int DW_ABBREV_CODE_field_declaration2 = 24; + public static final int DW_ABBREV_CODE_field_declaration3 = 25; + public static final int DW_ABBREV_CODE_field_declaration4 = 26; + public static final int DW_ABBREV_CODE_header_field = 27; + public static final int DW_ABBREV_CODE_array_data_type = 28; + public static final int DW_ABBREV_CODE_super_reference = 29; + public static final int DW_ABBREV_CODE_interface_implementor = 30; + /* 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. */ - public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 29; - public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 30; - public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 31; - - /* Level X DIEs */ - public static final int DW_ABBREV_CODE_inlined_subroutine = 33; - public static final int DW_ABBREV_CODE_inlined_subroutine_with_children = 34; + 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; /* * Define all the Dwarf tags we need for our DIEs. @@ -687,7 +687,7 @@ public void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, classProperties.fieldDeclarationIndex = fieldDeclarationIndex = new HashMap<>(); } if (fieldDeclarationIndex.get(fieldName) != null) { - assert fieldDeclarationIndex.get(fieldName) == pos; + assert fieldDeclarationIndex.get(fieldName) == pos : entry.getTypeName() + fieldName; } else { fieldDeclarationIndex.put(fieldName, pos); } @@ -698,8 +698,8 @@ public int getFieldDeclarationIndex(StructureTypeEntry entry, String fieldName) classProperties = lookupClassProperties(entry); assert classProperties.getTypeEntry() == entry; HashMap fieldDeclarationIndex = classProperties.fieldDeclarationIndex; - assert fieldDeclarationIndex != null; - assert fieldDeclarationIndex.get(fieldName) != null; + assert fieldDeclarationIndex != null : fieldName; + assert fieldDeclarationIndex.get(fieldName) != null : entry.getTypeName() + fieldName; return fieldDeclarationIndex.get(fieldName); } @@ -712,7 +712,7 @@ public void setMethodDeclarationIndex(ClassEntry classEntry, String methodName, classProperties.methodDeclarationIndex = methodDeclarationIndex = new HashMap<>(); } if (methodDeclarationIndex.get(methodName) != null) { - assert methodDeclarationIndex.get(methodName) == pos; + assert methodDeclarationIndex.get(methodName) == pos : classEntry.getTypeName() + methodName; } else { methodDeclarationIndex.put(methodName, pos); } @@ -723,8 +723,8 @@ public int getMethodDeclarationIndex(ClassEntry classEntry, String methodName) { classProperties = lookupClassProperties(classEntry); assert classProperties.getTypeEntry() == classEntry; HashMap methodDeclarationIndex = classProperties.methodDeclarationIndex; - assert methodDeclarationIndex != null; - assert methodDeclarationIndex.get(methodName) != null; + assert methodDeclarationIndex != null : classEntry.getTypeName() + methodName; + assert methodDeclarationIndex.get(methodName) != null : classEntry.getTypeName() + methodName; return methodDeclarationIndex.get(methodName); } 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 d885e5113be1..b2edbc8e716a 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 @@ -360,7 +360,7 @@ private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry pos = writeCUHeader(buffer, pos); assert pos == lengthPos + DW_DIE_HEADER_SIZE; /* Non-primary classes have no compiled methods so they also have no line section entry. */ - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_unit3; log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); @@ -372,17 +372,6 @@ private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); - /* Writing of lo and hi should really be optional. */ - int lo = 0; - log(context, " [0x%08x] lo_pc 0x%08x", pos, lo); - pos = writeAttrAddress(lo, buffer, pos); - int hi = 0; - log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); - pos = writeAttrAddress(hi, buffer, pos); - /* - * Note, there is no need to write a stmt_list (line section idx) for this class unit as the - * class has no code. - */ /* Now write the child DIEs starting with the layout and pointer type. */ @@ -430,7 +419,15 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b int pos = p; int lineIndex = getLineIndex(classEntry); String fileName = classEntry.getFileName(); - /* Primary classes only have a line section entry if they have an associated file. */ + /* + * Primary classes only have a line section entry if they have method and an associated + * file. + */ + List classPrimaryEntries = classEntry.getPrimaryEntries(); + int lo = findLo(classPrimaryEntries, false); + int hi = findHi(classPrimaryEntries, classEntry.includesDeoptTarget(), false); + // we must have at least one compiled method + assert hi > 0; int abbrevCode = (fileName.length() > 0 ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit2); setCUIndex(classEntry, pos); int lengthPos = pos; @@ -448,15 +445,12 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); - List classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Specify hi and lo for the compile unit which means we also need to ensure methods within * it are listed in ascending address order. */ - int lo = findLo(classPrimaryEntries, false); log(context, " [0x%08x] lo_pc 0x%08x", pos, lo); pos = writeAttrAddress(lo, buffer, pos); - int hi = findHi(classPrimaryEntries, classEntry.includesDeoptTarget(), false); log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); pos = writeAttrAddress(hi, buffer, pos); /* Only write stmt_list if the entry actually has line number info. */ @@ -1318,7 +1312,7 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr int pos = p; String methodKey = method.getSymbolName(); log(context, " [0x%08x] abstract inline method %s", pos, method.getSymbolName()); - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location_inline; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_abstract_inline_method; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] inline 0x%x", pos, DwarfDebugInfo.DW_INL_inlined); From 7a627e54522158ae5c8b0f4e01f76000a1868939 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 22:34:36 +0300 Subject: [PATCH 16/27] Refactor: Improve logging and assert messages --- .../elf/dwarf/DwarfInfoSectionImpl.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 b2edbc8e716a..b767bc9035ea 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 @@ -1335,6 +1335,7 @@ private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntr private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, Range range, int subprogramOffset, int depth, byte[] buffer, int p) { assert range.isInlined(); 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(); @@ -1358,17 +1359,17 @@ private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, R } else { code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; } - verboseLog(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 2, code); + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 1, code); pos = writeAbbrevCode(code, buffer, pos); - verboseLog(context, " [0x%08x] abstract_origin 0x%X", pos, subprogramOffset); + log(context, " [0x%08x] abstract_origin 0x%x", pos, subprogramOffset); pos = writeAttrRef4(subprogramOffset, buffer, pos); - verboseLog(context, " [0x%08x] lo_pc 0x%08x", pos, range.getLo()); + log(context, " [0x%08x] lo_pc 0x%08x", pos, range.getLo()); pos = writeAttrAddress(range.getLo(), buffer, pos); - verboseLog(context, " [0x%08x] hi_pc 0x%08x", pos, range.getHi()); + log(context, " [0x%08x] hi_pc 0x%08x", pos, range.getHi()); pos = writeAttrAddress(range.getHi(), buffer, pos); - verboseLog(context, " [0x%08x] call_file %d", pos, fileIndex); + log(context, " [0x%08x] call_file %d", pos, fileIndex); pos = writeAttrData4(fileIndex, buffer, pos); - verboseLog(context, " [0x%08x] call_line %d", pos, callLine); + log(context, " [0x%08x] call_line %d", pos, callLine); pos = writeAttrData4(callLine, buffer, pos); return pos; } @@ -1414,12 +1415,13 @@ private static int findLo(List classPrimaryEntries, boolean isDeop } } /* We should never get here. */ - assert false; + assert false : "should not reach"; return 0; } private static int findHi(List classPrimaryEntries, boolean includesDeoptTarget, boolean isDeoptTargetCU) { if (isDeoptTargetCU || !includesDeoptTarget) { + assert classPrimaryEntries.size() > 0 : "expected to find primary methods"; /* Either way the last entry is the one we want. */ return classPrimaryEntries.get(classPrimaryEntries.size() - 1).getPrimary().getHi(); } else { @@ -1435,7 +1437,7 @@ private static int findHi(List classPrimaryEntries, boolean includ } } /* We should never get here. */ - assert false; + assert false : "should not reach"; return 0; } From 9f36419170e97d94d53f5a0e050c2104dd7c42c7 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 22:41:21 +0300 Subject: [PATCH 17/27] Refactor: DwarfInfoSectionImpl#writeMethodLocation --- .../elf/dwarf/DwarfInfoSectionImpl.java | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) 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 b767bc9035ea..687f5e0dfef7 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 @@ -931,17 +931,11 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, bo assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; for (PrimaryEntry primaryEntry : classPrimaryEntries) { - Range range = primaryEntry.getPrimary(); - if (range.isDeoptTarget() != deoptTargets) { + Range primary = primaryEntry.getPrimary(); + if (primary.isDeoptTarget() != deoptTargets) { continue; } - pos = writeMethodLocation(context, classEntry, range, buffer, pos); - if (primaryEntry.getPrimary().withInlinedChildren()) { - /* - * the method has inlined ranges so write concrete inlined method entries - */ - pos = generateConcreteInlinedMethods(context, classEntry, primaryEntry, buffer, pos); - } + pos = writeMethodLocation(context, classEntry, primaryEntry, buffer, pos); } return pos; } @@ -1253,6 +1247,10 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt assert !classPrimaryEntries.isEmpty(); String fileName = classEntry.getFileName(); int lineIndex = getLineIndex(classEntry); + int lo = findLo(classPrimaryEntries, true); + int hi = findHi(classPrimaryEntries, true, true); + // we must have at least one compiled deopt method + assert hi > 0 : hi; int abbrevCode = (fileName.length() > 0 ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit2); log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); @@ -1263,8 +1261,6 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); - int lo = findLo(classPrimaryEntries, true); - int hi = findHi(classPrimaryEntries, true, true); log(context, " [0x%08x] lo_pc 0x%08x", pos, lo); pos = writeAttrAddress(lo, buffer, pos); log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); @@ -1281,27 +1277,34 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt return writeAttrNull(buffer, pos); } - private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p) { + private int writeMethodLocation(DebugContext context, ClassEntry classEntry, PrimaryEntry primaryEntry, byte[] buffer, int p) { int pos = p; + Range primary = primaryEntry.getPrimary(); log(context, " [0x%08x] method location", pos); - int abbrevCode; - abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_location; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, 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] lo_pc 0x%08x", pos, primary.getLo()); + pos = writeAttrAddress(primary.getLo(), buffer, pos); + log(context, " [0x%08x] hi_pc 0x%08x", pos, primary.getHi()); + pos = writeAttrAddress(primary.getHi(), buffer, pos); /* * Should pass true only if method is non-private. */ log(context, " [0x%08x] external true", pos); pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); - String methodKey = range.getSymbolName(); + String methodKey = primary.getSymbolName(); 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, range.getMethodEntry(), false, buffer, pos); + pos = writeMethodParameterDeclarations(context, classEntry, primary.getMethodEntry(), false, buffer, pos); + if (primary.withInlinedChildren()) { + /* + * the method has inlined ranges so write concrete inlined method entries as its + * children + */ + pos = generateConcreteInlinedMethods(context, classEntry, primaryEntry, buffer, pos); + } /* * Write a terminating null attribute. */ From 268c33256b26217c50339f452adea942fb48818f Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 17 Aug 2021 23:07:00 +0300 Subject: [PATCH 18/27] DebugInfo: Model subranges as trees to retain call-tree information PrimaryEntry provides a top-down subrange iterator that performs a depth-first traversal of the call-graph and a leaf subrange iterator that performs a depth-first traversal returning only the leafs. The sibling nodes in the tree are being merged whenever possible to reduce the debuginfo size. Co-authored-by: Foivos Zakkak --- substratevm/mx.substratevm/testhello.py | 4 +- .../objectfile/debugentry/ClassEntry.java | 1 - .../objectfile/debugentry/DebugInfoBase.java | 55 +++--- .../objectfile/debugentry/PrimaryEntry.java | 90 +++++++--- .../oracle/objectfile/debugentry/Range.java | 156 ++++++++++++++++-- .../elf/dwarf/DwarfInfoSectionImpl.java | 49 ++++-- .../elf/dwarf/DwarfLineSectionImpl.java | 34 ++-- .../pecoff/cv/CVLineRecordBuilder.java | 6 +- 8 files changed, 292 insertions(+), 103 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index c20fa718519d..fab64cd689e0 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -192,7 +192,7 @@ def test(): [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, wildcard_pattern), r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#2%s com\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#3%smain \(\) at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, package_pattern) + r"#3%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hash_pattern, wildcard_pattern) ]) checker.check(exec_string, skip_fails=False) @@ -401,7 +401,7 @@ def test(): r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, address_pattern, wildcard_pattern), r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#4%smain \(\) at %sIsolateEnterStub\.java:[0-9]+"%(spaces_pattern, package_pattern) + r"#4%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hash_pattern, wildcard_pattern) ]) 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 96c3020ba32c..04fe343cf4e3 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 @@ -168,7 +168,6 @@ public void indexSubRange(Range subrange) { /* We should already have seen the primary range. */ assert primaryEntry != null; assert primaryEntry.getClassEntry() == this; - primaryEntry.addSubRange(subrange); FileEntry subFileEntry = subrange.getFileEntry(); if (subFileEntry != null) { indexLocalFileEntry(subFileEntry); 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 d1b668011dd0..8e4d0551cab8 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 @@ -250,28 +250,12 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { Range primaryRange = new Range(stringTable, methodEntry, lo, hi, primaryLine); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); - debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { - String fileNameAtLine = debugLineInfo.fileName(); - Path filePathAtLine = debugLineInfo.filePath(); - String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.ownerType()); - String methodNameAtLine = debugLineInfo.name(); - boolean isInlined = debugLineInfo.getCaller() != null; - int loAtLine = lo + debugLineInfo.addressLo(); - int hiAtLine = lo + debugLineInfo.addressHi(); - int line = debugLineInfo.line(); - Range caller = addCallersSubRanges(debugLineInfo.getCaller(), primaryRange, classEntry, debugContext); - /* - * 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. - */ - ClassEntry subRangeClassEntry = ensureClassEntry(classNameAtLine); - MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(debugLineInfo, this, debugContext); - Range subRange = new Range(stringTable, subRangeMethodEntry, loAtLine, hiAtLine, line, primaryRange, isInlined, false, caller); - classEntry.indexSubRange(subRange); - try (DebugContext.Scope s = debugContext.scope("Subranges")) { - debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine); - } - }); + /* + * 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); })); debugInfoProvider.dataInfoProvider().forEach(debugDataInfo -> debugDataInfo.debugContext((debugContext) -> { @@ -353,7 +337,8 @@ ClassEntry lookupClassEntry(String typeName) { } /** - * Recursively creates the inlined caller subranges for nested inline subranges. + * Recursively creates subranges based on DebugLineInfo including, and appropriately linking, + * nested inline subranges. * * @param lineInfo * @param primaryRange @@ -363,24 +348,30 @@ ClassEntry lookupClassEntry(String typeName) { * primaryRange */ @SuppressWarnings("try") - private Range addCallersSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Range primaryRange, ClassEntry classEntry, DebugContext debugContext) { + private Range recursivelyAddSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Range primaryRange, ClassEntry classEntry, DebugContext debugContext) { /* Don't process the root method, it is already added as the primary range */ - if (lineInfo == null || lineInfo.getCaller() == null) { - assert lineInfo == null || (lineInfo.name().equals(primaryRange.getMethodName()) && lineInfo.ownerType().equals(primaryRange.getClassName())); + if (lineInfo == null) { return primaryRange; } - Range caller = addCallersSubRanges(lineInfo.getCaller(), primaryRange, classEntry, debugContext); - caller.setWithInlinedChildren(true); + /* + * 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 + */ + boolean isInline = lineInfo.getCaller() != null; + assert (isInline || (lineInfo.name().equals(primaryRange.getMethodName()) && lineInfo.ownerType().equals(primaryRange.getClassName()))); + + Range caller = recursivelyAddSubRanges(lineInfo.getCaller(), primaryRange, classEntry, debugContext); final String fileName = lineInfo.fileName(); final Path filePath = lineInfo.filePath(); - final String className = lineInfo.ownerType(); + final String className = TypeEntry.canonicalize(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(); - ClassEntry inlineClassEntry = ensureClassEntry(className); - MethodEntry inlineMethodEntry = inlineClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); - Range subRange = new Range(stringTable, inlineMethodEntry, lo, hi, line, primaryRange, true, true, caller); + ClassEntry subRangeClassEntry = ensureClassEntry(className); + MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); + Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isInline, caller); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java index c61e3428d140..0d1fd49379cc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java @@ -28,7 +28,8 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import java.util.ArrayList; +import java.util.ArrayDeque; +import java.util.Iterator; import java.util.List; /** @@ -43,10 +44,6 @@ public class PrimaryEntry { * Details of the class owning this range. */ private ClassEntry classEntry; - /** - * A list of subranges associated with the primary range. - */ - private List subranges; /** * Details of of compiled method frame size changes. */ @@ -59,22 +56,10 @@ public class PrimaryEntry { public PrimaryEntry(Range primary, List frameSizeInfos, int frameSize, ClassEntry classEntry) { this.primary = primary; this.classEntry = classEntry; - this.subranges = new ArrayList<>(); this.frameSizeInfos = frameSizeInfos; this.frameSize = frameSize; } - public void addSubRange(Range subrange) { - /* - * We should not see a subrange more than once. - */ - assert !subranges.contains(subrange); - /* - * We need to generate a file table entry for all ranges. - */ - subranges.add(subrange); - } - public Range getPrimary() { return primary; } @@ -83,8 +68,75 @@ public ClassEntry getClassEntry() { return classEntry; } - public List getSubranges() { - return subranges; + public Iterator topDownRangeIterator() { + return new Iterator() { + final ArrayDeque workStack = new ArrayDeque<>(); + Range current = primary.getFirstCallee(); + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Range next() { + assert hasNext(); + Range result = current; + forward(); + return result; + } + + private void forward() { + Range sibling = current.getNextCallee(); + assert sibling == null || (current.getHi() <= sibling.getLo()) : current.getHi() + " > " + sibling.getLo(); + if (!current.isLeaf()) { + /* save next sibling while we process the children */ + if (sibling != null) { + workStack.push(sibling); + } + current = current.getFirstCallee(); + } else if (sibling != null) { + current = sibling; + } else { + /* + * Return back up to parents' siblings, use pollFirst instead of pop to return + * null in case the work stack is empty + */ + current = workStack.pollFirst(); + } + } + }; + } + + public Iterator leafRangeIterator() { + final Iterator iter = topDownRangeIterator(); + return new Iterator() { + Range current = forwardLeaf(iter); + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Range next() { + assert hasNext(); + Range result = current; + current = forwardLeaf(iter); + return result; + } + + private Range forwardLeaf(Iterator t) { + if (t.hasNext()) { + Range next = t.next(); + while (next != null && !next.isLeaf()) { + next = t.next(); + } + return next; + } + return null; + } + }; } public List getFrameSizeInfos() { 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 0ed8775cff60..1951241dda14 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,6 +26,8 @@ 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 @@ -34,32 +36,50 @@ public class Range { private static final String CLASS_DELIMITER = "."; - private final Range caller; + private Range caller; private final MethodEntry methodEntry; private final String fullMethodName; private final String fullMethodNameWithParams; private final int lo; - private final int hi; + private int hi; private final int line; private final boolean isInlined; - private final boolean withChildren; - private boolean withInlinedChildren; + private final int depth; /* * This is null for a primary range. */ private final Range primary; + /* + * Support for tree of nested inline callee ranges + */ + + /** + * The first callee whose range is wholly contained in this range. + */ + private Range firstCallee; + + /** + * The last callee whose range is wholly contained in this range. + */ + private Range lastCallee; + + /** + * A link to chain callees of a given parent. + */ + private Range nextCallee; + /* * Create a primary range. */ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line) { - this(stringTable, methodEntry, lo, hi, line, null, false, false, null); + 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 isInline, boolean withChildren, Range caller) { + public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary, boolean isInline, Range caller) { assert methodEntry != null; if (methodEntry.fileEntry != null) { stringTable.uniqueDebugString(methodEntry.fileEntry.getFileName()); @@ -73,9 +93,32 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i this.line = line; this.isInlined = isInline; this.primary = primary; - this.withChildren = withChildren; - this.withInlinedChildren = false; + this.firstCallee = null; + this.lastCallee = null; + this.nextCallee = null; this.caller = caller; + if (caller != null) { + caller.addCallee(this); + } + if (this.isPrimary()) { + this.depth = -1; + } else { + this.depth = caller.depth + 1; + } + } + + private void addCallee(Range callee) { + assert this.lo <= callee.lo; + assert this.hi >= callee.hi; + assert callee.caller == this; + assert callee.nextCallee == null; + if (this.firstCallee == null) { + assert this.lastCallee == null; + this.firstCallee = this.lastCallee = callee; + } else { + this.lastCallee.nextCallee = callee; + this.lastCallee = callee; + } } public boolean contains(Range other) { @@ -182,19 +225,100 @@ public boolean isInlined() { return isInlined; } - public boolean withChildren() { - return withChildren; + public Range getCaller() { + return caller; } - public boolean withInlinedChildren() { - return withInlinedChildren; + public Range getFirstCallee() { + return firstCallee; } - public boolean setWithInlinedChildren(boolean withInlinedChildren) { - return this.withInlinedChildren = withInlinedChildren; + public Range getNextCallee() { + return nextCallee; } - public Range getCaller() { - return caller; + public Range getLastCallee() { + return lastCallee; + } + + public boolean isLeaf() { + return firstCallee == null; + } + + public int getDepth() { + return depth; + } + + public void mergeSubranges(DebugContext debugContext) { + Range next = getFirstCallee(); + if (next == null) { + return; + } + debugContext.log(DebugContext.INFO_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.getNextCallee(); + } + } + + /** + * 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 = getNextCallee(); + debugContext.log(DebugContext.INFO_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; + } + /* 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.INFO_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.nextCallee = sibling.nextCallee; + } + + private void reparentChildren(DebugContext debugContext, Range sibling) { + Range siblingNext = sibling.getFirstCallee(); + while (siblingNext != null) { + debugContext.log(DebugContext.INFO_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.nextCallee; + siblingNext.nextCallee = null; + addCallee(siblingNext); + siblingNext = newSiblingNext; + } } } 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 687f5e0dfef7..0df2f7862289 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,6 +27,7 @@ package com.oracle.objectfile.elf.dwarf; import java.lang.reflect.Modifier; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -958,31 +959,46 @@ private int writeAbstractInlineMethods(DebugContext context, ClassEntry classEnt private int generateConcreteInlinedMethods(DebugContext context, ClassEntry classEntry, PrimaryEntry primaryEntry, byte[] buffer, int p) { Range primary = primaryEntry.getPrimary(); - assert primary.withInlinedChildren(); + 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 = 0; - for (Range subrange : primaryEntry.getSubranges()) { + int depth = 1; + 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; continue; } + // if we just stepped out of a child range write nulls for each step up + while (depth > subrange.getDepth()) { + pos = writeAttrNull(buffer, pos); + depth--; + } MethodEntry method = subrange.getMethodEntry(); ClassEntry methodClassEntry = method.ownerType(); String methodKey = method.getSymbolName(); - int previousPos = pos; /* 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.withChildren()) { - while (depth > 0) { - pos = writeAttrNull(buffer, pos); - depth--; - } - } else if (previousPos != pos) { - // increment depth while write the children + if (!subrange.isLeaf()) { + // increment depth before writing the children depth++; } } + // if we just stepped out of a child range write nulls for each step up + while (depth > 1) { + pos = writeAttrNull(buffer, pos); + depth--; + } return pos; } @@ -1298,7 +1314,7 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Pri 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.withInlinedChildren()) { + if (!primary.isLeaf()) { /* * the method has inlined ranges so write concrete inlined method entries as its * children @@ -1345,7 +1361,8 @@ private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, R assert callLine >= -1 : callLine; if (callLine == -1) { log(context, " Unable to retrieve call line for inlined method %s", range.getFullMethodName()); - return p; + /* continue with line 0 as we must insert a tree node */ + callLine = 0; } Integer fileIndex; if (callerSubrange == range) { @@ -1357,10 +1374,10 @@ private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, R assert fileIndex != null; } final int code; - if (range.withChildren()) { - code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; - } else { + if (range.isLeaf()) { code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine; + } else { + code = DwarfDebugInfo.DW_ABBREV_CODE_inlined_subroutine_with_children; } log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth + 1, code); pos = writeAbbrevCode(code, buffer, pos); 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 847df5184907..bbd2479796ef 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 @@ -37,6 +37,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import org.graalvm.compiler.debug.DebugContext; +import java.util.Iterator; import java.util.Map; /** @@ -454,17 +455,20 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by * end_sequence when we finish all the subranges in the method. */ long line = primaryRange.getLine(); - if (line < 0 && primaryEntry.getSubranges().size() > 0) { - final Range subRange = primaryEntry.getSubranges().get(0); - 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); + 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); + } } } } @@ -526,11 +530,9 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by /* * Now write a row for each subrange lo and hi. */ - for (Range subrange : primaryEntry.getSubranges()) { - if (subrange.withChildren()) { - /* skip caller subranges */ - continue; - } + Iterator iterator = primaryEntry.leafRangeIterator(); + while (iterator.hasNext()) { + Range subrange = iterator.next(); assert subrange.getLo() >= primaryRange.getLo(); assert subrange.getHi() <= primaryRange.getHi(); FileEntry subFileEntry = subrange.getFileEntry(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java index 29a245d904fe..37daba6cda7b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java @@ -31,6 +31,8 @@ import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.Range; +import java.util.Iterator; + /* * In CV4, the line table consists of a series of file headers followed by line number entries. * If this is a different file, then update the length of the previous file header, write the @@ -67,7 +69,9 @@ CVLineRecord build(PrimaryEntry entry) { debug("CVLineRecord.computeContents: processing primary range %s\n", primaryRange); processRange(primaryRange); - for (Range subRange : primaryEntry.getSubranges()) { + Iterator iterator = primaryEntry.leafRangeIterator(); + while (iterator.hasNext()) { + Range subRange = iterator.next(); debug("CVLineRecord.computeContents: processing range %s\n", subRange); processRange(subRange); } From 6ae5665ec0f5f89ff4a7804dbeec25c85d0982ea Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Fri, 20 Aug 2021 16:05:59 +0300 Subject: [PATCH 19/27] Add missing documentation and rename some variables for readability Co-authored-by: Andrew Dinn --- substratevm/mx.substratevm/testhello.py | 6 +-- .../objectfile/debugentry/PrimaryEntry.java | 15 +++++- .../oracle/objectfile/debugentry/Range.java | 51 ++++++++++++------- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 4 +- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index fab64cd689e0..b03b6576a154 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -124,7 +124,7 @@ def test(): # define some useful patterns address_pattern = '0x[0-9a-f]+' - hash_pattern = '[0-9a-f]+' + hex_digits_pattern = '[0-9a-f]+' spaces_pattern = '[ \t]+' maybe_spaces_pattern = '[ \t]*' digits_pattern = '[0-9]+' @@ -192,7 +192,7 @@ def test(): [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, wildcard_pattern), r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#2%s com\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#3%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hash_pattern, wildcard_pattern) + r"#3%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hex_digits_pattern, wildcard_pattern) ]) checker.check(exec_string, skip_fails=False) @@ -401,7 +401,7 @@ def test(): r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, address_pattern, wildcard_pattern), r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), - r"#4%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hash_pattern, wildcard_pattern) + r"#4%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hex_digits_pattern, wildcard_pattern) ]) checker.check(exec_string, skip_fails=False) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java index 0d1fd49379cc..7c7ed80295e6 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java @@ -68,6 +68,12 @@ public ClassEntry getClassEntry() { return classEntry; } + /** + * Returns an iterator that traverses all the callees of the primary range associated with this + * entry. The iterator performs a depth-first pre-order traversal of the call tree. + * + * @return the iterator + */ public Iterator topDownRangeIterator() { return new Iterator() { final ArrayDeque workStack = new ArrayDeque<>(); @@ -87,7 +93,7 @@ public Range next() { } private void forward() { - Range sibling = current.getNextCallee(); + Range sibling = current.getSiblingCallee(); assert sibling == null || (current.getHi() <= sibling.getLo()) : current.getHi() + " > " + sibling.getLo(); if (!current.isLeaf()) { /* save next sibling while we process the children */ @@ -108,6 +114,13 @@ private void forward() { }; } + /** + * Returns an iterator that traverses the callees of the primary range associated with this + * entry and returns only the leafs. The iterator performs a depth-first pre-order traversal of + * the call tree returning only ranges with no callees. + * + * @return the iterator + */ public Iterator leafRangeIterator() { final Iterator iter = topDownRangeIterator(); return new Iterator() { 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 1951241dda14..43c64a540e9d 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 @@ -31,9 +31,8 @@ /** * 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. + * method. Each sub-range is linked with its caller and its callees, forming a call tree. */ - public class Range { private static final String CLASS_DELIMITER = "."; private Range caller; @@ -45,8 +44,9 @@ public class Range { private final int line; private final boolean isInlined; private final int depth; - /* - * This is null for a primary range. + /** + * This is null for a primary range. For sub ranges it holds the root of the call tree they + * belong to. */ private final Range primary; @@ -55,19 +55,19 @@ public class Range { */ /** - * The first callee whose range is wholly contained in this range. + * The first direct callee whose range is wholly contained in this range. */ private Range firstCallee; /** - * The last callee whose range is wholly contained in this range. + * The last direct callee whose range is wholly contained in this range. */ private Range lastCallee; /** - * A link to chain callees of a given parent. + * A link to a sibling callee, i.e., a range sharing the same caller with this range. */ - private Range nextCallee; + private Range siblingCallee; /* * Create a primary range. @@ -95,7 +95,7 @@ public Range(StringTable stringTable, MethodEntry methodEntry, int lo, int hi, i this.primary = primary; this.firstCallee = null; this.lastCallee = null; - this.nextCallee = null; + this.siblingCallee = null; this.caller = caller; if (caller != null) { caller.addCallee(this); @@ -111,12 +111,12 @@ private void addCallee(Range callee) { assert this.lo <= callee.lo; assert this.hi >= callee.hi; assert callee.caller == this; - assert callee.nextCallee == null; + assert callee.siblingCallee == null; if (this.firstCallee == null) { assert this.lastCallee == null; this.firstCallee = this.lastCallee = callee; } else { - this.lastCallee.nextCallee = callee; + this.lastCallee.siblingCallee = callee; this.lastCallee = callee; } } @@ -233,8 +233,8 @@ public Range getFirstCallee() { return firstCallee; } - public Range getNextCallee() { - return nextCallee; + public Range getSiblingCallee() { + return siblingCallee; } public Range getLastCallee() { @@ -249,6 +249,21 @@ 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) { @@ -264,7 +279,7 @@ public void mergeSubranges(DebugContext debugContext) { /* now this level is merged recursively merge children of each child node. */ while (next != null) { next.mergeSubranges(debugContext); - next = next.getNextCallee(); + next = next.getSiblingCallee(); } } @@ -273,7 +288,7 @@ public void mergeSubranges(DebugContext debugContext) { * node as is and returns the next sibling or null if no sibling exists. */ private Range maybeMergeSibling(DebugContext debugContext) { - Range sibling = getNextCallee(); + Range sibling = getSiblingCallee(); debugContext.log(DebugContext.INFO_LEVEL, "Merge subrange (maybe) [0x%x, 0x%x] %s", lo, hi, getFullMethodNameWithParams()); if (sibling == null) { /* all child nodes at this level have been merged */ @@ -306,7 +321,7 @@ private void unlink(DebugContext debugContext, Range sibling) { lo, hi, getFullMethodNameWithParams(), Boolean.valueOf(this.isInlined), sibling.lo, sibling.hi, sibling.getFullMethodNameWithParams(), Boolean.valueOf(sibling.isInlined)); debugContext.log(DebugContext.INFO_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.nextCallee = sibling.nextCallee; + this.siblingCallee = sibling.siblingCallee; } private void reparentChildren(DebugContext debugContext, Range sibling) { @@ -315,8 +330,8 @@ private void reparentChildren(DebugContext debugContext, Range sibling) { debugContext.log(DebugContext.INFO_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.nextCallee; - siblingNext.nextCallee = null; + Range newSiblingNext = siblingNext.siblingCallee; + siblingNext.siblingCallee = null; addCallee(siblingNext); siblingNext = newSiblingNext; } 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 642b3bbce3d0..26916fc8cec1 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 @@ -858,9 +858,9 @@ private int writeClassUnitAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; /* class compile unit with compiled methods and line info */ pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit1, buffer, pos); - /* class compile unit with line info but without line info */ + /* class compile unit with compiled methods but without line info */ pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit2, buffer, pos); - /* class compile unit without line info and without line info */ + /* class compile unit without compiled methods and without line info */ pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit3, buffer, pos); return pos; } From 3a7f4279c89b45612a98cdc8116afeb4c12d9e8b Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 24 Aug 2021 22:57:07 +0300 Subject: [PATCH 20/27] Refactor: Avoid toJavaName invocations when getting ranges' class --- .../objectfile/debugentry/DebugInfoBase.java | 28 +++++++-------- .../debuginfo/DebugInfoProvider.java | 4 +-- .../image/NativeImageDebugInfoProvider.java | 34 +++++-------------- 3 files changed, 24 insertions(+), 42 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 8e4d0551cab8..9e336bbcd562 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 @@ -35,6 +35,7 @@ import java.util.Map; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFileInfo; +import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.debuginfo.DebugInfoProvider; @@ -103,7 +104,7 @@ public abstract class DebugInfoBase { /** * index of already seen classes. */ - private Map primaryClassesIndex = new HashMap<>(); + private Map primaryClassesIndex = new HashMap<>(); /** * Index of files which contain primary or secondary ranges. */ @@ -238,17 +239,17 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ String fileName = debugCodeInfo.fileName(); Path filePath = debugCodeInfo.filePath(); - String className = TypeEntry.canonicalize(debugCodeInfo.ownerType()); + ResolvedJavaType ownerType = debugCodeInfo.ownerType(); String methodName = debugCodeInfo.name(); int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); /* Search for a method defining this primary range. */ - ClassEntry classEntry = ensureClassEntry(className); + ClassEntry classEntry = ensureClassEntry(ownerType); MethodEntry methodEntry = classEntry.ensureMethodEntryForDebugRangeInfo(debugCodeInfo, this, debugContext); Range primaryRange = new Range(stringTable, methodEntry, lo, hi, primaryLine); - debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); + debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", ownerType.toJavaName(), methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); /* * Record all subranges even if they have no line or file so we at least get a symbol @@ -349,7 +350,6 @@ ClassEntry lookupClassEntry(String typeName) { */ @SuppressWarnings("try") private Range recursivelyAddSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, Range primaryRange, ClassEntry classEntry, DebugContext debugContext) { - /* Don't process the root method, it is already added as the primary range */ if (lineInfo == null) { return primaryRange; } @@ -359,38 +359,38 @@ private Range recursivelyAddSubRanges(DebugInfoProvider.DebugLineInfo lineInfo, * number */ boolean isInline = lineInfo.getCaller() != null; - assert (isInline || (lineInfo.name().equals(primaryRange.getMethodName()) && lineInfo.ownerType().equals(primaryRange.getClassName()))); + 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 String className = TypeEntry.canonicalize(lineInfo.ownerType()); + 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(); - ClassEntry subRangeClassEntry = ensureClassEntry(className); + ClassEntry subRangeClassEntry = ensureClassEntry(ownerType); MethodEntry subRangeMethodEntry = subRangeClassEntry.ensureMethodEntryForDebugRangeInfo(lineInfo, this, debugContext); Range subRange = new Range(stringTable, subRangeMethodEntry, lo, hi, line, primaryRange, isInline, caller); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", - className, methodName, filePath, fileName, line, lo, hi); + ownerType.toJavaName(), methodName, filePath, fileName, line, lo, hi); } return subRange; } - private ClassEntry ensureClassEntry(String className) { + private ClassEntry ensureClassEntry(ResolvedJavaType type) { /* See if we already have an entry. */ - ClassEntry classEntry = primaryClassesIndex.get(className); + ClassEntry classEntry = primaryClassesIndex.get(type); if (classEntry == null) { - TypeEntry typeEntry = typesIndex.get(className); + TypeEntry typeEntry = typesIndex.get(TypeEntry.canonicalize(type.toJavaName())); assert (typeEntry != null && typeEntry.isClass()); classEntry = (ClassEntry) typeEntry; primaryClasses.add(classEntry); - primaryClassesIndex.put(className, classEntry); + primaryClassesIndex.put(type, classEntry); } - assert (classEntry.getTypeName().equals(className)); + assert (classEntry.getTypeName().equals(TypeEntry.canonicalize(type.toJavaName()))); return classEntry; } 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 c1641220fcce..ec7a124e734c 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.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; /** @@ -187,8 +188,6 @@ interface DebugMemberInfo extends DebugFileInfo { String name(); - String ownerType(); - String valueType(); int modifiers(); @@ -232,6 +231,7 @@ interface DebugMethodInfo extends DebugMemberInfo { * {@link com.oracle.objectfile.debugentry.Range}. */ interface DebugRangeInfo extends DebugMethodInfo { + ResolvedJavaType ownerType(); } /** 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 5e6ed24b63ad..faad9609aa37 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 @@ -364,7 +364,7 @@ private class NativeImageHeaderTypeInfo implements DebugHeaderTypeInfo { } void addField(String name, String valueType, int offset, @SuppressWarnings("hiding") int size) { - NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, typeName, valueType, offset, size); + NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, valueType, offset, size); fieldInfos.add(fieldinfo); } @@ -416,15 +416,13 @@ public Stream fieldInfoProvider() { private class NativeImageDebugHeaderFieldInfo implements DebugFieldInfo { private final String name; - private final String ownerType; private final String valueType; private final int offset; private final int size; private final int modifiers; - NativeImageDebugHeaderFieldInfo(String name, String ownerType, String valueType, int offset, int size) { + NativeImageDebugHeaderFieldInfo(String name, String valueType, int offset, int size) { this.name = name; - this.ownerType = ownerType; this.valueType = valueType; this.offset = offset; this.size = size; @@ -436,11 +434,6 @@ public String name() { return name; } - @Override - public String ownerType() { - return ownerType; - } - @Override public String valueType() { return valueType; @@ -585,11 +578,6 @@ public String name() { return field.getName(); } - @Override - public String ownerType() { - return typeName(); - } - @Override public String valueType() { HostedType valueType = field.getType(); @@ -655,11 +643,6 @@ public String name() { return name; } - @Override - public String ownerType() { - return typeName(); - } - @Override public String valueType() { return hostedMethod.getSignature().getReturnType(null).toJavaName(); @@ -740,7 +723,7 @@ private class NativeImageDebugArrayTypeInfo extends NativeImageDebugTypeInfo imp } void addField(String name, String valueType, int offset, @SuppressWarnings("hiding") int size) { - NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, typeName(), valueType, offset, size); + NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, valueType, offset, size); fieldInfos.add(fieldinfo); } @@ -861,8 +844,8 @@ public void debugContext(Consumer action) { } @Override - public String ownerType() { - return getDeclaringClass(hostedMethod, true).toJavaName(); + public ResolvedJavaType ownerType() { + return getDeclaringClass(hostedMethod, true); } @Override @@ -1072,12 +1055,11 @@ public Path cachePath() { } @Override - public String ownerType() { + public ResolvedJavaType ownerType() { if (method instanceof HostedMethod) { - return getDeclaringClass((HostedMethod) method, true).toJavaName(); - } else { - return method.getDeclaringClass().toJavaName(); + return getDeclaringClass((HostedMethod) method, true); } + return method.getDeclaringClass(); } @Override From 22014e6a3d9a4c4e288dadd2a3ee4f8657a5b0d9 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 25 Aug 2021 01:14:40 +0300 Subject: [PATCH 21/27] Refactor: Perform binary search on sorted methods --- .../objectfile/debugentry/ClassEntry.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) 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 04fe343cf4e3..7551ab988577 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 @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; import org.graalvm.compiler.debug.DebugContext; @@ -340,12 +339,16 @@ public ClassEntry getSuperClass() { public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { assert listIsSorted(methods); - ListIterator methodIterator = methods.listIterator(); String methodName = debugInfoBase.uniqueDebugString(debugRangeInfo.name()); String paramSignature = debugRangeInfo.paramSignature(); String returnTypeName = debugRangeInfo.valueType(); - while (methodIterator.hasNext()) { - MethodEntry methodEntry = methodIterator.next(); + /* Since the methods list is sorted we perform a binary search */ + int start = 0; + int end = methods.size() - 1; + assert end < (Integer.MAX_VALUE / 2); + while (start <= end) { + int middle = (start + end) / 2; + MethodEntry methodEntry = methods.get(middle); int comparisonResult = methodEntry.compareTo(methodName, paramSignature, returnTypeName); if (comparisonResult == 0) { methodEntry.updateRangeInfo(debugInfoBase, debugRangeInfo); @@ -354,13 +357,16 @@ public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeI indexLocalFileEntry(methodEntry.fileEntry); } return methodEntry; - } else if (comparisonResult > 0) { - methodIterator.previous(); - break; + } else if (comparisonResult < 0) { + start = middle + 1; + } else { + end = middle; } } + assert start == (end + 1) : start + " != " + end + " + 1"; + assert start <= methods.size() : start + " > " + methods.size(); MethodEntry newMethodEntry = processMethod(debugRangeInfo, debugInfoBase, debugContext); - methodIterator.add(newMethodEntry); + methods.add(start, newMethodEntry); return newMethodEntry; } From a294e6d93c1b85c726591f36c2677d003efc6e82 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 25 Aug 2021 16:03:15 +0300 Subject: [PATCH 22/27] Refactor: Use ResolvedJavaMethod to sort methods in ClassEntry This way we avoid the expensive calls to "toJavaName". This patch reduces the time spend in debug info generation from ~12s to 5s. --- .../objectfile/debugentry/ClassEntry.java | 9 ++-- .../objectfile/debugentry/MethodEntry.java | 42 ++++++------------- .../debuginfo/DebugInfoProvider.java | 5 ++- .../image/NativeImageDebugInfoProvider.java | 12 +++--- 4 files changed, 25 insertions(+), 43 deletions(-) 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 7551ab988577..08c33a753546 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 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; +import jdk.vm.ci.meta.ResolvedJavaMethod; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; @@ -272,7 +273,7 @@ private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, } protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { - String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); + String methodName = debugMethodInfo.name(); String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); int modifiers = debugMethodInfo.modifiers(); List paramTypes = debugMethodInfo.paramTypes(); @@ -339,9 +340,7 @@ public ClassEntry getSuperClass() { public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { assert listIsSorted(methods); - String methodName = debugInfoBase.uniqueDebugString(debugRangeInfo.name()); - String paramSignature = debugRangeInfo.paramSignature(); - String returnTypeName = debugRangeInfo.valueType(); + ResolvedJavaMethod javaMethod = debugRangeInfo.getJavaMethod(); /* Since the methods list is sorted we perform a binary search */ int start = 0; int end = methods.size() - 1; @@ -349,7 +348,7 @@ public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeI while (start <= end) { int middle = (start + end) / 2; MethodEntry methodEntry = methods.get(middle); - int comparisonResult = methodEntry.compareTo(methodName, paramSignature, returnTypeName); + int comparisonResult = methodEntry.compareTo(javaMethod); if (comparisonResult == 0) { methodEntry.updateRangeInfo(debugInfoBase, debugRangeInfo); if (methodEntry.fileEntry != null) { 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 326ffdb594b8..4b24173021b1 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 @@ -29,9 +29,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; - -import java.util.Arrays; -import java.util.stream.Collectors; +import jdk.vm.ci.meta.ResolvedJavaMethod; public class MethodEntry extends MemberEntry implements Comparable { final TypeEntry[] paramTypes; @@ -39,9 +37,9 @@ public class MethodEntry extends MemberEntry implements Comparable static final int DEOPT = 1 << 0; static final int IN_RANGE = 1 << 1; static final int INLINED = 1 << 2; + private final ResolvedJavaMethod javaMethod; int flags; final String symbolName; - private String signature; public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, FileEntry fileEntry, String methodName, ClassEntry ownerType, @@ -52,6 +50,7 @@ public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, this.paramTypes = paramTypes; this.paramNames = paramNames; this.symbolName = debugMethodInfo.symbolNameForMethod(); + this.javaMethod = debugMethodInfo.getJavaMethod(); this.flags = 0; if (debugMethodInfo.isDeoptTarget()) { setIsDeopt(); @@ -161,36 +160,19 @@ public String getSymbolName() { return symbolName; } - private String getSignature() { - if (signature == null) { - signature = Arrays.stream(paramTypes).map(TypeEntry::getTypeName).collect(Collectors.joining(", ")); + public int compareTo(ResolvedJavaMethod other) { + /* + * first try to sort methods by name, to have a nice sorting when printing types with ptype + */ + int comparisonResult = javaMethod.getName().compareTo(other.getName()); + if (comparisonResult != 0) { + return comparisonResult; } - return signature; - } - - public int compareTo(String methodName, String paramSignature, String returnTypeName) { - int nameComparison = memberName.compareTo(methodName); - if (nameComparison != 0) { - return nameComparison; - } - int typeComparison = valueType.getTypeName().compareTo(returnTypeName); - if (typeComparison != 0) { - return typeComparison; - } - return getSignature().compareTo(paramSignature); + return this.javaMethod.hashCode() - other.hashCode(); } @Override public int compareTo(MethodEntry other) { - assert other != null; - int nameComparison = methodName().compareTo(other.methodName()); - if (nameComparison != 0) { - return nameComparison; - } - int typeComparison = valueType.getTypeName().compareTo(other.valueType.getTypeName()); - if (typeComparison != 0) { - return typeComparison; - } - return getSignature().compareTo(other.getSignature()); + return compareTo(other.javaMethod); } } 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 ec7a124e734c..5b2709086cdb 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.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; @@ -201,9 +202,9 @@ interface DebugFieldInfo extends DebugMemberInfo { interface DebugMethodInfo extends DebugMemberInfo { /** - * @return a string identifying the method parameters. + * @return the JavaMethod of the method associated with this DebugMethodInfo. */ - String paramSignature(); + ResolvedJavaMethod getJavaMethod(); /** * @return an array of Strings identifying the method parameters. 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 faad9609aa37..788780f6a1fb 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 @@ -649,8 +649,8 @@ public String valueType() { } @Override - public String paramSignature() { - return hostedMethod.format("%P"); + public ResolvedJavaMethod getJavaMethod() { + return hostedMethod; } @Override @@ -875,8 +875,8 @@ public String symbolNameForMethod() { } @Override - public String paramSignature() { - return hostedMethod.format("%P"); + public ResolvedJavaMethod getJavaMethod() { + return hostedMethod; } @Override @@ -1099,8 +1099,8 @@ public String valueType() { } @Override - public String paramSignature() { - return method.format("%P"); + public ResolvedJavaMethod getJavaMethod() { + return method; } @Override From 6ffc908289ba3adb828e939b300d50b529b58c7f Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 24 Aug 2021 00:23:53 +0300 Subject: [PATCH 23/27] DebugInfo test extensions for nested inlined methods --- substratevm/mx.substratevm/testhello.py | 256 +++++++++++++++--- .../com.oracle.svm.test/src/hello/Hello.java | 104 ++++++- .../Target_hello_Hello_DefaultGreeter.java | 21 ++ 3 files changed, 347 insertions(+), 34 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index b03b6576a154..afa89f5b6b3f 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -169,27 +169,28 @@ def test(): # enable pretty printing of structures execute("set print pretty on") # set a break point at hello.Hello::main - # expect "Breakpoint 1 at 0x[0-9a-f]+: file hello.Hello.java, line 67." + # expect "Breakpoint 1 at 0x[0-9a-f]+: file hello.Hello.java, line 70." exec_string = execute("break hello.Hello::main") - rexp = r"Breakpoint 1 at %s: file hello/Hello\.java, line 67\."%address_pattern + rexp = r"Breakpoint 1 at %s: file hello/Hello\.java, line 70\."%address_pattern checker = Checker('break main', rexp) checker.check(exec_string) - # run the program + # run the program till the breakpoint execute("run") + execute("delete breakpoints") # list the line at the breakpoint - # expect "67 Greeter greeter = Greeter.greeter(args);" + # expect "70 Greeter greeter = Greeter.greeter(args);" exec_string = execute("list") - checker = Checker(r"list bp 1", "67%sGreeter greeter = Greeter\.greeter\(args\);"%spaces_pattern) + checker = Checker(r"list bp 1", "70%sGreeter greeter = Greeter\.greeter\(args\);"%spaces_pattern) checker.check(exec_string, skip_fails=False) # run a backtrace - # expect "#0 hello.Hello.main(java.lang.String[] *).* at hello.Hello.java:67" + # expect "#0 hello.Hello.main(java.lang.String[] *).* at hello.Hello.java:70" # expect "#1 0x[0-9a-f]+ in com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_.* at [a-z/]+/JavaMainWrapper.java:[0-9]+" exec_string = execute("backtrace") checker = Checker("backtrace hello.Hello::main", - [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, wildcard_pattern), + [r"#0%shello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:70"%(spaces_pattern, wildcard_pattern), r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#2%s com\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), r"#3%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hex_digits_pattern, wildcard_pattern) @@ -266,14 +267,6 @@ def test(): r"%s = 1000"%(wildcard_pattern)) checker.check(exec_string, skip_fails=False) - - # set a break point at hello.Hello$DefaultGreeter::greet - # expect "Breakpoint 2 at 0x[0-9a-f]+: file hello/Target_Hello_DefaultGreeter.java, line [0-9]+." - exec_string = execute("break hello.Hello$DefaultGreeter::greet") - rexp = r"Breakpoint 2 at %s: file hello/Target_hello_Hello_DefaultGreeter\.java, line %s\."%(address_pattern, digits_pattern) - checker = Checker("break on substituted method", rexp) - checker.check(exec_string, skip_fails=False) - # look up greet methods # expect "All functions matching regular expression "greet":" # expect "" @@ -290,7 +283,7 @@ def test(): r"File hello/Target_hello_Hello_DefaultGreeter\.java:", r"%svoid hello.Hello\$DefaultGreeter::greet\(void\);"%maybe_spaces_pattern] checker = Checker("info func greet", rexp) - checker.check(exec_string, skip_fails=True) + checker.check(exec_string) # look up PrintStream.println methods # expect "All functions matching regular expression "java.io.PrintStream.println":" @@ -303,20 +296,13 @@ def test(): checker = Checker("info func java.io.PrintStream::println", rexp) checker.check(exec_string) - # set a break point at PrintStream.println(String) - # expect "Breakpoint 3 at 0x[0-9a-f]+: java.base/java/io/PrintStream.java, line [0-9]+." - exec_string = execute("break java.io.PrintStream::println(java.lang.String *)") - rexp = r"Breakpoint 3 at %s: file .*java/io/PrintStream\.java, line %s\."%(address_pattern, digits_pattern) - checker = Checker('break println', rexp) - checker.check(exec_string, skip_fails=False) - # step into method call execute("step") # list current line - # expect "34 if (args.length == 0) {" + # expect "37 if (args.length == 0) {" exec_string = execute("list") - rexp = r"34%sif \(args\.length == 0\) {"%spaces_pattern + rexp = r"37%sif \(args\.length == 0\) {"%spaces_pattern checker = Checker('list hello.Hello$Greeter.greeter', rexp) checker.check(exec_string, skip_fails=False) @@ -397,8 +383,8 @@ def test(): # run a backtrace exec_string = execute("backtrace") checker = Checker("backtrace hello.Hello.Greeter::greeter", - [r"#0%shello\.Hello\$Greeter::greeter\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:34"%(spaces_pattern, wildcard_pattern), - r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:67"%(spaces_pattern, address_pattern, wildcard_pattern), + [r"#0%shello\.Hello\$Greeter::greeter\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:37"%(spaces_pattern, wildcard_pattern), + r"#1%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\)%s at hello/Hello\.java:70"%(spaces_pattern, address_pattern, wildcard_pattern), r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, wildcard_pattern, package_pattern), r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::run%s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, wildcard_pattern, package_pattern), r"#4%s%s in com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s"%(spaces_pattern, address_pattern, hex_digits_pattern, wildcard_pattern) @@ -421,16 +407,35 @@ def test(): print(checker) sys.exit(1) - # continue to next 2 breakpoints - execute("continue") + # set breakpoint at substituted method hello.Hello$DefaultGreeter::greet + # expect "Breakpoint 2 at 0x[0-9a-f]+: file hello/Target_Hello_DefaultGreeter.java, line [0-9]+." + exec_string = execute("break hello.Hello$DefaultGreeter::greet") + rexp = r"Breakpoint %s at %s: file hello/Target_hello_Hello_DefaultGreeter\.java, line %s\."%(digits_pattern, address_pattern, digits_pattern) + checker = Checker("break on substituted method", rexp) + checker.check(exec_string, skip_fails=False) + execute("delete breakpoints") + + # set a break point at standard library PrintStream.println(String) + # expect "Breakpoint 3 at 0x[0-9a-f]+: java.base/java/io/PrintStream.java, line [0-9]+." + exec_string = execute("break java.io.PrintStream::println(java.lang.String *)") + rexp = r"Breakpoint %s at %s: file .*java/io/PrintStream\.java, line %s\."%(digits_pattern, address_pattern, digits_pattern) + checker = Checker('break println', rexp) + checker.check(exec_string, skip_fails=False) + execute("continue") # run backtrace to check we are in java.io.PrintStream::println(java.lang.String) # expect "#0 java.io.PrintStream::println(java.lang.String).* at java.base/java/io/PrintStream.java:[0-9]+" - exec_string = execute("backtrace 1") - checker = Checker("backtrace 1 PrintStream::println", - [r"#0%sjava\.io\.PrintStream::println\(java\.lang\.String \*\)%s at %sjava/io/PrintStream.java:%s"%(spaces_pattern, wildcard_pattern, wildcard_pattern, digits_pattern)]) - checker.check(exec_string, skip_fails=False) + exec_string = execute("backtrace 5") + rexp = [r"#0%sjava\.io\.PrintStream::println\(java\.lang\.String \*\)%s at %sjava/io/PrintStream.java:%s"%(spaces_pattern, wildcard_pattern, wildcard_pattern, digits_pattern), + r"#1%s%s in hello\.SubstituteHelperClass::nestedGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:59"%(spaces_pattern, address_pattern), + # FIXME: Ideally we should see the following two lines in the backtrace as well! + # r"#2%s%s in hello\.SubstituteHelperClass::staticInlineGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:53"%(digits_pattern, spaces_pattern, address_pattern), + # r"#3%s%s in hello\.SubstituteHelperClass::inlineGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:48"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello\$DefaultGreeter::greet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:39"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:70"%(digits_pattern, spaces_pattern, address_pattern)] + checker = Checker("backtrace PrintStream::println", rexp) + checker.check(exec_string) # list current line # expect "[0-9]+ synchronized (this) {" @@ -486,6 +491,191 @@ def test(): r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) checker.check(exec_string, skip_fails=False) + ### + # Tests for inlined methods + ### + + # print details of Hello type + exec_string = execute("ptype 'hello.Hello'") + rexp = [r"type = class hello\.Hello : public java\.lang\.Object {", + # ptype lists inlined methods allthrough they are not listed with info func + r"%sprivate:"%spaces_pattern, + r"%sstatic void inlineCallChain\(void\);"%spaces_pattern, + r"%sstatic void inlineFrom\(void\);"%spaces_pattern, + r"%sstatic void inlineHere\(int\);"%spaces_pattern, + r"%sstatic void inlineMee\(void\);"%spaces_pattern, + r"%sstatic void inlineMixTo\(int\);"%spaces_pattern, + r"%sstatic void inlineMoo\(void\);"%spaces_pattern, + r"%sstatic void inlineTailRecursion\(int\);"%spaces_pattern, + r"%sstatic void inlineTo\(int\);"%spaces_pattern, + r"%spublic:"%spaces_pattern, + r"%sstatic void main\(java\.lang\.String\[\] \*\);"%spaces_pattern, + r"%sprivate:"%spaces_pattern, + r"%sstatic void noInlineFoo\(void\);"%spaces_pattern, + r"%sstatic void noInlineHere\(int\);"%spaces_pattern, + r"%sstatic void noInlineTest\(void\);"%spaces_pattern, + r"%sstatic void noInlineThis\(void\);"%spaces_pattern, + r"}"] + checker = Checker('ptype hello.Hello', rexp) + checker.check(exec_string, skip_fails=False) + + # list methods matching regural expression "nlined", inline methods are not listed + exec_string = execute("info func nline") + rexp = [r"All functions matching regular expression \"nline\":", + r"File hello/Hello\.java:", + r"%svoid hello\.Hello::noInlineFoo\(void\);"%spaces_pattern, + 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.check(exec_string) + + # list inlineMee and inlineMoo 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"109%sSystem\.out\.println\(\"This is a cow\"\);"%spaces_pattern] + checker = Checker('list inlineMee', rexp) + checker.check(execute("list inlineMee")) + checker = Checker('list inlineMoo', rexp) + checker.check(execute("list inlineMoo")) + + execute("delete breakpoints") + # Set breakpoint at inlined method and step through its nested inline methods + exec_string = execute("break hello.Hello::inlineMee") + rexp = r"Breakpoint %s at %s: hello\.Hello::inlineMee\. \(2 locations\)"%(digits_pattern, address_pattern) + checker = Checker('break inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + + exec_string = execute("info break 4") + # The breakpoint will be set 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"4.1%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern), + r"4.2%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern)] + checker = Checker('info break inlineMee', rexp) + checker.check(exec_string) + + execute("continue") + exec_string = execute("list") + rexp = [r"104%sinlineMoo\(\);"%spaces_pattern] + checker = Checker('hit break at inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + execute("step") + exec_string = execute("list") + rexp = [r"109%sSystem\.out\.println\(\"This is a cow\"\);"%spaces_pattern] + checker = Checker('step in inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + exec_string = execute("backtrace 4") + rexp = [r"#0%shello\.Hello::inlineMoo \(\) at hello/Hello\.java:109"%spaces_pattern, + r"#1%shello\.Hello::inlineMee \(\) at hello/Hello\.java:104"%spaces_pattern, + r"#2%shello\.Hello::noInlineFoo\(void\) \(\) at hello/Hello\.java:94"%spaces_pattern, + r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:70"%(spaces_pattern, address_pattern)] + checker = Checker('backtrace inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + + execute("continue") + exec_string = execute("list") + rexp = [r"104%sinlineMoo\(\);"%spaces_pattern] + checker = Checker('hit break at inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + execute("step") + exec_string = execute("list") + rexp = [r"109%sSystem\.out\.println\(\"This is a cow\"\);"%spaces_pattern] + checker = Checker('step in inlineMee', rexp) + checker.check(exec_string, skip_fails=False) + exec_string = execute("backtrace 4") + rexp = [r"#0%shello\.Hello::inlineMoo \(\) at hello/Hello\.java:109"%spaces_pattern, + r"#1%shello\.Hello::inlineMee \(\) at hello/Hello\.java:104"%spaces_pattern, + r"#2%shello\.Hello::inlineCallChain \(\) at hello/Hello\.java:99"%spaces_pattern, + r"#3%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:86"%spaces_pattern] + checker = Checker('backtrace inlineMee 2', 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 129\."%(digits_pattern, address_pattern) + checker = Checker('break noInlineTest', rexp) + checker.check(exec_string, skip_fails=False) + + execute("continue") + exec_string = execute("list") + rexp = r"129%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern + checker = Checker('hig breakpoint in noInlineTest', rexp) + checker.check(exec_string, skip_fails=False) + exec_string = execute("backtrace 4") + rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:129"%(spaces_pattern), + # FIXME: the following two lines should appear in the backtrace as well! + # r"#%s%shello\.Hello::inlinedA \(\) at hello/Hello\.java:124"%(digits_pattern, spaces_pattern), + # r"#%s%shello\.Hello::inlinedIs \(\) at hello/Hello\.java:119"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:114"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] + checker = Checker('backtrace in inlinedMethod2', rexp) + checker.check(exec_string, skip_fails=False) + + execute("delete breakpoints") + exec_string = execute("break Hello.java:149") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 149\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:149', rexp) + checker.check(exec_string) + + execute("continue 5") + exec_string = execute("backtrace 8") + rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:149"%(spaces_pattern), # FIXME why is inlineMixTo missing the int arg here? + r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + # FIXME: the commented lines should also appear in the backtrace! + # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), # FIXME the line number here should be 141 + # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:134"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] + checker = Checker('backtrace in recursive inlineMixTo', rexp) + checker.check(exec_string, skip_fails=False) + + execute("delete breakpoints") + exec_string = execute("break Hello.java:162") + rexp = r"Breakpoint %s at %s: Hello\.java:162\. \(2 locations\)"%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:162', rexp) + checker.check(exec_string) + + execute("continue 5") + exec_string = execute("backtrace 8") + rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:162"%(spaces_pattern), + # FIXME: the commented lines should also appear in the backtrace! + # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), + # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), + r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] + checker = Checker('backtrace in recursive inlineTo', rexp) + checker.check(exec_string, skip_fails=False) + + execute("delete breakpoints") + exec_string = execute("break Hello.java:168") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 168\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:168', rexp) + checker.check(exec_string) + + execute("continue 5") + exec_string = execute("backtrace 8") + rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), + r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:162"%(digits_pattern, spaces_pattern, address_pattern)] + checker = Checker('backtrace in recursive inlineTo', rexp) + checker.check(exec_string, skip_fails=False) + 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 0482024ce1ac..5a99fac094e4 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -28,6 +28,9 @@ // Checkstyle: stop +import com.oracle.svm.core.annotate.AlwaysInline; +import com.oracle.svm.core.annotate.NeverInline; + public class Hello { public abstract static class Greeter { static Greeter greeter(String[] args) { @@ -53,7 +56,7 @@ public void greet() { public static class NamedGreeter extends Greeter { private String name; - NamedGreeter(String name) { + public NamedGreeter(String name) { this.name = name; } @@ -66,6 +69,105 @@ public void greet() { public static void main(String[] args) { Greeter greeter = Greeter.greeter(args); greeter.greet(); + /* + * Perform the following call chains + * + * main --no-inline--> noInlineFoo --inline--> inlineMee --inline--> inlineMoo + * main --inline--> inlineCallChain --inline--> inlineMee --inline--> inlineMoo + * main --no-inline--> noInlineThis --inline--> inlineIs --inline--> inlineA --no-inline--> noInlineTest + * main --inline--> inlineFrom --no-inline--> noInlineHere --inline--> inlineMixTo --no-inline--+ + * ^ | + * +-------------(rec call n-times)-------------+ + * main --inline--> inlineFrom --inline--> inlineHere --inline--> inlineTo --inline--+ + * ^ | + * +---------(rec call n-times)----------+ + */ + noInlineFoo(); + inlineCallChain(); + noInlineThis(); + inlineFrom(); System.exit(0); } + + @NeverInline("For testing purposes") + private static void noInlineFoo() { + inlineMee(); + } + + @AlwaysInline("For testing purposes") + private static void inlineCallChain() { + inlineMee(); + } + + @AlwaysInline("For testing purposes") + private static void inlineMee() { + inlineMoo(); + } + + @AlwaysInline("For testing purposes") + private static void inlineMoo() { + System.out.println("This is a cow"); + } + + @NeverInline("For testing purposes") + private static void noInlineThis() { + inlineIs(); + } + + @AlwaysInline("For testing purposes") + private static void inlineIs() { + inlineA(); + } + + @AlwaysInline("For testing purposes") + private static void inlineA() { + noInlineTest(); + } + + @NeverInline("For testing purposes") + private static void noInlineTest() { + System.out.println("This is a test"); + } + + @AlwaysInline("For testing purposes") + private static void inlineFrom() { + noInlineHere(5); + inlineHere(5); + inlineTailRecursion(5); + } + + @NeverInline("For testing purposes") + private static void noInlineHere(int n) { + inlineMixTo(n); + } + + @AlwaysInline("For testing purposes") + private static void inlineMixTo(int n) { + if (n > 0) { + noInlineHere(n - 1); + } + System.out.println("Recursive mixed calls!"); + } + + @AlwaysInline("For testing purposes") + private static void inlineHere(int n) { + inlineTo(n); + } + + @AlwaysInline("For testing purposes") + private static void inlineTo(int n) { + if (n > 0) { + inlineHere(n - 1); + } + System.out.println("Recursive inline calls!"); + } + + @AlwaysInline("For testing purposes") + private static void inlineTailRecursion(int n) { + if (n <= 0) { + System.out.println("Recursive inline calls!"); + return; + } + inlineTailRecursion(n-1); + } } diff --git a/substratevm/src/com.oracle.svm.test/src/hello/Target_hello_Hello_DefaultGreeter.java b/substratevm/src/com.oracle.svm.test/src/hello/Target_hello_Hello_DefaultGreeter.java index db73478cd50d..1fee4b7e22ca 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Target_hello_Hello_DefaultGreeter.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Target_hello_Hello_DefaultGreeter.java @@ -26,6 +26,8 @@ package hello; +import com.oracle.svm.core.annotate.AlwaysInline; +import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -34,6 +36,25 @@ final class Target_hello_Hello_DefaultGreeter { @SuppressWarnings("static-method") @Substitute public void greet() { + SubstituteHelperClass substituteHelperClass = new SubstituteHelperClass(); + substituteHelperClass.inlineGreet(); + } + +} + +class SubstituteHelperClass { + @AlwaysInline("For testing purposes") + void inlineGreet() { + staticInlineGreet(); + } + + @AlwaysInline("For testing purposes") + private static void staticInlineGreet() { + nestedGreet(); + } + + @NeverInline("For testing purposes") + private static void nestedGreet() { // Checkstyle: stop System.out.println("Hello, substituted world!"); // Checkstyle: resume From 7f0e606abfae6b45ad473ed2ff6d68146b8a3b3c Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Wed, 1 Sep 2021 15:04:52 +0100 Subject: [PATCH 24/27] Ensure call node source positions are not lost at substitution Copy original source position in InvokeWithExceptionNode.replaceWithInvoke Update debug info test to resstore check for resulting inline frames --- .../nodes/InvokeWithExceptionNode.java | 2 + substratevm/mx.substratevm/testhello.py | 107 +++++++++--------- .../com.oracle.svm.test/src/hello/Hello.java | 4 +- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java index ca01af2b655d..642d5fd15c40 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java @@ -248,6 +248,8 @@ public InvokeNode replaceWithInvoke() { AbstractBeginNode oldException = this.exceptionEdge; graph().replaceSplitWithFixed(this, newInvoke, this.next()); GraphUtil.killCFG(oldException); + // copy across any original node source position + newInvoke.setNodeSourcePosition(getNodeSourcePosition()); return newInvoke; } diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index afa89f5b6b3f..a0c45a9a4cbb 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -86,7 +86,8 @@ def check(self, text, skip_fails=True): print('Checker %s: match %d failed at line %d %s\n'%(self.name, i, line_idx, line)) print(self) print(text) - sys.exit(1) + # sys.exit(1) + return matches else: matches.append(match) line_idx += 1 @@ -94,7 +95,8 @@ def check(self, text, skip_fails=True): print('Checker %s: insufficient matching lines %d for regular expressions %d'%(self.name, len(matches), num_rexps)) print(self) print(text) - sys.exit(1) + # sys.exit(1) + return matches print(text) return matches @@ -426,14 +428,13 @@ def test(): # run backtrace to check we are in java.io.PrintStream::println(java.lang.String) # expect "#0 java.io.PrintStream::println(java.lang.String).* at java.base/java/io/PrintStream.java:[0-9]+" - exec_string = execute("backtrace 5") + exec_string = execute("backtrace 6") rexp = [r"#0%sjava\.io\.PrintStream::println\(java\.lang\.String \*\)%s at %sjava/io/PrintStream.java:%s"%(spaces_pattern, wildcard_pattern, wildcard_pattern, digits_pattern), r"#1%s%s in hello\.SubstituteHelperClass::nestedGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:59"%(spaces_pattern, address_pattern), - # FIXME: Ideally we should see the following two lines in the backtrace as well! - # r"#2%s%s in hello\.SubstituteHelperClass::staticInlineGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:53"%(digits_pattern, spaces_pattern, address_pattern), - # r"#3%s%s in hello\.SubstituteHelperClass::inlineGreet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:48"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello\$DefaultGreeter::greet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:39"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:70"%(digits_pattern, spaces_pattern, address_pattern)] + r"#2%s%s in hello\.SubstituteHelperClass::staticInlineGreet \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:53"%(spaces_pattern, address_pattern), + r"#3%s hello\.SubstituteHelperClass::inlineGreet \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:48"%(spaces_pattern), + r"#4%s hello\.Hello\$DefaultGreeter::greet\(void\) \(\) at hello/Target_hello_Hello_DefaultGreeter\.java:40"%(spaces_pattern), + r"#5%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:71"%(spaces_pattern, address_pattern)] checker = Checker("backtrace PrintStream::println", rexp) checker.check(exec_string) @@ -498,11 +499,13 @@ def test(): # print details of Hello type exec_string = execute("ptype 'hello.Hello'") rexp = [r"type = class hello\.Hello : public java\.lang\.Object {", - # ptype lists inlined methods allthrough they are not listed with info func + # ptype lists inlined methods although they are not listed with info func r"%sprivate:"%spaces_pattern, + r"%sstatic void inlineA\(void\);"%spaces_pattern, r"%sstatic void inlineCallChain\(void\);"%spaces_pattern, r"%sstatic void inlineFrom\(void\);"%spaces_pattern, r"%sstatic void inlineHere\(int\);"%spaces_pattern, + r"%sstatic void inlineIs\(void\);"%spaces_pattern, r"%sstatic void inlineMee\(void\);"%spaces_pattern, r"%sstatic void inlineMixTo\(int\);"%spaces_pattern, r"%sstatic void inlineMoo\(void\);"%spaces_pattern, @@ -541,7 +544,7 @@ def test(): execute("delete breakpoints") # Set breakpoint at inlined method and step through its nested inline methods exec_string = execute("break hello.Hello::inlineMee") - rexp = r"Breakpoint %s at %s: hello\.Hello::inlineMee\. \(2 locations\)"%(digits_pattern, address_pattern) + rexp = r"Breakpoint %s at %s: hello\.Hello::inlineMee\. \(3 locations\)"%(digits_pattern, address_pattern) checker = Checker('break inlineMee', rexp) checker.check(exec_string, skip_fails=False) @@ -549,7 +552,8 @@ def test(): # The breakpoint will be set 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"4.1%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern), - r"4.2%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern)] + r"4.2%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern), + r"4.3%sy%s%s in hello\.Hello::inlineMee at hello/Hello\.java:109"%(spaces_pattern, spaces_pattern, address_pattern)] checker = Checker('info break inlineMee', rexp) checker.check(exec_string) @@ -567,7 +571,7 @@ def test(): rexp = [r"#0%shello\.Hello::inlineMoo \(\) at hello/Hello\.java:109"%spaces_pattern, r"#1%shello\.Hello::inlineMee \(\) at hello/Hello\.java:104"%spaces_pattern, r"#2%shello\.Hello::noInlineFoo\(void\) \(\) at hello/Hello\.java:94"%spaces_pattern, - r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:70"%(spaces_pattern, address_pattern)] + r"#3%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:85"%(spaces_pattern, address_pattern)] checker = Checker('backtrace inlineMee', rexp) checker.check(exec_string, skip_fails=False) @@ -598,16 +602,15 @@ def test(): execute("continue") exec_string = execute("list") rexp = r"129%sSystem.out.println\(\"This is a test\"\);"%spaces_pattern - checker = Checker('hig breakpoint in noInlineTest', rexp) + checker = Checker('hit breakpoint in noInlineTest', rexp) checker.check(exec_string, skip_fails=False) - exec_string = execute("backtrace 4") + exec_string = execute("backtrace 5") rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:129"%(spaces_pattern), - # FIXME: the following two lines should appear in the backtrace as well! - # r"#%s%shello\.Hello::inlinedA \(\) at hello/Hello\.java:124"%(digits_pattern, spaces_pattern), - # r"#%s%shello\.Hello::inlinedIs \(\) at hello/Hello\.java:119"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:114"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] - checker = Checker('backtrace in inlinedMethod2', rexp) + r"#1%s%s in hello\.Hello::inlinedA \(\) at hello/Hello\.java:124"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlinedIs \(\) at hello/Hello\.java:119"%(spaces_pattern), + r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:114"%(spaces_pattern), + r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:87"%(spaces_pattern, address_pattern)] + checker = Checker('backtrace in inlinedMethod', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") @@ -617,22 +620,21 @@ def test(): checker.check(exec_string) execute("continue 5") - exec_string = execute("backtrace 8") - rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:149"%(spaces_pattern), # FIXME why is inlineMixTo missing the int arg here? + exec_string = execute("backtrace 14") + rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:149"%(spaces_pattern), r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - # FIXME: the commented lines should also appear in the backtrace! - # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), # FIXME the line number here should be 141 - # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:134"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] + r"#2%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#3%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + r"#4%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#5%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + r"#6%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#7%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + r"#8%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#9%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + r"#10%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#11%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), + r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:134"%(spaces_pattern, address_pattern), + r"#13%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) @@ -643,19 +645,19 @@ def test(): checker.check(exec_string) execute("continue 5") - exec_string = execute("backtrace 8") + exec_string = execute("backtrace 12") rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:162"%(spaces_pattern), - # FIXME: the commented lines should also appear in the backtrace! - # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(digits_pattern, spaces_pattern, address_pattern), - # r"#%s%s in hello\.Hello::inlineHere\(int\) \(\) at hello/Hello\.java:154"%(digits_pattern, spaces_pattern), - r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(digits_pattern, spaces_pattern, address_pattern)] + r"#1%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#3%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), + r"#4%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#5%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), + r"#6%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#7%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), + r"#8%shello\.Hello::inlineTo \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#9%shello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern), + r"#10%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:135"%(spaces_pattern), + r"#11%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) @@ -668,11 +670,12 @@ def test(): execute("continue 5") exec_string = execute("backtrace 8") rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(digits_pattern, spaces_pattern, address_pattern), - r"#%s%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:162"%(digits_pattern, spaces_pattern, address_pattern)] + r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), + r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), + r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), + r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), + r"#5%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:136"%(spaces_pattern), + r"#6%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) 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 5a99fac094e4..e70d133ee8ea 100644 --- a/substratevm/src/com.oracle.svm.test/src/hello/Hello.java +++ b/substratevm/src/com.oracle.svm.test/src/hello/Hello.java @@ -69,7 +69,7 @@ public void greet() { public static void main(String[] args) { Greeter greeter = Greeter.greeter(args); greeter.greet(); - /* + /*- * Perform the following call chains * * main --no-inline--> noInlineFoo --inline--> inlineMee --inline--> inlineMoo @@ -168,6 +168,6 @@ private static void inlineTailRecursion(int n) { System.out.println("Recursive inline calls!"); return; } - inlineTailRecursion(n-1); + inlineTailRecursion(n - 1); } } From 276a6d35333834632a29f4fd408c638ac8e30b29 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 6 Sep 2021 10:18:12 +0100 Subject: [PATCH 25/27] Correct regression introduced into debuginfo test script --- substratevm/mx.substratevm/testhello.py | 39 +++++++++++++------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index a0c45a9a4cbb..32346d942bd3 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -86,7 +86,7 @@ def check(self, text, skip_fails=True): print('Checker %s: match %d failed at line %d %s\n'%(self.name, i, line_idx, line)) print(self) print(text) - # sys.exit(1) + sys.exit(1) return matches else: matches.append(match) @@ -95,7 +95,7 @@ def check(self, text, skip_fails=True): print('Checker %s: insufficient matching lines %d for regular expressions %d'%(self.name, len(matches), num_rexps)) print(self) print(text) - # sys.exit(1) + sys.exit(1) return matches print(text) return matches @@ -606,11 +606,11 @@ def test(): checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 5") rexp = [r"#0%shello\.Hello::noInlineTest\(void\) \(\) at hello/Hello\.java:129"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlinedA \(\) at hello/Hello\.java:124"%(spaces_pattern, address_pattern), - r"#2%shello\.Hello::inlinedIs \(\) at hello/Hello\.java:119"%(spaces_pattern), + r"#1%s%s in hello\.Hello::inlineA \(\) at hello/Hello\.java:124"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlineIs \(\) at hello/Hello\.java:119"%(spaces_pattern), r"#3%shello\.Hello::noInlineThis\(void\) \(\) at hello/Hello\.java:114"%(spaces_pattern), r"#4%s%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:87"%(spaces_pattern, address_pattern)] - checker = Checker('backtrace in inlinedMethod', rexp) + checker = Checker('backtrace in inlineMethod', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") @@ -623,18 +623,18 @@ def test(): exec_string = execute("backtrace 14") rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:149"%(spaces_pattern), r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - r"#2%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#2%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), r"#3%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - r"#4%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#4%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), r"#5%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - r"#6%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#6%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), r"#7%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - r"#8%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#8%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), r"#9%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), - r"#10%s%sin hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), + r"#10%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:147"%(spaces_pattern, address_pattern), r"#11%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:141"%(spaces_pattern), r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:134"%(spaces_pattern, address_pattern), - r"#13%s in hello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:109"%(spaces_pattern)] + r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) @@ -645,7 +645,7 @@ def test(): checker.check(exec_string) execute("continue 5") - exec_string = execute("backtrace 12") + exec_string = execute("backtrace 14") rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:162"%(spaces_pattern), r"#1%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), r"#2%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), @@ -654,10 +654,12 @@ def test(): r"#5%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), r"#6%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), r"#7%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), - r"#8%shello\.Hello::inlineTo \(\) at hello/Hello\.java:160"%(spaces_pattern), - r"#9%shello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern), - r"#10%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:135"%(spaces_pattern), - r"#11%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] + r"#8%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#9%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern, address_pattern), + r"#10%shello\.Hello::inlineTo \(\) at hello/Hello\.java:160"%(spaces_pattern), + r"#11%shello\.Hello::inlineHere \(\) at hello/Hello\.java:154"%(spaces_pattern), + r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:135"%(spaces_pattern), + r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) @@ -674,8 +676,9 @@ def test(): r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), - r"#5%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:136"%(spaces_pattern), - r"#6%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] + r"#5%s%s in hello\.Hello::inlineTailRecursion \(\) at hello/Hello\.java:171"%(spaces_pattern, address_pattern), + r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:136"%(spaces_pattern), + r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:88"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) From be1f5e0fe8d05ee0f89092617d6b47be03588e56 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 6 Sep 2021 10:19:44 +0100 Subject: [PATCH 26/27] Track methods with a hash index instead of a sorted list. --- .../objectfile/debugentry/ClassEntry.java | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) 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 08c33a753546..6ff0635aa5ed 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 @@ -32,7 +32,6 @@ import java.util.List; import java.util.Map; -import jdk.vm.ci.meta.ResolvedJavaMethod; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; @@ -63,6 +62,10 @@ public class ClassEntry extends StructureTypeEntry { * Details of methods located in this instance. */ protected List methods; + /** + * An index of all currently known methods keyed by the unique local symbol name of the method. + */ + private Map methodsIndex; /** * A list recording details of all primary ranges included in this class sorted by ascending * address range. @@ -98,6 +101,7 @@ public ClassEntry(String className, FileEntry fileEntry, int size) { this.interfaces = new ArrayList<>(); this.fileEntry = fileEntry; this.methods = new ArrayList<>(); + this.methodsIndex = new HashMap<>(); this.primaryEntries = new ArrayList<>(); this.primaryIndex = new HashMap<>(); this.localFiles = new ArrayList<>(); @@ -137,9 +141,7 @@ public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInf /* Add details of fields and field types */ debugInstanceTypeInfo.fieldInfoProvider().forEach(debugFieldInfo -> this.processField(debugFieldInfo, debugInfoBase, debugContext)); /* Add details of methods and method types */ - debugInstanceTypeInfo.methodInfoProvider().forEach(debugMethodInfo -> this.methods.add(this.processMethod(debugMethodInfo, debugInfoBase, debugContext))); - /* Sort methods to improve lookup speed */ - this.methods.sort(MethodEntry::compareTo); + debugInstanceTypeInfo.methodInfoProvider().forEach(debugMethodInfo -> this.processMethod(debugMethodInfo, debugInfoBase, debugContext)); } public void indexPrimary(Range primary, List frameSizeInfos, int frameSize) { @@ -174,6 +176,13 @@ public void indexSubRange(Range subrange) { } } + private void indexMethodEntry(MethodEntry methodEntry) { + String methodName = methodEntry.getSymbolName(); + assert methodsIndex.get(methodName) == null : methodName; + methods.add(methodEntry); + methodsIndex.put(methodName, methodEntry); + } + private void indexLocalFileEntry(FileEntry localFileEntry) { if (localFilesIndex.get(localFileEntry) == null) { localFiles.add(localFileEntry); @@ -296,8 +305,11 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa * substitution */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(debugMethodInfo); - return new MethodEntry(debugInfoBase, debugMethodInfo, methodFileEntry, methodName, this, resultType, - paramTypeArray, paramNameArray); + MethodEntry methodEntry = new MethodEntry(debugInfoBase, debugMethodInfo, methodFileEntry, methodName, + this, resultType, paramTypeArray, paramNameArray); + indexMethodEntry(methodEntry); + + return methodEntry; } @Override @@ -339,34 +351,19 @@ public ClassEntry getSuperClass() { } public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { - assert listIsSorted(methods); - ResolvedJavaMethod javaMethod = debugRangeInfo.getJavaMethod(); - /* Since the methods list is sorted we perform a binary search */ - int start = 0; - int end = methods.size() - 1; - assert end < (Integer.MAX_VALUE / 2); - while (start <= end) { - int middle = (start + end) / 2; - MethodEntry methodEntry = methods.get(middle); - int comparisonResult = methodEntry.compareTo(javaMethod); - if (comparisonResult == 0) { - methodEntry.updateRangeInfo(debugInfoBase, debugRangeInfo); - if (methodEntry.fileEntry != null) { - /* Ensure that the methodEntry's fileEntry is present in the localsFileIndex */ - indexLocalFileEntry(methodEntry.fileEntry); - } - return methodEntry; - } else if (comparisonResult < 0) { - start = middle + 1; - } else { - end = middle; + + MethodEntry methodEntry = methodsIndex.get(debugRangeInfo.symbolNameForMethod()); + if (methodEntry == null) { + methodEntry = processMethod(debugRangeInfo, debugInfoBase, debugContext); + } else { + methodEntry.updateRangeInfo(debugInfoBase, debugRangeInfo); + /* Ensure that the methodEntry's fileEntry is present in the localsFileIndex */ + FileEntry methodFileEntry = methodEntry.fileEntry; + if (methodFileEntry != null) { + indexLocalFileEntry(methodFileEntry); } } - assert start == (end + 1) : start + " != " + end + " + 1"; - assert start <= methods.size() : start + " > " + methods.size(); - MethodEntry newMethodEntry = processMethod(debugRangeInfo, debugInfoBase, debugContext); - methods.add(start, newMethodEntry); - return newMethodEntry; + return methodEntry; } private static boolean listIsSorted(List list) { From 65d546682a9f1a21d66dfc41af738505582c5814 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 6 Sep 2021 16:00:45 +0100 Subject: [PATCH 27/27] Remove redundant methods and fields --- .../objectfile/debugentry/ClassEntry.java | 8 +------ .../objectfile/debugentry/MethodEntry.java | 21 +------------------ .../debuginfo/DebugInfoProvider.java | 6 ------ .../image/NativeImageDebugInfoProvider.java | 15 ------------- 4 files changed, 2 insertions(+), 48 deletions(-) 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 6ff0635aa5ed..643ad61ec936 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 @@ -306,7 +306,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, paramTypeArray, paramNameArray); indexMethodEntry(methodEntry); return methodEntry; @@ -366,12 +366,6 @@ public MethodEntry ensureMethodEntryForDebugRangeInfo(DebugRangeInfo debugRangeI return methodEntry; } - private static boolean listIsSorted(List list) { - List copy = new ArrayList<>(list); - copy.sort(MethodEntry::compareTo); - return list.equals(copy); - } - public List getMethods() { return methods; } 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 4b24173021b1..a5ab87a66717 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 @@ -29,15 +29,13 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; -import jdk.vm.ci.meta.ResolvedJavaMethod; -public class MethodEntry extends MemberEntry implements Comparable { +public class MethodEntry extends MemberEntry { final TypeEntry[] paramTypes; final String[] paramNames; static final int DEOPT = 1 << 0; static final int IN_RANGE = 1 << 1; static final int INLINED = 1 << 2; - private final ResolvedJavaMethod javaMethod; int flags; final String symbolName; @@ -50,7 +48,6 @@ public MethodEntry(DebugInfoBase debugInfoBase, DebugMethodInfo debugMethodInfo, this.paramTypes = paramTypes; this.paramNames = paramNames; this.symbolName = debugMethodInfo.symbolNameForMethod(); - this.javaMethod = debugMethodInfo.getJavaMethod(); this.flags = 0; if (debugMethodInfo.isDeoptTarget()) { setIsDeopt(); @@ -159,20 +156,4 @@ public void updateRangeInfo(DebugInfoBase debugInfoBase, DebugMethodInfo debugMe public String getSymbolName() { return symbolName; } - - public int compareTo(ResolvedJavaMethod other) { - /* - * first try to sort methods by name, to have a nice sorting when printing types with ptype - */ - int comparisonResult = javaMethod.getName().compareTo(other.getName()); - if (comparisonResult != 0) { - return comparisonResult; - } - return this.javaMethod.hashCode() - other.hashCode(); - } - - @Override - public int compareTo(MethodEntry other) { - return compareTo(other.javaMethod); - } } 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 5b2709086cdb..b7af9cc7f695 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,6 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.debug.DebugContext; @@ -201,11 +200,6 @@ interface DebugFieldInfo extends DebugMemberInfo { } interface DebugMethodInfo extends DebugMemberInfo { - /** - * @return the JavaMethod of the method associated with this DebugMethodInfo. - */ - ResolvedJavaMethod getJavaMethod(); - /** * @return an array of Strings identifying the method parameters. */ 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 788780f6a1fb..4997385e88af 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 @@ -648,11 +648,6 @@ public String valueType() { return hostedMethod.getSignature().getReturnType(null).toJavaName(); } - @Override - public ResolvedJavaMethod getJavaMethod() { - return hostedMethod; - } - @Override public List paramTypes() { Signature signature = hostedMethod.getSignature(); @@ -874,11 +869,6 @@ public String symbolNameForMethod() { return NativeImage.localSymbolNameForMethod(hostedMethod); } - @Override - public ResolvedJavaMethod getJavaMethod() { - return hostedMethod; - } - @Override public String valueType() { return hostedMethod.format("%R"); @@ -1098,11 +1088,6 @@ public String valueType() { return method.format("%R"); } - @Override - public ResolvedJavaMethod getJavaMethod() { - return method; - } - @Override public String symbolNameForMethod() { return NativeImage.localSymbolNameForMethod(method);